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 typedef struct NV50CrtcPrivRec {
44 int pclk; /* Target pixel clock in kHz */
46 } NV50CrtcPrivRec, *NV50CrtcPrivPtr;
48 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update);
51 * PLL calculation. pclk is in kHz.
54 NV50CalcPLL(float pclk, int *pNA, int *pMA, int *pNB, int *pMB, int *pP)
56 const float refclk = 27000.0f;
57 const float minVcoA = 100000;
58 const float maxVcoA = 400000;
59 const float minVcoB = 600000;
60 float maxVcoB = 1400000;
61 const float minUA = 2000;
62 const float maxUA = 400000;
63 const float minUB = 50000;
64 const float maxUB = 200000;
65 const int minNA = 1, maxNA = 255;
66 const int minNB = 1, maxNB = 31;
67 const int minMA = 1, maxMA = 255;
68 const int minMB = 1, maxMB = 31;
69 const int minP = 0, maxP = 6;
73 int na, ma, nb, mb, p;
74 float bestError = FLT_MAX;
76 *pNA = *pMA = *pNB = *pMB = *pP = 0;
78 if(maxVcoB < pclk + pclk / 200)
79 maxVcoB = pclk + pclk / 200;
80 if(minVcoB / (1 << maxP) > pclk)
81 pclk = minVcoB / (1 << maxP);
83 vcoB = maxVcoB - maxVcoB / 200;
85 vcoB /= 1 << (lowP + 1);
87 while(pclk <= vcoB && lowP < maxP)
93 vcoB = maxVcoB + maxVcoB / 200;
95 vcoB /= 1 << (highP + 1);
97 while(pclk <= vcoB && highP < maxP)
103 for(p = lowP; p <= highP; p++)
105 for(ma = minMA; ma <= maxMA; ma++)
107 if(refclk / ma < minUA)
109 else if(refclk / ma > maxUA)
112 for(na = minNA; na <= maxNA; na++)
114 if(refclk * na / ma < minVcoA || refclk * na / ma > maxVcoA)
117 for(mb = minMB; mb <= maxMB; mb++)
119 if(refclk * na / ma / mb < minUB)
121 else if(refclk * na / ma / mb > maxUB)
124 nb = rint(pclk * (1 << p) * (ma / (float)na) * mb / refclk);
132 float freq = refclk * (na / (float)ma) * (nb / (float)mb) / (1 << p);
133 float error = fabsf(pclk - freq);
134 if(error < bestError) {
150 NV50CrtcSetPClk(xf86CrtcPtr crtc)
152 NVPtr pNv = NVPTR(crtc->scrn);
153 NV50CrtcPrivPtr pPriv = crtc->driver_private;
154 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
155 const int headOff = 0x800 * pPriv->head;
156 int lo_n, lo_m, hi_n, hi_m, p, i;
157 CARD32 lo = pNv->REGS[(0x00614104+headOff)/4];
158 CARD32 hi = pNv->REGS[(0x00614108+headOff)/4];
160 pNv->REGS[(0x00614100+headOff)/4] = 0x10000610;
164 NV50CalcPLL(pPriv->pclk, &lo_n, &lo_m, &hi_n, &hi_m, &p);
166 lo |= (lo_m << 16) | lo_n;
167 hi |= (p << 28) | (hi_m << 16) | hi_n;
168 pNv->REGS[(0x00614104+headOff)/4] = lo;
169 pNv->REGS[(0x00614108+headOff)/4] = hi;
170 pNv->REGS[(0x00614200+headOff)/4] = 0;
172 for(i = 0; i < xf86_config->num_output; i++) {
173 xf86OutputPtr output = xf86_config->output[i];
175 if(output->crtc != crtc)
177 NV50OutputSetPClk(output, pPriv->pclk);
182 NV50DispCommand(ScrnInfoPtr pScrn, CARD32 addr, CARD32 data)
184 NVPtr pNv = NVPTR(pScrn);
186 pNv->REGS[0x00610304/4] = data;
187 pNv->REGS[0x00610300/4] = addr | 0x80010001;
189 while(pNv->REGS[0x00610300/4] & 0x80000000) {
190 const int super = ffs((pNv->REGS[0x00610024/4] >> 4) & 7);
194 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
195 const CARD32 r = pNv->REGS[0x00610030/4];
198 for(i = 0; i < xf86_config->num_crtc; i++)
200 xf86CrtcPtr crtc = xf86_config->crtc[i];
201 NV50CrtcPrivPtr pPriv = crtc->driver_private;
203 if(r & (0x200 << pPriv->head))
204 NV50CrtcSetPClk(crtc);
208 pNv->REGS[0x00610024/4] = 8 << super;
209 pNv->REGS[0x00610030/4] = 0x80000000;
215 NV50CrtcGetHead(xf86CrtcPtr crtc)
217 NV50CrtcPrivPtr pPriv = crtc->driver_private;
222 NV50DispPreInit(ScrnInfoPtr pScrn)
224 NVPtr pNv = NVPTR(pScrn);
226 pNv->REGS[0x00610184/4] = pNv->REGS[0x00614004/4];
227 pNv->REGS[0x00610190/4] = pNv->REGS[0x00616100/4];
228 pNv->REGS[0x006101a0/4] = pNv->REGS[0x00616900/4];
229 pNv->REGS[0x00610194/4] = pNv->REGS[0x00616104/4];
230 pNv->REGS[0x006101a4/4] = pNv->REGS[0x00616904/4];
231 pNv->REGS[0x00610198/4] = pNv->REGS[0x00616108/4];
232 pNv->REGS[0x006101a8/4] = pNv->REGS[0x00616908/4];
233 pNv->REGS[0x0061019C/4] = pNv->REGS[0x0061610C/4];
234 pNv->REGS[0x006101ac/4] = pNv->REGS[0x0061690c/4];
235 pNv->REGS[0x006101D0/4] = pNv->REGS[0x0061A000/4];
236 pNv->REGS[0x006101D4/4] = pNv->REGS[0x0061A800/4];
237 pNv->REGS[0x006101D8/4] = pNv->REGS[0x0061B000/4];
238 pNv->REGS[0x006101E0/4] = pNv->REGS[0x0061C000/4];
239 pNv->REGS[0x006101E4/4] = pNv->REGS[0x0061C800/4];
240 pNv->REGS[0x0061c00c/4] = 0x03010700;
241 pNv->REGS[0x0061c010/4] = 0x0000152f;
242 pNv->REGS[0x0061c014/4] = 0x00000000;
243 pNv->REGS[0x0061c018/4] = 0x00245af8;
244 pNv->REGS[0x0061c80c/4] = 0x03010700;
245 pNv->REGS[0x0061c810/4] = 0x0000152f;
246 pNv->REGS[0x0061c814/4] = 0x00000000;
247 pNv->REGS[0x0061c818/4] = 0x00245af8;
248 pNv->REGS[0x0061A004/4] = 0x80550000;
249 pNv->REGS[0x0061A010/4] = 0x00000001;
250 pNv->REGS[0x0061A804/4] = 0x80550000;
251 pNv->REGS[0x0061A810/4] = 0x00000001;
252 pNv->REGS[0x0061B004/4] = 0x80550000;
253 pNv->REGS[0x0061B010/4] = 0x00000001;
259 NV50DispInit(ScrnInfoPtr pScrn)
261 NVPtr pNv = NVPTR(pScrn);
263 if(pNv->REGS[0x00610024/4] & 0x100) {
264 pNv->REGS[0x00610024/4] = 0x100;
265 pNv->REGS[0x006194E8/4] &= ~1;
266 while(pNv->REGS[0x006194E8/4] & 2);
269 pNv->REGS[0x00610200/4] = 0x2b00;
270 while((pNv->REGS[0x00610200/4] & 0x1e0000) != 0);
271 pNv->REGS[0x00610300/4] = 1;
272 pNv->REGS[0x00610200/4] = 0x1000b03;
273 while(!(pNv->REGS[0x00610200/4] & 0x40000000));
286 NV50DispShutdown(ScrnInfoPtr pScrn)
288 NVPtr pNv = NVPTR(pScrn);
289 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
292 for(i = 0; i < xf86_config->num_crtc; i++) {
293 xf86CrtcPtr crtc = xf86_config->crtc[i];
295 NV50CrtcBlankScreen(crtc, TRUE);
300 for(i = 0; i < xf86_config->num_crtc; i++) {
301 xf86CrtcPtr crtc = xf86_config->crtc[i];
304 const CARD32 mask = 4 << NV50CrtcGetHead(crtc);
306 pNv->REGS[0x00610024/4] = mask;
307 while(!(pNv->REGS[0x00610024/4] & mask));
311 pNv->REGS[0x00610200/4] = 0;
312 pNv->REGS[0x00610300/4] = 0;
313 while((pNv->REGS[0x00610200/4] & 0x1e0000) != 0);
317 NV50CrtcModeFixup(xf86CrtcPtr crtc,
318 DisplayModePtr mode, DisplayModePtr adjusted_mode)
320 // TODO: Fix up the mode here
325 NV50CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode,
326 DisplayModePtr adjusted_mode, int x, int y)
328 ScrnInfoPtr pScrn = crtc->scrn;
329 NV50CrtcPrivPtr pPriv = crtc->driver_private;
330 const int HDisplay = mode->HDisplay, VDisplay = mode->VDisplay;
331 const int headOff = 0x400 * NV50CrtcGetHead(crtc);
332 int interlaceDiv, fudge;
334 // TODO: Use adjusted_mode and fix it up in NV50CrtcModeFixup
335 pPriv->pclk = mode->Clock;
337 /* Magic mode timing fudge factor */
338 fudge = ((mode->Flags & V_INTERLACE) && (mode->Flags & V_DBLSCAN)) ? 2 : 1;
339 interlaceDiv = (mode->Flags & V_INTERLACE) ? 2 : 1;
341 C(0x00000804 + headOff, mode->Clock | 0x800000);
342 C(0x00000808 + headOff, (mode->Flags & V_INTERLACE) ? 2 : 0);
343 C(0x00000810 + headOff, 0);
344 C(0x0000082C + headOff, 0);
345 C(0x00000814 + headOff, mode->CrtcVTotal << 16 | mode->CrtcHTotal);
346 C(0x00000818 + headOff,
347 ((mode->CrtcVSyncEnd - mode->CrtcVSyncStart) / interlaceDiv - 1) << 16 |
348 (mode->CrtcHSyncEnd - mode->CrtcHSyncStart - 1));
349 C(0x0000081C + headOff,
350 ((mode->CrtcVBlankEnd - mode->CrtcVSyncStart) / interlaceDiv - fudge) << 16 |
351 (mode->CrtcHBlankEnd - mode->CrtcHSyncStart - 1));
352 C(0x00000820 + headOff,
353 ((mode->CrtcVTotal - mode->CrtcVSyncStart + mode->CrtcVBlankStart) / interlaceDiv - fudge) << 16 |
354 (mode->CrtcHTotal - mode->CrtcHSyncStart + mode->CrtcHBlankStart - 1));
355 if(mode->Flags & V_INTERLACE) {
356 C(0x00000824 + headOff,
357 ((mode->CrtcVTotal + mode->CrtcVBlankEnd - mode->CrtcVSyncStart) / 2 - 2) << 16 |
358 ((2*mode->CrtcVTotal - mode->CrtcVSyncStart + mode->CrtcVBlankStart) / 2 - 2));
360 C(0x00000868 + headOff, pScrn->virtualY << 16 | pScrn->virtualX);
361 C(0x0000086C + headOff, pScrn->displayWidth * (pScrn->bitsPerPixel / 8) | 0x100000);
362 switch(pScrn->depth) {
363 case 8: C(0x00000870 + headOff, 0x1E00); break;
364 case 15: C(0x00000870 + headOff, 0xE900); break;
365 case 16: C(0x00000870 + headOff, 0xE800); break;
366 case 24: C(0x00000870 + headOff, 0xCF00); break;
368 C(0x000008A0 + headOff, 0);
369 if((mode->Flags & V_DBLSCAN) || (mode->Flags & V_INTERLACE) ||
370 mode->CrtcHDisplay != HDisplay || mode->CrtcVDisplay != VDisplay) {
371 C(0x000008A4 + headOff, 9);
373 C(0x000008A4 + headOff, 0);
375 C(0x000008A8 + headOff, 0x40000);
376 C(0x000008C0 + headOff, y << 16 | x);
377 C(0x000008C8 + headOff, VDisplay << 16 | HDisplay);
378 C(0x000008D4 + headOff, 0);
379 C(0x000008D8 + headOff, mode->CrtcVDisplay << 16 | mode->CrtcHDisplay);
380 C(0x000008DC + headOff, mode->CrtcVDisplay << 16 | mode->CrtcHDisplay);
382 NV50CrtcBlankScreen(crtc, FALSE);
386 NV50CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank)
388 ScrnInfoPtr pScrn = crtc->scrn;
389 NVPtr pNv = NVPTR(pScrn);
390 NV50CrtcPrivPtr pPriv = crtc->driver_private;
391 const int headOff = 0x400 * pPriv->head;
394 NV50CrtcShowHideCursor(crtc, FALSE, FALSE);
396 C(0x00000840 + headOff, 0);
397 C(0x00000844 + headOff, 0);
398 if(pNv->NVArch != 0x50)
399 C(0x0000085C + headOff, 0);
400 C(0x00000874 + headOff, 0);
401 if(pNv->NVArch != 0x50)
402 C(0x0000089C + headOff, 0);
404 C(0x00000860 + headOff, pNv->FB->offset >> 8);
405 C(0x00000864 + headOff, 0);
406 pNv->REGS[0x00610380/4] = 0;
407 /*XXX: in "nv" this is total vram size. our RamAmountKBytes is clamped
410 pNv->REGS[0x00610384/4] = pNv->RamAmountKBytes * 1024 - 1;
411 pNv->REGS[0x00610388/4] = 0x150000;
412 pNv->REGS[0x0061038C/4] = 0;
413 C(0x00000884 + headOff, pNv->Cursor->offset >> 8);
414 if(pNv->NVArch != 0x50)
415 C(0x0000089C + headOff, 1);
416 if(pPriv->cursorVisible)
417 NV50CrtcShowHideCursor(crtc, TRUE, FALSE);
418 C(0x00000840 + headOff, pScrn->depth == 8 ? 0x80000000 : 0xc0000000);
419 C(0x00000844 + headOff, pNv->CLUT->offset >> 8);
420 if(pNv->NVArch != 0x50)
421 C(0x0000085C + headOff, 1);
422 C(0x00000874 + headOff, 1);
427 NV50CrtcDPMSSet(xf86CrtcPtr crtc, int mode)
431 /******************************** Cursor stuff ********************************/
432 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update)
434 ScrnInfoPtr pScrn = crtc->scrn;
435 NV50CrtcPrivPtr pPriv = crtc->driver_private;
436 const int headOff = 0x400 * NV50CrtcGetHead(crtc);
438 C(0x00000880 + headOff, show ? 0x85000000 : 0x5000000);
440 pPriv->cursorVisible = show;
445 void NV50CrtcShowCursor(xf86CrtcPtr crtc)
447 NV50CrtcShowHideCursor(crtc, TRUE, TRUE);
450 void NV50CrtcHideCursor(xf86CrtcPtr crtc)
452 NV50CrtcShowHideCursor(crtc, FALSE, TRUE);
455 /******************************** CRTC stuff ********************************/
458 NV50CrtcLock(xf86CrtcPtr crtc)
464 NV50CrtcPrepare(xf86CrtcPtr crtc)
466 ScrnInfoPtr pScrn = crtc->scrn;
467 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
470 for(i = 0; i < xf86_config->num_output; i++) {
471 xf86OutputPtr output = xf86_config->output[i];
474 output->funcs->mode_set(output, NULL, NULL);
479 NV50CrtcCommit(xf86CrtcPtr crtc)
481 ScrnInfoPtr pScrn = crtc->scrn;
482 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
483 int i, crtc_mask = 0;
485 /* If any heads are unused, blank them */
486 for(i = 0; i < xf86_config->num_output; i++) {
487 xf86OutputPtr output = xf86_config->output[i];
490 /* XXXagp: This assumes that xf86_config->crtc[i] is HEADi */
491 crtc_mask |= 1 << NV50CrtcGetHead(output->crtc);
494 for(i = 0; i < xf86_config->num_crtc; i++)
495 if(!((1 << i) & crtc_mask))
496 NV50CrtcBlankScreen(xf86_config->crtc[i], TRUE);
501 static const xf86CrtcFuncsRec nv50_crtc_funcs = {
502 .dpms = NV50CrtcDPMSSet,
505 .lock = NV50CrtcLock,
507 .mode_fixup = NV50CrtcModeFixup,
508 .prepare = NV50CrtcPrepare,
509 .mode_set = NV50CrtcModeSet,
510 // .gamma_set = NV50DispGammaSet,
511 .commit = NV50CrtcCommit,
512 .shadow_create = NULL,
513 .shadow_destroy = NULL,
514 .set_cursor_position = NV50SetCursorPosition,
515 .show_cursor = NV50CrtcShowCursor,
516 .hide_cursor = NV50CrtcHideCursor,
517 .load_cursor_argb = NV50LoadCursorARGB,
522 NV50DispCreateCrtcs(ScrnInfoPtr pScrn)
526 NV50CrtcPrivPtr nv50_crtc;
528 /* Create a "crtc" object for each head */
529 for(head = HEAD0; head <= HEAD1; head++) {
530 crtc = xf86CrtcCreate(pScrn, &nv50_crtc_funcs);
533 nv50_crtc = xnfcalloc(sizeof(*nv50_crtc), 1);
534 nv50_crtc->head = head;
535 crtc->driver_private = nv50_crtc;
539 #endif /* ENABLE_RANDR12 */