2 * Copyright (c) 2007 NVIDIA, Corporation
3 * Copyright (c) 2008 Maarten Maathuis
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:
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
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.
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"
36 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update);
39 * PLL calculation. pclk is in kHz.
42 NV50CalcPLL(float pclk, int *pNA, int *pMA, int *pNB, int *pMB, int *pP)
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;
61 int na, ma, nb, mb, p;
62 float bestError = FLT_MAX;
64 *pNA = *pMA = *pNB = *pMB = *pP = 0;
66 if(maxVcoB < pclk + pclk / 200)
67 maxVcoB = pclk + pclk / 200;
68 if(minVcoB / (1 << maxP) > pclk)
69 pclk = minVcoB / (1 << maxP);
71 vcoB = maxVcoB - maxVcoB / 200;
73 vcoB /= 1 << (lowP + 1);
75 while(pclk <= vcoB && lowP < maxP)
81 vcoB = maxVcoB + maxVcoB / 200;
83 vcoB /= 1 << (highP + 1);
85 while(pclk <= vcoB && highP < maxP)
91 for(p = lowP; p <= highP; p++)
93 for(ma = minMA; ma <= maxMA; ma++)
95 if(refclk / ma < minUA)
97 else if(refclk / ma > maxUA)
100 for(na = minNA; na <= maxNA; na++)
102 if(refclk * na / ma < minVcoA || refclk * na / ma > maxVcoA)
105 for(mb = minMB; mb <= maxMB; mb++)
107 if(refclk * na / ma / mb < minUB)
109 else if(refclk * na / ma / mb > maxUB)
112 nb = rint(pclk * (1 << p) * (ma / (float)na) * mb / refclk);
120 float freq = refclk * (na / (float)ma) * (nb / (float)mb) / (1 << p);
121 float error = fabsf(pclk - freq);
122 if(error < bestError) {
137 void NV50CrtcSetPClk(xf86CrtcPtr crtc)
139 ScrnInfoPtr pScrn = crtc->scrn;
140 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcSetPClk is called.\n");
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);
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.
153 NVWrite(pNv, NV50_CRTC0_CLK_CTRL1 + nv_crtc->head * 0x800, NV50_CRTC_CLK_CTRL1_CONNECTED | 0x10000011);
157 NV50CalcPLL(nv_crtc->pclk, &lo_n, &lo_m, &hi_n, &hi_m, &p);
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);
166 for(i = 0; i < xf86_config->num_output; i++) {
167 xf86OutputPtr output = xf86_config->output[i];
169 if(output->crtc != crtc)
171 NV50OutputSetPClk(output, nv_crtc->pclk);
176 NV50CrtcGetHead(xf86CrtcPtr crtc)
178 NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
179 return nv_crtc->head;
183 NV50DispPreInit(ScrnInfoPtr pScrn)
185 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50DispPreInit is called.\n");
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);
214 NV50DispInit(ScrnInfoPtr pScrn)
216 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50DispInit is called.\n");
218 NVPtr pNv = NVPTR(pScrn);
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);
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 */
229 val = NVRead(pNv, 0x00610200);
230 if ((val & 0x9f0000) == 0x20000)
231 NVWrite(pNv, 0x00610200, val | 0x800000);
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));
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);
252 NV50DispShutdown(ScrnInfoPtr pScrn)
254 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50DispShutdown is called.\n");
255 NVPtr pNv = NVPTR(pScrn);
256 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
259 for(i = 0; i < xf86_config->num_crtc; i++) {
260 xf86CrtcPtr crtc = xf86_config->crtc[i];
262 NV50CrtcBlankScreen(crtc, TRUE);
265 NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
267 for(i = 0; i < xf86_config->num_crtc; i++) {
268 xf86CrtcPtr crtc = xf86_config->crtc[i];
269 NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
273 if (nv_crtc->head == 1)
274 mask = NV50_DISPLAY_SUPERVISOR_DISABLE_CRTC1;
276 mask = NV50_DISPLAY_SUPERVISOR_DISABLE_CRTC0;
278 NVWrite(pNv, NV50_DISPLAY_SUPERVISOR, mask);
279 while(!(NVRead(pNv, NV50_DISPLAY_SUPERVISOR) & mask));
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));
291 NV50CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode, DisplayModePtr adjusted_mode, int x, int y)
293 ScrnInfoPtr pScrn = crtc->scrn;
294 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcModeSet is called with position (%d, %d).\n", x, y);
296 NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
298 nv_crtc->pclk = adjusted_mode->Clock;
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;
311 if (adjusted_mode->Flags & V_INTERLACE) {
313 vsync_start_to_end /= 2;
318 if (adjusted_mode->Flags & V_DBLSCAN) {
319 vsync_start_to_end -= 1;
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));
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) {
343 NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_8BPP);
346 NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_15BPP);
349 NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_16BPP);
352 NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_24BPP);
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);
362 NV50CrtcBlankScreen(crtc, FALSE);
366 NV50CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank)
368 ScrnInfoPtr pScrn = crtc->scrn;
369 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcBlankScreen is called (%s).\n", blank ? "blanked" : "unblanked");
371 NVPtr pNv = NVPTR(pScrn);
372 NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
375 NV50CrtcShowHideCursor(crtc, FALSE, FALSE);
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);
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);
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);
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);
413 /******************************** Cursor stuff ********************************/
414 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update)
416 ScrnInfoPtr pScrn = crtc->scrn;
417 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcShowHideCursor is called (%s, %s).\n", show ? "show" : "hide", update ? "update" : "no update");
419 NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
421 NV50CrtcCommand(crtc, NV50_CRTC0_CURSOR,
422 show ? NV50_CRTC0_CURSOR_SHOW : NV50_CRTC0_CURSOR_HIDE);
424 nv_crtc->cursorVisible = show;
425 NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
429 void NV50CrtcShowCursor(xf86CrtcPtr crtc)
431 NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
433 /* Calling NV50_UPDATE_DISPLAY during modeset will lock up everything. */
434 if (nv_crtc->modeset_lock)
437 NV50CrtcShowHideCursor(crtc, TRUE, TRUE);
440 void NV50CrtcHideCursor(xf86CrtcPtr crtc)
442 NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
444 /* Calling NV50_UPDATE_DISPLAY during modeset will lock up everything. */
445 if (nv_crtc->modeset_lock)
448 NV50CrtcShowHideCursor(crtc, FALSE, TRUE);
451 /******************************** CRTC stuff ********************************/
454 NV50CrtcPrepare(xf86CrtcPtr crtc)
456 ScrnInfoPtr pScrn = crtc->scrn;
457 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcPrepare is called.\n");
459 NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
460 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
463 nv_crtc->modeset_lock = TRUE;
465 for(i = 0; i < xf86_config->num_output; i++) {
466 xf86OutputPtr output = xf86_config->output[i];
469 output->funcs->mode_set(output, NULL, NULL);
474 NV50CrtcSetDither(xf86CrtcPtr crtc, Bool update)
476 ScrnInfoPtr pScrn = crtc->scrn;
477 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcSetDither is called (%s).\n", update ? "update" : "no update");
479 xf86OutputPtr output = NULL;
480 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
483 for (i = 0; i < xf86_config->num_output; i++) {
484 if (xf86_config->output[i]->crtc == crtc) {
485 output = xf86_config->output[i];
493 NVOutputPrivatePtr nv_output = output->driver_private;
495 NV50CrtcCommand(crtc, NV50_CRTC0_DITHERING_CTRL, nv_output->dithering ?
496 NV50_CRTC0_DITHERING_CTRL_ON : NV50_CRTC0_DITHERING_CTRL_OFF);
498 NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
501 static void ComputeAspectScale(DisplayModePtr mode, DisplayModePtr adjusted_mode, int *outX, int *outY)
503 float scaleX, scaleY, scale;
505 scaleX = adjusted_mode->HDisplay / (float)mode->HDisplay;
506 scaleY = adjusted_mode->VDisplay / (float)mode->VDisplay;
513 *outX = mode->HDisplay * scale;
514 *outY = mode->VDisplay * scale;
517 void NV50CrtcSetScale(xf86CrtcPtr crtc, DisplayModePtr mode, DisplayModePtr adjusted_mode, enum scaling_modes scale)
519 ScrnInfoPtr pScrn = crtc->scrn;
520 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcSetScale is called.\n");
522 int outX = 0, outY = 0;
526 ComputeAspectScale(mode, adjusted_mode, &outX, &outY);
529 case SCALE_FULLSCREEN:
530 outX = adjusted_mode->HDisplay;
531 outY = adjusted_mode->VDisplay;
535 outX = mode->HDisplay;
536 outY = mode->VDisplay;
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);
545 NV50CrtcCommand(crtc, NV50_CRTC0_SCALE_CTRL, 0);
547 NV50CrtcCommand(crtc, NV50_CRTC0_SCALE_REG1, outY << 16 | outX);
548 NV50CrtcCommand(crtc, NV50_CRTC0_SCALE_REG2, outY << 16 | outX);
552 NV50CrtcCommit(xf86CrtcPtr crtc)
554 ScrnInfoPtr pScrn = crtc->scrn;
555 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcCommit is called.\n");
557 NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
558 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
559 int i, crtc_mask = 0;
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];
566 /* XXXagp: This assumes that xf86_config->crtc[i] is HEADi */
567 crtc_mask |= 1 << NV50CrtcGetHead(output->crtc);
571 for(i = 0; i < xf86_config->num_crtc; i++) {
572 if(!((1 << i) & crtc_mask)) {
573 NV50CrtcBlankScreen(xf86_config->crtc[i], TRUE);
577 xf86_reload_cursors (pScrn->pScreen);
579 NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
581 nv_crtc->modeset_lock = FALSE;