2 * Copyright (c) 2007 NVIDIA, Corporation
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:
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
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.
36 #include "nv_include.h"
37 #include "nv50_type.h"
38 #include "nv50_cursor.h"
39 #include "nv50_display.h"
40 #include "nv50_output.h"
42 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update);
45 * PLL calculation. pclk is in kHz.
48 NV50CalcPLL(float pclk, int *pNA, int *pMA, int *pNB, int *pMB, int *pP)
50 const float refclk = 27000.0f;
51 const float minVcoA = 100000;
52 const float maxVcoA = 400000;
53 const float minVcoB = 600000;
54 float maxVcoB = 1400000;
55 const float minUA = 2000;
56 const float maxUA = 400000;
57 const float minUB = 50000;
58 const float maxUB = 200000;
59 const int minNA = 1, maxNA = 255;
60 const int minNB = 1, maxNB = 31;
61 const int minMA = 1, maxMA = 255;
62 const int minMB = 1, maxMB = 31;
63 const int minP = 0, maxP = 6;
67 int na, ma, nb, mb, p;
68 float bestError = FLT_MAX;
70 *pNA = *pMA = *pNB = *pMB = *pP = 0;
72 if(maxVcoB < pclk + pclk / 200)
73 maxVcoB = pclk + pclk / 200;
74 if(minVcoB / (1 << maxP) > pclk)
75 pclk = minVcoB / (1 << maxP);
77 vcoB = maxVcoB - maxVcoB / 200;
79 vcoB /= 1 << (lowP + 1);
81 while(pclk <= vcoB && lowP < maxP)
87 vcoB = maxVcoB + maxVcoB / 200;
89 vcoB /= 1 << (highP + 1);
91 while(pclk <= vcoB && highP < maxP)
97 for(p = lowP; p <= highP; p++)
99 for(ma = minMA; ma <= maxMA; ma++)
101 if(refclk / ma < minUA)
103 else if(refclk / ma > maxUA)
106 for(na = minNA; na <= maxNA; na++)
108 if(refclk * na / ma < minVcoA || refclk * na / ma > maxVcoA)
111 for(mb = minMB; mb <= maxMB; mb++)
113 if(refclk * na / ma / mb < minUB)
115 else if(refclk * na / ma / mb > maxUB)
118 nb = rint(pclk * (1 << p) * (ma / (float)na) * mb / refclk);
126 float freq = refclk * (na / (float)ma) * (nb / (float)mb) / (1 << p);
127 float error = fabsf(pclk - freq);
128 if(error < bestError) {
143 void NV50CrtcSetPClk(xf86CrtcPtr crtc)
145 NVPtr pNv = NVPTR(crtc->scrn);
146 NV50CrtcPrivPtr pPriv = crtc->driver_private;
147 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
148 const int headOff = 0x800 * pPriv->head;
149 int lo_n, lo_m, hi_n, hi_m, p, i;
150 CARD32 lo = pNv->REGS[(0x00614104+headOff)/4];
151 CARD32 hi = pNv->REGS[(0x00614108+headOff)/4];
153 pNv->REGS[(0x00614100+headOff)/4] = 0x10000610;
157 NV50CalcPLL(pPriv->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 pNv->REGS[(0x00614104+headOff)/4] = lo;
162 pNv->REGS[(0x00614108+headOff)/4] = hi;
163 pNv->REGS[(0x00614200+headOff)/4] = 0;
165 for(i = 0; i < xf86_config->num_output; i++) {
166 xf86OutputPtr output = xf86_config->output[i];
168 if(output->crtc != crtc)
170 NV50OutputSetPClk(output, pPriv->pclk);
175 NV50CrtcGetHead(xf86CrtcPtr crtc)
177 NV50CrtcPrivPtr pPriv = crtc->driver_private;
182 NV50DispPreInit(ScrnInfoPtr pScrn)
184 NVPtr pNv = NVPTR(pScrn);
186 pNv->REGS[0x00610184/4] = pNv->REGS[0x00614004/4];
187 pNv->REGS[0x00610190/4] = pNv->REGS[0x00616100/4];
188 pNv->REGS[0x006101a0/4] = pNv->REGS[0x00616900/4];
189 pNv->REGS[0x00610194/4] = pNv->REGS[0x00616104/4];
190 pNv->REGS[0x006101a4/4] = pNv->REGS[0x00616904/4];
191 pNv->REGS[0x00610198/4] = pNv->REGS[0x00616108/4];
192 pNv->REGS[0x006101a8/4] = pNv->REGS[0x00616908/4];
193 pNv->REGS[0x0061019C/4] = pNv->REGS[0x0061610C/4];
194 pNv->REGS[0x006101ac/4] = pNv->REGS[0x0061690c/4];
195 pNv->REGS[0x006101D0/4] = pNv->REGS[0x0061A000/4];
196 pNv->REGS[0x006101D4/4] = pNv->REGS[0x0061A800/4];
197 pNv->REGS[0x006101D8/4] = pNv->REGS[0x0061B000/4];
198 pNv->REGS[0x006101E0/4] = pNv->REGS[0x0061C000/4];
199 pNv->REGS[0x006101E4/4] = pNv->REGS[0x0061C800/4];
200 pNv->REGS[0x0061A004/4] = 0x80550000;
201 pNv->REGS[0x0061A010/4] = 0x00000001;
202 pNv->REGS[0x0061A804/4] = 0x80550000;
203 pNv->REGS[0x0061A810/4] = 0x00000001;
204 pNv->REGS[0x0061B004/4] = 0x80550000;
205 pNv->REGS[0x0061B010/4] = 0x00000001;
211 NV50DispInit(ScrnInfoPtr pScrn)
213 NVPtr pNv = NVPTR(pScrn);
215 if(pNv->REGS[0x00610024/4] & 0x100) {
216 pNv->REGS[0x00610024/4] = 0x100;
217 pNv->REGS[0x006194E8/4] &= ~1;
218 while(pNv->REGS[0x006194E8/4] & 2);
221 pNv->REGS[0x00610200/4] = 0x2b00;
222 while((pNv->REGS[0x00610200/4] & 0x1e0000) != 0);
223 pNv->REGS[0x00610300/4] = 1;
224 pNv->REGS[0x00610200/4] = 0x1000b03;
225 while(!(pNv->REGS[0x00610200/4] & 0x40000000));
227 NV50DisplayCommand(pScrn, 0x84, 0);
228 NV50DisplayCommand(pScrn, 0x88, 0);
229 NV50DisplayCommand(pScrn, 0x874, 0);
230 NV50DisplayCommand(pScrn, 0x800, 0);
231 NV50DisplayCommand(pScrn, 0x810, 0);
232 NV50DisplayCommand(pScrn, 0x82c, 0);
238 NV50DispShutdown(ScrnInfoPtr pScrn)
240 NVPtr pNv = NVPTR(pScrn);
241 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
244 for(i = 0; i < xf86_config->num_crtc; i++) {
245 xf86CrtcPtr crtc = xf86_config->crtc[i];
247 NV50CrtcBlankScreen(crtc, TRUE);
250 NV50DisplayCommand(pScrn, 0x80, 0);
252 for(i = 0; i < xf86_config->num_crtc; i++) {
253 xf86CrtcPtr crtc = xf86_config->crtc[i];
256 const CARD32 mask = 4 << NV50CrtcGetHead(crtc);
258 pNv->REGS[0x00610024/4] = mask;
259 while(!(pNv->REGS[0x00610024/4] & mask));
263 pNv->REGS[0x00610200/4] = 0;
264 pNv->REGS[0x00610300/4] = 0;
265 while ((pNv->REGS[0x00610200/4] & 0x1e0000) != 0);
266 while ((pNv->REGS[0x0061C030/4] & 0x10000000));
267 while ((pNv->REGS[0x0061C830/4] & 0x10000000));
271 NV50CrtcDoModeFixup(DisplayModePtr dst, const DisplayModePtr src)
273 /* Magic mode timing fudge factor */
274 const int fudge = ((src->Flags & V_INTERLACE) && (src->Flags & V_DBLSCAN)) ? 2 : 1;
275 const int interlaceDiv = (src->Flags & V_INTERLACE) ? 2 : 1;
277 /* Stash the src timings in the Crtc fields in dst */
278 dst->CrtcHBlankStart = src->CrtcVTotal << 16 | src->CrtcHTotal;
279 dst->CrtcHSyncEnd = ((src->CrtcVSyncEnd - src->CrtcVSyncStart) / interlaceDiv - 1) << 16 |
280 (src->CrtcHSyncEnd - src->CrtcHSyncStart - 1);
281 dst->CrtcHBlankEnd = ((src->CrtcVBlankEnd - src->CrtcVSyncStart) / interlaceDiv - fudge) << 16 |
282 (src->CrtcHBlankEnd - src->CrtcHSyncStart - 1);
283 dst->CrtcHTotal = ((src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / interlaceDiv - fudge) << 16 |
284 (src->CrtcHTotal - src->CrtcHSyncStart + src->CrtcHBlankStart - 1);
285 dst->CrtcHSkew = ((src->CrtcVTotal + src->CrtcVBlankEnd - src->CrtcVSyncStart) / 2 - 2) << 16 |
286 ((2*src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / 2 - 2);
290 NV50CrtcModeFixup(xf86CrtcPtr crtc,
291 DisplayModePtr mode, DisplayModePtr adjusted_mode)
293 NV50CrtcPrivPtr pPriv = crtc->driver_private;
295 if (pPriv->skipModeFixup)
298 NV50CrtcDoModeFixup(adjusted_mode, mode);
303 NV50CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode,
304 DisplayModePtr adjusted_mode, int x, int y)
306 ScrnInfoPtr pScrn = crtc->scrn;
307 NV50CrtcPrivPtr pPriv = crtc->driver_private;
308 const int HDisplay = adjusted_mode->HDisplay;
309 const int VDisplay = adjusted_mode->VDisplay;
311 pPriv->pclk = adjusted_mode->Clock;
313 /* DoCrtc includes head offset */
314 NV50CrtcCommand(crtc, 0x804, adjusted_mode->Clock | 0x800000);
315 NV50CrtcCommand(crtc, 0x808, (adjusted_mode->Flags & V_INTERLACE) ? 2 : 0);
316 NV50CrtcCommand(crtc, 0x810, 0);
317 NV50CrtcCommand(crtc, 0x82c, 0);
318 NV50CrtcCommand(crtc, 0x814, adjusted_mode->CrtcHBlankStart);
319 NV50CrtcCommand(crtc, 0x818, adjusted_mode->CrtcHSyncEnd);
320 NV50CrtcCommand(crtc, 0x81c, adjusted_mode->CrtcHBlankEnd);
321 NV50CrtcCommand(crtc, 0x820, adjusted_mode->CrtcHTotal);
322 if(adjusted_mode->Flags & V_INTERLACE) {
323 NV50CrtcCommand(crtc, 0x824, adjusted_mode->CrtcHSkew);
325 NV50CrtcCommand(crtc, 0x868, pScrn->virtualY << 16 | pScrn->virtualX);
326 NV50CrtcCommand(crtc, 0x86c, pScrn->displayWidth * (pScrn->bitsPerPixel / 8) | 0x100000);
327 switch(pScrn->depth) {
328 case 8: NV50CrtcCommand(crtc, 0x870, 0x1e00); break;
329 case 15: NV50CrtcCommand(crtc, 0x870, 0xe900); break;
330 case 16: NV50CrtcCommand(crtc, 0x870, 0xe800); break;
331 case 24: NV50CrtcCommand(crtc, 0x870, 0xcf00); break;
333 NV50CrtcSetDither(crtc, pPriv->dither, FALSE);
334 NV50CrtcCommand(crtc, 0x8a8, 0x40000);
335 NV50CrtcCommand(crtc, 0x8c0, y << 16 | x);
336 NV50CrtcCommand(crtc, 0x8c8, VDisplay << 16 | HDisplay);
337 NV50CrtcCommand(crtc, 0x8d4, 0);
339 NV50CrtcBlankScreen(crtc, FALSE);
343 NV50CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank)
345 ScrnInfoPtr pScrn = crtc->scrn;
346 NVPtr pNv = NVPTR(pScrn);
347 NV50CrtcPrivPtr pPriv = crtc->driver_private;
350 NV50CrtcShowHideCursor(crtc, FALSE, FALSE);
352 NV50CrtcCommand(crtc, 0x840, 0);
353 NV50CrtcCommand(crtc, 0x844, 0);
354 if(pNv->NVArch != 0x50)
355 NV50CrtcCommand(crtc, 0x85c, 0);
356 NV50CrtcCommand(crtc, 0x874, 0);
357 if(pNv->NVArch != 0x50)
358 NV50CrtcCommand(crtc, 0x89c, 0);
360 NV50CrtcCommand(crtc, 0x860, pNv->FB->offset >> 8);
361 NV50CrtcCommand(crtc, 0x864, 0);
362 pNv->REGS[0x00610380/4] = 0;
363 /*XXX: in "nv" this is total vram size. our RamAmountKBytes is clamped
366 pNv->REGS[0x00610384/4] = pNv->RamAmountKBytes * 1024 - 1;
367 pNv->REGS[0x00610388/4] = 0x150000;
368 pNv->REGS[0x0061038C/4] = 0;
369 NV50CrtcCommand(crtc, 0x884, pNv->Cursor->offset >> 8);
370 if(pNv->NVArch != 0x50)
371 NV50CrtcCommand(crtc, 0x89c, 1);
372 if(pPriv->cursorVisible)
373 NV50CrtcShowHideCursor(crtc, TRUE, FALSE);
374 NV50CrtcCommand(crtc, 0x840, pScrn->depth == 8 ? 0x80000000 : 0xc0000000);
375 NV50CrtcCommand(crtc, 0x844, pNv->CLUT->offset >> 8);
376 if(pNv->NVArch != 0x50)
377 NV50CrtcCommand(crtc, 0x85c, 1);
378 NV50CrtcCommand(crtc, 0x874, 1);
382 /******************************** Cursor stuff ********************************/
383 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update)
385 ScrnInfoPtr pScrn = crtc->scrn;
386 NV50CrtcPrivPtr pPriv = crtc->driver_private;
388 NV50CrtcCommand(crtc, 0x880, show ? 0x85000000 : 0x5000000);
390 pPriv->cursorVisible = show;
391 NV50CrtcCommand(crtc, 0x80, 0);
395 void NV50CrtcShowCursor(xf86CrtcPtr crtc)
397 NV50CrtcShowHideCursor(crtc, TRUE, TRUE);
400 void NV50CrtcHideCursor(xf86CrtcPtr crtc)
402 NV50CrtcShowHideCursor(crtc, FALSE, TRUE);
405 /******************************** CRTC stuff ********************************/
408 NV50CrtcPrepare(xf86CrtcPtr crtc)
410 ScrnInfoPtr pScrn = crtc->scrn;
411 NV50CrtcPrivPtr pPriv = crtc->driver_private;
412 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
415 for(i = 0; i < xf86_config->num_output; i++) {
416 xf86OutputPtr output = xf86_config->output[i];
419 output->funcs->mode_set(output, NULL, NULL);
422 pPriv->skipModeFixup = FALSE;
426 NV50CrtcSkipModeFixup(xf86CrtcPtr crtc)
428 NV50CrtcPrivPtr pPriv = crtc->driver_private;
429 pPriv->skipModeFixup = TRUE;
433 NV50CrtcSetDither(xf86CrtcPtr crtc, Bool dither, Bool update)
435 ScrnInfoPtr pScrn = crtc->scrn;
436 NV50CrtcPrivPtr pPriv = crtc->driver_private;
438 pPriv->dither = dither;
440 NV50CrtcCommand(crtc, 0x8a0, dither ? 0x11 : 0);
442 NV50CrtcCommand(crtc, 0x80, 0);
445 static void ComputeAspectScale(DisplayModePtr mode, int *outX, int *outY)
447 float scaleX, scaleY, scale;
449 scaleX = mode->CrtcHDisplay / (float)mode->HDisplay;
450 scaleY = mode->CrtcVDisplay / (float)mode->VDisplay;
457 *outX = mode->HDisplay * scale;
458 *outY = mode->VDisplay * scale;
461 void NV50CrtcSetScale(xf86CrtcPtr crtc, DisplayModePtr mode,
462 enum NV50ScaleMode scale)
464 ScrnInfoPtr pScrn = crtc->scrn;
465 NV50CrtcPrivPtr pPriv = crtc->driver_private;
469 case NV50_SCALE_ASPECT:
470 ComputeAspectScale(mode, &outX, &outY);
474 case NV50_SCALE_FILL:
475 outX = mode->CrtcHDisplay;
476 outY = mode->CrtcVDisplay;
479 case NV50_SCALE_CENTER:
480 outX = mode->HDisplay;
481 outY = mode->VDisplay;
485 if((mode->Flags & V_DBLSCAN) || (mode->Flags & V_INTERLACE) ||
486 mode->HDisplay != outX || mode->VDisplay != outY) {
487 NV50CrtcCommand(crtc, 0x8a4, 9);
489 NV50CrtcCommand(crtc, 0x8a4, 0);
491 NV50CrtcCommand(crtc, 0x8d8, outY << 16 | outX);
492 NV50CrtcCommand(crtc, 0x8dc, outY << 16 | outX);
496 NV50CrtcCommit(xf86CrtcPtr crtc)
498 ScrnInfoPtr pScrn = crtc->scrn;
499 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
500 int i, crtc_mask = 0;
502 /* If any heads are unused, blank them */
503 for(i = 0; i < xf86_config->num_output; i++) {
504 xf86OutputPtr output = xf86_config->output[i];
507 /* XXXagp: This assumes that xf86_config->crtc[i] is HEADi */
508 crtc_mask |= 1 << NV50CrtcGetHead(output->crtc);
511 for(i = 0; i < xf86_config->num_crtc; i++)
512 if(!((1 << i) & crtc_mask))
513 NV50CrtcBlankScreen(xf86_config->crtc[i], TRUE);
515 NV50CrtcCommand(crtc, 0x80, 0);
518 #endif /* ENABLE_RANDR12 */