1fc5a983dSmrg/* 271ba42d0Smrg * Copyright (c) 2007,2010 NVIDIA Corporation 3fc5a983dSmrg * 4fc5a983dSmrg * Permission is hereby granted, free of charge, to any person obtaining a 5fc5a983dSmrg * copy of this software and associated documentation files (the 6fc5a983dSmrg * "Software"), to deal in the Software without restriction, including 7fc5a983dSmrg * without limitation the rights to use, copy, modify, merge, publish, 8fc5a983dSmrg * distribute, sublicense, and/or sell copies of the Software, and to 9fc5a983dSmrg * permit persons to whom the Software is furnished to do so, subject to 10fc5a983dSmrg * the following conditions: 11fc5a983dSmrg * 12fc5a983dSmrg * The above copyright notice and this permission notice shall be included 13fc5a983dSmrg * in all copies or substantial portions of the Software. 14fc5a983dSmrg * 15fc5a983dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16fc5a983dSmrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17fc5a983dSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18fc5a983dSmrg * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19fc5a983dSmrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20fc5a983dSmrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21fc5a983dSmrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22fc5a983dSmrg */ 23fc5a983dSmrg 24fc5a983dSmrg 25fc5a983dSmrg#ifdef HAVE_CONFIG_H 26fc5a983dSmrg#include "config.h" 27fc5a983dSmrg#endif 28fc5a983dSmrg 29fc5a983dSmrg#include <float.h> 30fc5a983dSmrg#include <math.h> 31fc5a983dSmrg#include <strings.h> 32fc5a983dSmrg#include <unistd.h> 33fc5a983dSmrg 34fc5a983dSmrg#include "g80_type.h" 35fc5a983dSmrg#include "g80_cursor.h" 36fc5a983dSmrg#include "g80_display.h" 37fc5a983dSmrg#include "g80_output.h" 38fc5a983dSmrg 39fc5a983dSmrgtypedef struct G80CrtcPrivRec { 40fc5a983dSmrg Head head; 41fc5a983dSmrg int pclk; /* Target pixel clock in kHz */ 42fc5a983dSmrg Bool cursorVisible; 43fc5a983dSmrg Bool skipModeFixup; 44fc5a983dSmrg Bool dither; 4571ba42d0Smrg /* Look-up table values to be set when the CRTC is enabled */ 4671ba42d0Smrg uint16_t lut_r[256], lut_g[256], lut_b[256]; 47fc5a983dSmrg} G80CrtcPrivRec, *G80CrtcPrivPtr; 48fc5a983dSmrg 49fc5a983dSmrgstatic void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update); 50fc5a983dSmrg 51fc5a983dSmrg/* 52fc5a983dSmrg * PLL calculation. pclk is in kHz. 53fc5a983dSmrg */ 54fc5a983dSmrgstatic void 55fc5a983dSmrgG80CalcPLL(float pclk, int *pNA, int *pMA, int *pNB, int *pMB, int *pP) 56fc5a983dSmrg{ 57fc5a983dSmrg const float refclk = 27000.0f; 58fc5a983dSmrg const float minVcoA = 100000; 59fc5a983dSmrg const float maxVcoA = 400000; 60fc5a983dSmrg const float minVcoB = 600000; 61fc5a983dSmrg float maxVcoB = 1400000; 62fc5a983dSmrg const float minUA = 2000; 63fc5a983dSmrg const float maxUA = 400000; 64fc5a983dSmrg const float minUB = 50000; 65fc5a983dSmrg const float maxUB = 200000; 66fc5a983dSmrg const int minNA = 1, maxNA = 255; 67fc5a983dSmrg const int minNB = 1, maxNB = 31; 68fc5a983dSmrg const int minMA = 1, maxMA = 255; 69fc5a983dSmrg const int minMB = 1, maxMB = 31; 70fc5a983dSmrg const int minP = 0, maxP = 6; 71fc5a983dSmrg int lowP, highP; 72fc5a983dSmrg float vcoB; 73fc5a983dSmrg 74fc5a983dSmrg int na, ma, nb, mb, p; 75fc5a983dSmrg float bestError = FLT_MAX; 76fc5a983dSmrg 77fc5a983dSmrg *pNA = *pMA = *pNB = *pMB = *pP = 0; 78fc5a983dSmrg 79fc5a983dSmrg if(maxVcoB < pclk + pclk / 200) 80fc5a983dSmrg maxVcoB = pclk + pclk / 200; 81fc5a983dSmrg if(minVcoB / (1 << maxP) > pclk) 82fc5a983dSmrg pclk = minVcoB / (1 << maxP); 83fc5a983dSmrg 84fc5a983dSmrg vcoB = maxVcoB - maxVcoB / 200; 85fc5a983dSmrg lowP = minP; 86fc5a983dSmrg vcoB /= 1 << (lowP + 1); 87fc5a983dSmrg 88fc5a983dSmrg while(pclk <= vcoB && lowP < maxP) 89fc5a983dSmrg { 90fc5a983dSmrg vcoB /= 2; 91fc5a983dSmrg lowP++; 92fc5a983dSmrg } 93fc5a983dSmrg 94fc5a983dSmrg vcoB = maxVcoB + maxVcoB / 200; 95fc5a983dSmrg highP = lowP; 96fc5a983dSmrg vcoB /= 1 << (highP + 1); 97fc5a983dSmrg 98fc5a983dSmrg while(pclk <= vcoB && highP < maxP) 99fc5a983dSmrg { 100fc5a983dSmrg vcoB /= 2; 101fc5a983dSmrg highP++; 102fc5a983dSmrg } 103fc5a983dSmrg 104fc5a983dSmrg for(p = lowP; p <= highP; p++) 105fc5a983dSmrg { 106fc5a983dSmrg for(ma = minMA; ma <= maxMA; ma++) 107fc5a983dSmrg { 108fc5a983dSmrg if(refclk / ma < minUA) 109fc5a983dSmrg break; 110fc5a983dSmrg else if(refclk / ma > maxUA) 111fc5a983dSmrg continue; 112fc5a983dSmrg 113fc5a983dSmrg for(na = minNA; na <= maxNA; na++) 114fc5a983dSmrg { 115fc5a983dSmrg if(refclk * na / ma < minVcoA || refclk * na / ma > maxVcoA) 116fc5a983dSmrg continue; 117fc5a983dSmrg 118fc5a983dSmrg for(mb = minMB; mb <= maxMB; mb++) 119fc5a983dSmrg { 120fc5a983dSmrg if(refclk * na / ma / mb < minUB) 121fc5a983dSmrg break; 122fc5a983dSmrg else if(refclk * na / ma / mb > maxUB) 123fc5a983dSmrg continue; 124fc5a983dSmrg 125fc5a983dSmrg nb = rint(pclk * (1 << p) * (ma / (float)na) * mb / refclk); 126fc5a983dSmrg 127fc5a983dSmrg if(nb > maxNB) 128fc5a983dSmrg break; 129fc5a983dSmrg else if(nb < minNB) 130fc5a983dSmrg continue; 131fc5a983dSmrg else 132fc5a983dSmrg { 133fc5a983dSmrg float freq = refclk * (na / (float)ma) * (nb / (float)mb) / (1 << p); 134fc5a983dSmrg float error = fabsf(pclk - freq); 135fc5a983dSmrg if(error < bestError) { 136fc5a983dSmrg *pNA = na; 137fc5a983dSmrg *pMA = ma; 138fc5a983dSmrg *pNB = nb; 139fc5a983dSmrg *pMB = mb; 140fc5a983dSmrg *pP = p; 141fc5a983dSmrg bestError = error; 142fc5a983dSmrg } 143fc5a983dSmrg } 144fc5a983dSmrg } 145fc5a983dSmrg } 146fc5a983dSmrg } 147fc5a983dSmrg } 148fc5a983dSmrg} 149fc5a983dSmrg 150bd2f6fc9Smrgstatic void 151bd2f6fc9SmrgG80CalcPLL2(float pclk, int *pN, int *pM, int *pPL) 152bd2f6fc9Smrg{ 153bd2f6fc9Smrg const float refclk = 27000.0f; 154bd2f6fc9Smrg const int minN = 8, maxN = 255; 155bd2f6fc9Smrg const int minM = 1, maxM = 255; 156bd2f6fc9Smrg const int minPL = 1, maxPL = 63; 157bd2f6fc9Smrg const int minU = 25000, maxU = 50000; 158bd2f6fc9Smrg const int minVco = 500000; 159bd2f6fc9Smrg int maxVco = 1000000; 160bd2f6fc9Smrg int lowPL, highPL, pl; 161bd2f6fc9Smrg float vco, bestError = FLT_MAX; 162bd2f6fc9Smrg 163bd2f6fc9Smrg vco = pclk + pclk / 50; 164bd2f6fc9Smrg 165bd2f6fc9Smrg if(maxVco < vco) maxVco = vco; 166bd2f6fc9Smrg 167bd2f6fc9Smrg highPL = (maxVco + vco - 1) / pclk; 168bd2f6fc9Smrg if(highPL > maxPL) highPL = maxPL; 169bd2f6fc9Smrg if(highPL < minPL) highPL = minPL; 170bd2f6fc9Smrg 171bd2f6fc9Smrg lowPL = minVco / vco; 172bd2f6fc9Smrg if(lowPL > maxPL) lowPL = maxPL; 173bd2f6fc9Smrg if(lowPL < minPL) lowPL = minPL; 174bd2f6fc9Smrg 175bd2f6fc9Smrg for(pl = highPL; pl >= lowPL; pl--) { 176bd2f6fc9Smrg int m; 177bd2f6fc9Smrg 178bd2f6fc9Smrg for(m = minM; m <= maxM; m++) { 179bd2f6fc9Smrg int n; 180bd2f6fc9Smrg float freq, error; 181bd2f6fc9Smrg 182bd2f6fc9Smrg if(refclk / m < minU) break; 183bd2f6fc9Smrg if(refclk / m > maxU) continue; 184bd2f6fc9Smrg 185bd2f6fc9Smrg n = rint(pclk * pl * m / refclk); 186bd2f6fc9Smrg if(n > maxN) break; 187bd2f6fc9Smrg if(n < minN) continue; 188bd2f6fc9Smrg 189bd2f6fc9Smrg freq = refclk * (n / (float)m) / pl; 190bd2f6fc9Smrg error = fabsf(pclk - freq); 191bd2f6fc9Smrg if(error < bestError) { 192bd2f6fc9Smrg *pN = n; 193bd2f6fc9Smrg *pM = m; 194bd2f6fc9Smrg *pPL = pl; 195bd2f6fc9Smrg bestError = error; 196bd2f6fc9Smrg } 197bd2f6fc9Smrg } 198bd2f6fc9Smrg } 199bd2f6fc9Smrg} 200bd2f6fc9Smrg 201fc5a983dSmrgstatic void 202fc5a983dSmrgG80CrtcSetPClk(xf86CrtcPtr crtc) 203fc5a983dSmrg{ 204fc5a983dSmrg G80Ptr pNv = G80PTR(crtc->scrn); 205fc5a983dSmrg G80CrtcPrivPtr pPriv = crtc->driver_private; 206fc5a983dSmrg xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); 207fc5a983dSmrg const int headOff = 0x800 * pPriv->head; 208bd2f6fc9Smrg int i; 209bd2f6fc9Smrg 210bd2f6fc9Smrg if(pPriv->pclk == 0) 211bd2f6fc9Smrg return; 212bd2f6fc9Smrg 213bd2f6fc9Smrg if(pNv->architecture <= 0xa0 || 214bd2f6fc9Smrg pNv->architecture == 0xaa || 215bd2f6fc9Smrg pNv->architecture == 0xac) { 216bd304fc0Smrg int lo_n, lo_m, hi_n, hi_m, p; 217bd2f6fc9Smrg CARD32 lo = pNv->reg[(0x00614104+headOff)/4]; 218bd2f6fc9Smrg CARD32 hi = pNv->reg[(0x00614108+headOff)/4]; 219bd2f6fc9Smrg 220bd2f6fc9Smrg pNv->reg[(0x00614100+headOff)/4] = 0x10000610; 221bd2f6fc9Smrg lo &= 0xff00ff00; 222bd2f6fc9Smrg hi &= 0x8000ff00; 223fc5a983dSmrg 224bd2f6fc9Smrg G80CalcPLL(pPriv->pclk, &lo_n, &lo_m, &hi_n, &hi_m, &p); 225fc5a983dSmrg 226bd2f6fc9Smrg lo |= (lo_m << 16) | lo_n; 227bd2f6fc9Smrg hi |= (p << 28) | (hi_m << 16) | hi_n; 228bd2f6fc9Smrg pNv->reg[(0x00614104+headOff)/4] = lo; 229bd2f6fc9Smrg pNv->reg[(0x00614108+headOff)/4] = hi; 230bd2f6fc9Smrg } else { 231bd2f6fc9Smrg int n, m, pl; 232bd2f6fc9Smrg CARD32 r = pNv->reg[(0x00614104+headOff)/4]; 233fc5a983dSmrg 234bd2f6fc9Smrg pNv->reg[(0x00614100+headOff)/4] = 0x50000610; 235bd2f6fc9Smrg r &= 0xffc00000; 236bd2f6fc9Smrg 237bd2f6fc9Smrg G80CalcPLL2(pPriv->pclk, &n, &m, &pl); 238bd2f6fc9Smrg r |= pl << 16 | m << 8 | n; 239bd2f6fc9Smrg 240bd2f6fc9Smrg pNv->reg[(0x00614104+headOff)/4] = r; 241bd2f6fc9Smrg } 242fc5a983dSmrg pNv->reg[(0x00614200+headOff)/4] = 0; 243fc5a983dSmrg 244fc5a983dSmrg for(i = 0; i < xf86_config->num_output; i++) { 245fc5a983dSmrg xf86OutputPtr output = xf86_config->output[i]; 246fc5a983dSmrg 247fc5a983dSmrg if(output->crtc != crtc) 248fc5a983dSmrg continue; 249fc5a983dSmrg G80OutputSetPClk(output, pPriv->pclk); 250fc5a983dSmrg } 251fc5a983dSmrg} 252fc5a983dSmrg 253fc5a983dSmrgvoid 254fc5a983dSmrgG80DispCommand(ScrnInfoPtr pScrn, CARD32 addr, CARD32 data) 255fc5a983dSmrg{ 256fc5a983dSmrg G80Ptr pNv = G80PTR(pScrn); 257fc5a983dSmrg 258fc5a983dSmrg pNv->reg[0x00610304/4] = data; 259fc5a983dSmrg pNv->reg[0x00610300/4] = addr | 0x80010001; 260fc5a983dSmrg 261fc5a983dSmrg while(pNv->reg[0x00610300/4] & 0x80000000) { 262fc5a983dSmrg const int super = ffs((pNv->reg[0x00610024/4] >> 4) & 7); 263fc5a983dSmrg 264fc5a983dSmrg if(super) { 265fc5a983dSmrg if(super == 2) { 266fc5a983dSmrg xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 267fc5a983dSmrg int i; 268fc5a983dSmrg 269fc5a983dSmrg for(i = 0; i < xf86_config->num_crtc; i++) 270fc5a983dSmrg { 271fc5a983dSmrg xf86CrtcPtr crtc = xf86_config->crtc[i]; 272f3561b8bSmrg const int headOff = 0x800 * G80CrtcGetHead(crtc); 273fc5a983dSmrg 274f3561b8bSmrg if((pNv->reg[(0x00614200+headOff)/4] & 0xc0) == 0x80) 275fc5a983dSmrg G80CrtcSetPClk(crtc); 276fc5a983dSmrg } 277fc5a983dSmrg } 278fc5a983dSmrg 279fc5a983dSmrg pNv->reg[0x00610024/4] = 8 << super; 280fc5a983dSmrg pNv->reg[0x00610030/4] = 0x80000000; 281fc5a983dSmrg } 282fc5a983dSmrg } 283fc5a983dSmrg} 284fc5a983dSmrg 285fc5a983dSmrgHead 286fc5a983dSmrgG80CrtcGetHead(xf86CrtcPtr crtc) 287fc5a983dSmrg{ 288fc5a983dSmrg G80CrtcPrivPtr pPriv = crtc->driver_private; 289fc5a983dSmrg return pPriv->head; 290fc5a983dSmrg} 291fc5a983dSmrg 292fc5a983dSmrgBool 293fc5a983dSmrgG80DispPreInit(ScrnInfoPtr pScrn) 294fc5a983dSmrg{ 295fc5a983dSmrg G80Ptr pNv = G80PTR(pScrn); 296fc5a983dSmrg 297fc5a983dSmrg pNv->reg[0x00610184/4] = pNv->reg[0x00614004/4]; 298fc5a983dSmrg pNv->reg[0x00610190/4] = pNv->reg[0x00616100/4]; 299fc5a983dSmrg pNv->reg[0x006101a0/4] = pNv->reg[0x00616900/4]; 300fc5a983dSmrg pNv->reg[0x00610194/4] = pNv->reg[0x00616104/4]; 301fc5a983dSmrg pNv->reg[0x006101a4/4] = pNv->reg[0x00616904/4]; 302fc5a983dSmrg pNv->reg[0x00610198/4] = pNv->reg[0x00616108/4]; 303fc5a983dSmrg pNv->reg[0x006101a8/4] = pNv->reg[0x00616908/4]; 304fc5a983dSmrg pNv->reg[0x0061019C/4] = pNv->reg[0x0061610C/4]; 305fc5a983dSmrg pNv->reg[0x006101ac/4] = pNv->reg[0x0061690c/4]; 306fc5a983dSmrg pNv->reg[0x006101D0/4] = pNv->reg[0x0061A000/4]; 307fc5a983dSmrg pNv->reg[0x006101D4/4] = pNv->reg[0x0061A800/4]; 308fc5a983dSmrg pNv->reg[0x006101D8/4] = pNv->reg[0x0061B000/4]; 309fc5a983dSmrg pNv->reg[0x006101E0/4] = pNv->reg[0x0061C000/4]; 310fc5a983dSmrg pNv->reg[0x006101E4/4] = pNv->reg[0x0061C800/4]; 311bd2f6fc9Smrg pNv->reg[0x006101E8/4] = pNv->reg[0x0061D000/4]; 312bd2f6fc9Smrg pNv->reg[0x006101EC/4] = pNv->reg[0x0061D800/4]; 313fc5a983dSmrg pNv->reg[0x0061A004/4] = 0x80550000; 314fc5a983dSmrg pNv->reg[0x0061A010/4] = 0x00000001; 315fc5a983dSmrg pNv->reg[0x0061A804/4] = 0x80550000; 316fc5a983dSmrg pNv->reg[0x0061A810/4] = 0x00000001; 317fc5a983dSmrg pNv->reg[0x0061B004/4] = 0x80550000; 318fc5a983dSmrg pNv->reg[0x0061B010/4] = 0x00000001; 319fc5a983dSmrg 320fc5a983dSmrg return TRUE; 321fc5a983dSmrg} 322fc5a983dSmrg 323fc5a983dSmrgBool 324fc5a983dSmrgG80DispInit(ScrnInfoPtr pScrn) 325fc5a983dSmrg{ 326fc5a983dSmrg G80Ptr pNv = G80PTR(pScrn); 327fc5a983dSmrg CARD32 val; 328fc5a983dSmrg 329fc5a983dSmrg if(pNv->reg[0x00610024/4] & 0x100) { 330fc5a983dSmrg pNv->reg[0x00610024/4] = 0x100; 331fc5a983dSmrg pNv->reg[0x006194E8/4] &= ~1; 332fc5a983dSmrg while(pNv->reg[0x006194E8/4] & 2); 333fc5a983dSmrg } 334fc5a983dSmrg 335fc5a983dSmrg pNv->reg[0x00610200/4] = 0x2b00; 336fc5a983dSmrg do { 337fc5a983dSmrg val = pNv->reg[0x00610200/4]; 338fc5a983dSmrg 339fc5a983dSmrg if ((val & 0x9f0000) == 0x20000) 340fc5a983dSmrg pNv->reg[0x00610200/4] = val | 0x800000; 341fc5a983dSmrg 342fc5a983dSmrg if ((val & 0x3f0000) == 0x30000) 343fc5a983dSmrg pNv->reg[0x00610200/4] = val | 0x200000; 344fc5a983dSmrg } while ((val & 0x1e0000) != 0); 345fc5a983dSmrg pNv->reg[0x00610300/4] = 1; 346fc5a983dSmrg pNv->reg[0x00610200/4] = 0x1000b03; 347fc5a983dSmrg while(!(pNv->reg[0x00610200/4] & 0x40000000)); 348fc5a983dSmrg 349fc5a983dSmrg C(0x00000084, 0); 350fc5a983dSmrg C(0x00000088, 0); 351fc5a983dSmrg C(0x00000874, 0); 352fc5a983dSmrg C(0x00000800, 0); 353fc5a983dSmrg C(0x00000810, 0); 354fc5a983dSmrg C(0x0000082C, 0); 355fc5a983dSmrg 356fc5a983dSmrg return TRUE; 357fc5a983dSmrg} 358fc5a983dSmrg 359fc5a983dSmrgvoid 360fc5a983dSmrgG80DispShutdown(ScrnInfoPtr pScrn) 361fc5a983dSmrg{ 362fc5a983dSmrg G80Ptr pNv = G80PTR(pScrn); 363fc5a983dSmrg xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 364fc5a983dSmrg int i; 365fc5a983dSmrg 366fc5a983dSmrg for(i = 0; i < xf86_config->num_crtc; i++) { 367fc5a983dSmrg xf86CrtcPtr crtc = xf86_config->crtc[i]; 368fc5a983dSmrg 369fc5a983dSmrg G80CrtcBlankScreen(crtc, TRUE); 370fc5a983dSmrg } 371fc5a983dSmrg 372fc5a983dSmrg C(0x00000080, 0); 373fc5a983dSmrg 374fc5a983dSmrg for(i = 0; i < xf86_config->num_crtc; i++) { 375fc5a983dSmrg xf86CrtcPtr crtc = xf86_config->crtc[i]; 376fc5a983dSmrg 377fc5a983dSmrg if(crtc->enabled) { 378fc5a983dSmrg const CARD32 mask = 4 << G80CrtcGetHead(crtc); 379fc5a983dSmrg 380fc5a983dSmrg pNv->reg[0x00610024/4] = mask; 381fc5a983dSmrg while(!(pNv->reg[0x00610024/4] & mask)); 382fc5a983dSmrg } 383fc5a983dSmrg } 384fc5a983dSmrg 385fc5a983dSmrg pNv->reg[0x00610200/4] = 0; 386fc5a983dSmrg pNv->reg[0x00610300/4] = 0; 387fc5a983dSmrg while((pNv->reg[0x00610200/4] & 0x1e0000) != 0); 388fc5a983dSmrg while((pNv->reg[0x61C030/4] & 0x10000000)); 389fc5a983dSmrg while((pNv->reg[0x61C830/4] & 0x10000000)); 390fc5a983dSmrg} 391fc5a983dSmrg 392fc5a983dSmrgvoid 393fc5a983dSmrgG80CrtcDoModeFixup(DisplayModePtr dst, const DisplayModePtr src) 394fc5a983dSmrg{ 395fc5a983dSmrg /* Magic mode timing fudge factor */ 396fc5a983dSmrg const int fudge = ((src->Flags & V_INTERLACE) && (src->Flags & V_DBLSCAN)) ? 2 : 1; 397fc5a983dSmrg const int interlaceDiv = (src->Flags & V_INTERLACE) ? 2 : 1; 398fc5a983dSmrg 399fc5a983dSmrg /* Stash the src timings in the Crtc fields in dst */ 400fc5a983dSmrg dst->CrtcHBlankStart = src->CrtcVTotal << 16 | src->CrtcHTotal; 401fc5a983dSmrg dst->CrtcHSyncEnd = ((src->CrtcVSyncEnd - src->CrtcVSyncStart) / interlaceDiv - 1) << 16 | 402fc5a983dSmrg (src->CrtcHSyncEnd - src->CrtcHSyncStart - 1); 403fc5a983dSmrg dst->CrtcHBlankEnd = ((src->CrtcVBlankEnd - src->CrtcVSyncStart) / interlaceDiv - fudge) << 16 | 404fc5a983dSmrg (src->CrtcHBlankEnd - src->CrtcHSyncStart - 1); 405fc5a983dSmrg dst->CrtcHTotal = ((src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / interlaceDiv - fudge) << 16 | 406fc5a983dSmrg (src->CrtcHTotal - src->CrtcHSyncStart + src->CrtcHBlankStart - 1); 407fc5a983dSmrg dst->CrtcHSkew = ((src->CrtcVTotal + src->CrtcVBlankEnd - src->CrtcVSyncStart) / 2 - 2) << 16 | 408fc5a983dSmrg ((2*src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / 2 - 2); 409fc5a983dSmrg} 410fc5a983dSmrg 411fc5a983dSmrgstatic Bool 412fc5a983dSmrgG80CrtcModeFixup(xf86CrtcPtr crtc, 413fc5a983dSmrg DisplayModePtr mode, DisplayModePtr adjusted_mode) 414fc5a983dSmrg{ 415fc5a983dSmrg G80CrtcPrivPtr pPriv = crtc->driver_private; 416fc5a983dSmrg 417fc5a983dSmrg if(pPriv->skipModeFixup) 418fc5a983dSmrg return TRUE; 419fc5a983dSmrg 420fc5a983dSmrg G80CrtcDoModeFixup(adjusted_mode, mode); 421fc5a983dSmrg return TRUE; 422fc5a983dSmrg} 423fc5a983dSmrg 424fc5a983dSmrgstatic void 425fc5a983dSmrgG80CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode, 426fc5a983dSmrg DisplayModePtr adjusted_mode, int x, int y) 427fc5a983dSmrg{ 428fc5a983dSmrg ScrnInfoPtr pScrn = crtc->scrn; 429fc5a983dSmrg G80CrtcPrivPtr pPriv = crtc->driver_private; 430fc5a983dSmrg const int HDisplay = adjusted_mode->HDisplay, VDisplay = adjusted_mode->VDisplay; 431fc5a983dSmrg const int headOff = 0x400 * G80CrtcGetHead(crtc); 432fc5a983dSmrg 433fc5a983dSmrg pPriv->pclk = adjusted_mode->Clock; 434fc5a983dSmrg 435fc5a983dSmrg C(0x00000804 + headOff, adjusted_mode->Clock | 0x800000); 436fc5a983dSmrg C(0x00000808 + headOff, (adjusted_mode->Flags & V_INTERLACE) ? 2 : 0); 437fc5a983dSmrg C(0x00000810 + headOff, 0); 438fc5a983dSmrg C(0x0000082C + headOff, 0); 439fc5a983dSmrg C(0x00000814 + headOff, adjusted_mode->CrtcHBlankStart); 440fc5a983dSmrg C(0x00000818 + headOff, adjusted_mode->CrtcHSyncEnd); 441fc5a983dSmrg C(0x0000081C + headOff, adjusted_mode->CrtcHBlankEnd); 442fc5a983dSmrg C(0x00000820 + headOff, adjusted_mode->CrtcHTotal); 443fc5a983dSmrg if(adjusted_mode->Flags & V_INTERLACE) 444fc5a983dSmrg C(0x00000824 + headOff, adjusted_mode->CrtcHSkew); 445fc5a983dSmrg C(0x00000868 + headOff, pScrn->virtualY << 16 | pScrn->virtualX); 446fc5a983dSmrg C(0x0000086C + headOff, pScrn->displayWidth * (pScrn->bitsPerPixel / 8) | 0x100000); 447fc5a983dSmrg switch(pScrn->depth) { 448fc5a983dSmrg case 8: C(0x00000870 + headOff, 0x1E00); break; 449fc5a983dSmrg case 15: C(0x00000870 + headOff, 0xE900); break; 450fc5a983dSmrg case 16: C(0x00000870 + headOff, 0xE800); break; 451fc5a983dSmrg case 24: C(0x00000870 + headOff, 0xCF00); break; 452fc5a983dSmrg } 453fc5a983dSmrg G80CrtcSetDither(crtc, pPriv->dither, FALSE); 454fc5a983dSmrg C(0x000008A8 + headOff, 0x40000); 455fc5a983dSmrg C(0x000008C0 + headOff, y << 16 | x); 456fc5a983dSmrg C(0x000008C8 + headOff, VDisplay << 16 | HDisplay); 457fc5a983dSmrg C(0x000008D4 + headOff, 0); 458fc5a983dSmrg 459fc5a983dSmrg G80CrtcBlankScreen(crtc, FALSE); 460fc5a983dSmrg} 461fc5a983dSmrg 462fc5a983dSmrgvoid 463fc5a983dSmrgG80CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank) 464fc5a983dSmrg{ 465fc5a983dSmrg ScrnInfoPtr pScrn = crtc->scrn; 466fc5a983dSmrg G80Ptr pNv = G80PTR(pScrn); 467fc5a983dSmrg G80CrtcPrivPtr pPriv = crtc->driver_private; 468fc5a983dSmrg const int headOff = 0x400 * pPriv->head; 469fc5a983dSmrg 470fc5a983dSmrg if(blank) { 471fc5a983dSmrg G80CrtcShowHideCursor(crtc, FALSE, FALSE); 472fc5a983dSmrg 473fc5a983dSmrg C(0x00000840 + headOff, 0); 474fc5a983dSmrg C(0x00000844 + headOff, 0); 475fc5a983dSmrg if(pNv->architecture != 0x50) 476fc5a983dSmrg C(0x0000085C + headOff, 0); 477fc5a983dSmrg C(0x00000874 + headOff, 0); 478fc5a983dSmrg if(pNv->architecture != 0x50) 479fc5a983dSmrg C(0x0000089C + headOff, 0); 480fc5a983dSmrg } else { 481fc5a983dSmrg C(0x00000860 + headOff, 0); 482fc5a983dSmrg C(0x00000864 + headOff, 0); 483fc5a983dSmrg pNv->reg[0x00610380/4] = 0; 484fc5a983dSmrg pNv->reg[0x00610384/4] = pNv->videoRam * 1024 - 1; 485fc5a983dSmrg pNv->reg[0x00610388/4] = 0x150000; 486fc5a983dSmrg pNv->reg[0x0061038C/4] = 0; 487fc5a983dSmrg C(0x00000884 + headOff, (pNv->videoRam << 2) - 0x40); 488fc5a983dSmrg if(pNv->architecture != 0x50) 489fc5a983dSmrg C(0x0000089C + headOff, 1); 490fc5a983dSmrg if(pPriv->cursorVisible) 491fc5a983dSmrg G80CrtcShowHideCursor(crtc, TRUE, FALSE); 492fc5a983dSmrg C(0x00000840 + headOff, pScrn->depth == 8 ? 0x80000000 : 0xc0000000); 49371ba42d0Smrg C(0x00000844 + headOff, (pNv->videoRam * 1024 - 0x5000 - 0x1000 * pPriv->head) >> 8); 494fc5a983dSmrg if(pNv->architecture != 0x50) 495fc5a983dSmrg C(0x0000085C + headOff, 1); 496fc5a983dSmrg C(0x00000874 + headOff, 1); 497fc5a983dSmrg } 498fc5a983dSmrg} 499fc5a983dSmrg 500fc5a983dSmrgstatic void 501fc5a983dSmrgG80CrtcDPMSSet(xf86CrtcPtr crtc, int mode) 502fc5a983dSmrg{ 503fc5a983dSmrg} 504fc5a983dSmrg 505fc5a983dSmrg/******************************** Cursor stuff ********************************/ 506fc5a983dSmrgstatic void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update) 507fc5a983dSmrg{ 508fc5a983dSmrg ScrnInfoPtr pScrn = crtc->scrn; 509fc5a983dSmrg G80CrtcPrivPtr pPriv = crtc->driver_private; 510fc5a983dSmrg const int headOff = 0x400 * G80CrtcGetHead(crtc); 511fc5a983dSmrg 512fc5a983dSmrg C(0x00000880 + headOff, show ? 0x85000000 : 0x5000000); 513fc5a983dSmrg if(update) { 514fc5a983dSmrg pPriv->cursorVisible = show; 515fc5a983dSmrg C(0x00000080, 0); 516fc5a983dSmrg } 517fc5a983dSmrg} 518fc5a983dSmrg 519fc5a983dSmrgstatic void G80CrtcShowCursor(xf86CrtcPtr crtc) 520fc5a983dSmrg{ 521fc5a983dSmrg G80CrtcShowHideCursor(crtc, TRUE, TRUE); 522fc5a983dSmrg} 523fc5a983dSmrg 524fc5a983dSmrgstatic void G80CrtcHideCursor(xf86CrtcPtr crtc) 525fc5a983dSmrg{ 526fc5a983dSmrg G80CrtcShowHideCursor(crtc, FALSE, TRUE); 527fc5a983dSmrg} 528fc5a983dSmrg 529fc5a983dSmrg/******************************** CRTC stuff ********************************/ 530fc5a983dSmrg 531fc5a983dSmrgstatic Bool 532fc5a983dSmrgG80CrtcLock(xf86CrtcPtr crtc) 533fc5a983dSmrg{ 534fc5a983dSmrg return FALSE; 535fc5a983dSmrg} 536fc5a983dSmrg 537fc5a983dSmrgstatic void 538fc5a983dSmrgG80CrtcPrepare(xf86CrtcPtr crtc) 539fc5a983dSmrg{ 540fc5a983dSmrg ScrnInfoPtr pScrn = crtc->scrn; 541fc5a983dSmrg G80CrtcPrivPtr pPriv = crtc->driver_private; 542fc5a983dSmrg xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 543fc5a983dSmrg int i; 544fc5a983dSmrg 545fc5a983dSmrg for(i = 0; i < xf86_config->num_output; i++) { 546fc5a983dSmrg xf86OutputPtr output = xf86_config->output[i]; 547fc5a983dSmrg 548fc5a983dSmrg if(!output->crtc) 549fc5a983dSmrg output->funcs->mode_set(output, NULL, NULL); 550fc5a983dSmrg } 551fc5a983dSmrg 552fc5a983dSmrg pPriv->skipModeFixup = FALSE; 553fc5a983dSmrg} 554fc5a983dSmrg 555fc5a983dSmrgvoid 556fc5a983dSmrgG80CrtcSkipModeFixup(xf86CrtcPtr crtc) 557fc5a983dSmrg{ 558fc5a983dSmrg G80CrtcPrivPtr pPriv = crtc->driver_private; 559fc5a983dSmrg pPriv->skipModeFixup = TRUE; 560fc5a983dSmrg} 561fc5a983dSmrg 562fc5a983dSmrgvoid 563fc5a983dSmrgG80CrtcSetDither(xf86CrtcPtr crtc, Bool dither, Bool update) 564fc5a983dSmrg{ 565fc5a983dSmrg ScrnInfoPtr pScrn = crtc->scrn; 566fc5a983dSmrg G80CrtcPrivPtr pPriv = crtc->driver_private; 567fc5a983dSmrg const int headOff = 0x400 * G80CrtcGetHead(crtc); 568fc5a983dSmrg 569fc5a983dSmrg pPriv->dither = dither; 570fc5a983dSmrg 571fc5a983dSmrg C(0x000008A0 + headOff, dither ? 0x11 : 0); 572fc5a983dSmrg if(update) C(0x00000080, 0); 573fc5a983dSmrg} 574fc5a983dSmrg 575fc5a983dSmrgstatic void ComputeAspectScale(DisplayModePtr mode, int *outX, int *outY) 576fc5a983dSmrg{ 577fc5a983dSmrg float scaleX, scaleY, scale; 578fc5a983dSmrg 579fc5a983dSmrg scaleX = mode->CrtcHDisplay / (float)mode->HDisplay; 580fc5a983dSmrg scaleY = mode->CrtcVDisplay / (float)mode->VDisplay; 581fc5a983dSmrg 582fc5a983dSmrg if(scaleX > scaleY) 583fc5a983dSmrg scale = scaleY; 584fc5a983dSmrg else 585fc5a983dSmrg scale = scaleX; 586fc5a983dSmrg 587fc5a983dSmrg *outX = mode->HDisplay * scale; 588fc5a983dSmrg *outY = mode->VDisplay * scale; 589fc5a983dSmrg} 590fc5a983dSmrg 591fc5a983dSmrgvoid G80CrtcSetScale(xf86CrtcPtr crtc, DisplayModePtr mode, 592fc5a983dSmrg enum G80ScaleMode scale) 593fc5a983dSmrg{ 594fc5a983dSmrg ScrnInfoPtr pScrn = crtc->scrn; 595fc5a983dSmrg G80CrtcPrivPtr pPriv = crtc->driver_private; 596fc5a983dSmrg const int headOff = 0x400 * pPriv->head; 597fc5a983dSmrg int outX, outY; 598fc5a983dSmrg 599fc5a983dSmrg switch(scale) { 600fc5a983dSmrg default: 601fc5a983dSmrg case G80_SCALE_ASPECT: 602fc5a983dSmrg ComputeAspectScale(mode, &outX, &outY); 603fc5a983dSmrg break; 604fc5a983dSmrg 605fc5a983dSmrg case G80_SCALE_OFF: 606fc5a983dSmrg case G80_SCALE_FILL: 607fc5a983dSmrg outX = mode->CrtcHDisplay; 608fc5a983dSmrg outY = mode->CrtcVDisplay; 609fc5a983dSmrg break; 610fc5a983dSmrg 611fc5a983dSmrg case G80_SCALE_CENTER: 612fc5a983dSmrg outX = mode->HDisplay; 613fc5a983dSmrg outY = mode->VDisplay; 614fc5a983dSmrg break; 615fc5a983dSmrg } 616fc5a983dSmrg 617fc5a983dSmrg if((mode->Flags & V_DBLSCAN) || (mode->Flags & V_INTERLACE) || 618fc5a983dSmrg mode->HDisplay != outX || mode->VDisplay != outY) { 619fc5a983dSmrg C(0x000008A4 + headOff, 9); 620fc5a983dSmrg } else { 621fc5a983dSmrg C(0x000008A4 + headOff, 0); 622fc5a983dSmrg } 623fc5a983dSmrg C(0x000008D8 + headOff, outY << 16 | outX); 624fc5a983dSmrg C(0x000008DC + headOff, outY << 16 | outX); 625fc5a983dSmrg} 626fc5a983dSmrg 627fc5a983dSmrgstatic void 628fc5a983dSmrgG80CrtcCommit(xf86CrtcPtr crtc) 629fc5a983dSmrg{ 630fc5a983dSmrg ScrnInfoPtr pScrn = crtc->scrn; 631fc5a983dSmrg xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); 632fc5a983dSmrg int i, crtc_mask = 0; 633fc5a983dSmrg 634fc5a983dSmrg /* If any heads are unused, blank them */ 635fc5a983dSmrg for(i = 0; i < xf86_config->num_output; i++) { 636fc5a983dSmrg xf86OutputPtr output = xf86_config->output[i]; 637fc5a983dSmrg 638fc5a983dSmrg if(output->crtc) 639fc5a983dSmrg /* XXXagp: This assumes that xf86_config->crtc[i] is HEADi */ 640fc5a983dSmrg crtc_mask |= 1 << G80CrtcGetHead(output->crtc); 641fc5a983dSmrg } 642fc5a983dSmrg 643fc5a983dSmrg for(i = 0; i < xf86_config->num_crtc; i++) 644fc5a983dSmrg if(!((1 << i) & crtc_mask)) 645fc5a983dSmrg G80CrtcBlankScreen(xf86_config->crtc[i], TRUE); 646fc5a983dSmrg 647fc5a983dSmrg C(0x00000080, 0); 648fc5a983dSmrg} 649fc5a983dSmrg 65071ba42d0Smrgstatic void 65171ba42d0SmrgG80CrtcGammaSet(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green, CARD16 *blue, 65271ba42d0Smrg int size) 65371ba42d0Smrg{ 65471ba42d0Smrg ScrnInfoPtr pScrn = crtc->scrn; 65571ba42d0Smrg G80Ptr pNv = G80PTR(pScrn); 65671ba42d0Smrg G80CrtcPrivPtr pPriv = crtc->driver_private; 65771ba42d0Smrg int i; 65871ba42d0Smrg volatile struct { 65971ba42d0Smrg uint16_t red, green, blue, unused; 66071ba42d0Smrg } *lut = (void*)&pNv->mem[pNv->videoRam * 1024 - 0x5000 - 0x1000 * pPriv->head]; 66171ba42d0Smrg 66271ba42d0Smrg assert(size == 256); 66371ba42d0Smrg 66471ba42d0Smrg for(i = 0; i < size; i++) { 66571ba42d0Smrg pPriv->lut_r[i] = lut[i].red = red[i] >> 2; 66671ba42d0Smrg pPriv->lut_g[i] = lut[i].green = green[i] >> 2; 66771ba42d0Smrg pPriv->lut_b[i] = lut[i].blue = blue[i] >> 2; 66871ba42d0Smrg } 66971ba42d0Smrg 67071ba42d0Smrg lut[256] = lut[255]; 67171ba42d0Smrg} 67271ba42d0Smrg 67371ba42d0Smrgvoid 67471ba42d0SmrgG80LoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices, LOCO *colors, 67571ba42d0Smrg VisualPtr pVisual) 67671ba42d0Smrg{ 67771ba42d0Smrg xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 67871ba42d0Smrg int i, j, index; 67971ba42d0Smrg int p; 68071ba42d0Smrg uint16_t lut_r[256], lut_g[256], lut_b[256]; 68171ba42d0Smrg 68271ba42d0Smrg for(p = 0; p < xf86_config->num_crtc; p++) { 68371ba42d0Smrg xf86CrtcPtr crtc = xf86_config->crtc[p]; 68471ba42d0Smrg G80CrtcPrivPtr pPriv = crtc->driver_private; 68571ba42d0Smrg 68671ba42d0Smrg /* Initialize to the old lookup table values. */ 68771ba42d0Smrg for(i = 0; i < 256; i++) { 68871ba42d0Smrg lut_r[i] = pPriv->lut_r[i] << 2; 68971ba42d0Smrg lut_g[i] = pPriv->lut_g[i] << 2; 69071ba42d0Smrg lut_b[i] = pPriv->lut_b[i] << 2; 69171ba42d0Smrg } 69271ba42d0Smrg 69371ba42d0Smrg switch(pScrn->depth) { 69471ba42d0Smrg case 15: 69571ba42d0Smrg for(i = 0; i < numColors; i++) { 69671ba42d0Smrg index = indices[i]; 69771ba42d0Smrg for(j = 0; j < 8; j++) { 69871ba42d0Smrg lut_r[index * 8 + j] = 69971ba42d0Smrg colors[index].red << 8; 70071ba42d0Smrg lut_g[index * 8 + j] = 70171ba42d0Smrg colors[index].green << 8; 70271ba42d0Smrg lut_b[index * 8 + j] = 70371ba42d0Smrg colors[index].blue << 8; 70471ba42d0Smrg } 70571ba42d0Smrg } 70671ba42d0Smrg break; 70771ba42d0Smrg case 16: 70871ba42d0Smrg for(i = 0; i < numColors; i++) { 70971ba42d0Smrg index = indices[i]; 71071ba42d0Smrg 71171ba42d0Smrg if(index <= 31) { 71271ba42d0Smrg for(j = 0; j < 8; j++) { 71371ba42d0Smrg lut_r[index * 8 + j] = 71471ba42d0Smrg colors[index].red << 8; 71571ba42d0Smrg lut_b[index * 8 + j] = 71671ba42d0Smrg colors[index].blue << 8; 71771ba42d0Smrg } 71871ba42d0Smrg } 71971ba42d0Smrg 72071ba42d0Smrg for(j = 0; j < 4; j++) { 72171ba42d0Smrg lut_g[index * 4 + j] = 72271ba42d0Smrg colors[index].green << 8; 72371ba42d0Smrg } 72471ba42d0Smrg } 72571ba42d0Smrg break; 72671ba42d0Smrg default: 72771ba42d0Smrg for(i = 0; i < numColors; i++) { 72871ba42d0Smrg index = indices[i]; 72971ba42d0Smrg lut_r[index] = colors[index].red << 8; 73071ba42d0Smrg lut_g[index] = colors[index].green << 8; 73171ba42d0Smrg lut_b[index] = colors[index].blue << 8; 73271ba42d0Smrg } 73371ba42d0Smrg break; 73471ba42d0Smrg } 73571ba42d0Smrg 73671ba42d0Smrg /* Make the change through RandR */ 73771ba42d0Smrg RRCrtcGammaSet(crtc->randr_crtc, lut_r, lut_g, lut_b); 73871ba42d0Smrg } 73971ba42d0Smrg} 74071ba42d0Smrg 741fc5a983dSmrgstatic const xf86CrtcFuncsRec g80_crtc_funcs = { 742fc5a983dSmrg .dpms = G80CrtcDPMSSet, 743fc5a983dSmrg .save = NULL, 744fc5a983dSmrg .restore = NULL, 745fc5a983dSmrg .lock = G80CrtcLock, 746fc5a983dSmrg .unlock = NULL, 747fc5a983dSmrg .mode_fixup = G80CrtcModeFixup, 748fc5a983dSmrg .prepare = G80CrtcPrepare, 749fc5a983dSmrg .mode_set = G80CrtcModeSet, 75071ba42d0Smrg .gamma_set = G80CrtcGammaSet, 751fc5a983dSmrg .commit = G80CrtcCommit, 752fc5a983dSmrg .shadow_create = NULL, 753fc5a983dSmrg .shadow_destroy = NULL, 754fc5a983dSmrg .set_cursor_position = G80SetCursorPosition, 755fc5a983dSmrg .show_cursor = G80CrtcShowCursor, 756fc5a983dSmrg .hide_cursor = G80CrtcHideCursor, 757fc5a983dSmrg .load_cursor_argb = G80LoadCursorARGB, 758fc5a983dSmrg .destroy = NULL, 759fc5a983dSmrg}; 760fc5a983dSmrg 761fc5a983dSmrgvoid 762fc5a983dSmrgG80DispCreateCrtcs(ScrnInfoPtr pScrn) 763fc5a983dSmrg{ 764fc5a983dSmrg G80Ptr pNv = G80PTR(pScrn); 765fc5a983dSmrg Head head; 766fc5a983dSmrg xf86CrtcPtr crtc; 767fc5a983dSmrg G80CrtcPrivPtr g80_crtc; 768fc5a983dSmrg 769fc5a983dSmrg /* Create a "crtc" object for each head */ 770fc5a983dSmrg for(head = HEAD0; head <= HEAD1; head++) { 771fc5a983dSmrg crtc = xf86CrtcCreate(pScrn, &g80_crtc_funcs); 772fc5a983dSmrg if(!crtc) return; 773fc5a983dSmrg 774fc5a983dSmrg g80_crtc = xnfcalloc(sizeof(*g80_crtc), 1); 775fc5a983dSmrg g80_crtc->head = head; 776fc5a983dSmrg g80_crtc->dither = pNv->Dither; 777fc5a983dSmrg crtc->driver_private = g80_crtc; 778fc5a983dSmrg } 779fc5a983dSmrg} 780