Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame^] | 1 | /***************************************************************************\ |
| 2 | |* *| |
| 3 | |* Copyright 2003 NVIDIA, Corporation. All rights reserved. *| |
| 4 | |* *| |
| 5 | |* NOTICE TO USER: The source code is copyrighted under U.S. and *| |
| 6 | |* international laws. Users and possessors of this source code are *| |
| 7 | |* hereby granted a nonexclusive, royalty-free copyright license to *| |
| 8 | |* use this code in individual and commercial software. *| |
| 9 | |* *| |
| 10 | |* Any use of this source code must include, in the user documenta- *| |
| 11 | |* tion and internal comments to the code, notices to the end user *| |
| 12 | |* as follows: *| |
| 13 | |* *| |
| 14 | |* Copyright 2003 NVIDIA, Corporation. All rights reserved. *| |
| 15 | |* *| |
| 16 | |* NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY *| |
| 17 | |* OF THIS SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" *| |
| 18 | |* WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND. NVIDIA, CORPOR- *| |
| 19 | |* ATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOURCE CODE, *| |
| 20 | |* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE- *| |
| 21 | |* MENT, AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL *| |
| 22 | |* NVIDIA, CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT, INCI- *| |
| 23 | |* DENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RE- *| |
| 24 | |* SULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION *| |
| 25 | |* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *| |
| 26 | |* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE. *| |
| 27 | |* *| |
| 28 | |* U.S. Government End Users. This source code is a "commercial *| |
| 29 | |* item," as that term is defined at 48 C.F.R. 2.101 (OCT 1995), *| |
| 30 | |* consisting of "commercial computer software" and "commercial *| |
| 31 | |* computer software documentation," as such terms are used in *| |
| 32 | |* 48 C.F.R. 12.212 (SEPT 1995) and is provided to the U.S. Govern- *| |
| 33 | |* ment only as a commercial end item. Consistent with 48 C.F.R. *| |
| 34 | |* 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (JUNE 1995), *| |
| 35 | |* all U.S. Government End Users acquire the source code with only *| |
| 36 | |* those rights set forth herein. *| |
| 37 | |* *| |
| 38 | \***************************************************************************/ |
| 39 | |
| 40 | /* |
| 41 | * GPL Licensing Note - According to Mark Vojkovich, author of the Xorg/ |
| 42 | * XFree86 'nv' driver, this source code is provided under MIT-style licensing |
| 43 | * where the source code is provided "as is" without warranty of any kind. |
| 44 | * The only usage restriction is for the copyright notices to be retained |
| 45 | * whenever code is used. |
| 46 | * |
| 47 | * Antonino Daplas <adaplas@pol.net> 2005-03-11 |
| 48 | */ |
| 49 | |
| 50 | #include <video/vga.h> |
| 51 | #include <linux/delay.h> |
| 52 | #include <linux/pci.h> |
| 53 | #include "nv_type.h" |
| 54 | #include "nv_local.h" |
| 55 | #include "nv_proto.h" |
| 56 | /* |
| 57 | * Override VGA I/O routines. |
| 58 | */ |
| 59 | void NVWriteCrtc(struct nvidia_par *par, u8 index, u8 value) |
| 60 | { |
| 61 | VGA_WR08(par->PCIO, par->IOBase + 0x04, index); |
| 62 | VGA_WR08(par->PCIO, par->IOBase + 0x05, value); |
| 63 | } |
| 64 | u8 NVReadCrtc(struct nvidia_par *par, u8 index) |
| 65 | { |
| 66 | VGA_WR08(par->PCIO, par->IOBase + 0x04, index); |
| 67 | return (VGA_RD08(par->PCIO, par->IOBase + 0x05)); |
| 68 | } |
| 69 | void NVWriteGr(struct nvidia_par *par, u8 index, u8 value) |
| 70 | { |
| 71 | VGA_WR08(par->PVIO, VGA_GFX_I, index); |
| 72 | VGA_WR08(par->PVIO, VGA_GFX_D, value); |
| 73 | } |
| 74 | u8 NVReadGr(struct nvidia_par *par, u8 index) |
| 75 | { |
| 76 | VGA_WR08(par->PVIO, VGA_GFX_I, index); |
| 77 | return (VGA_RD08(par->PVIO, VGA_GFX_D)); |
| 78 | } |
| 79 | void NVWriteSeq(struct nvidia_par *par, u8 index, u8 value) |
| 80 | { |
| 81 | VGA_WR08(par->PVIO, VGA_SEQ_I, index); |
| 82 | VGA_WR08(par->PVIO, VGA_SEQ_D, value); |
| 83 | } |
| 84 | u8 NVReadSeq(struct nvidia_par *par, u8 index) |
| 85 | { |
| 86 | VGA_WR08(par->PVIO, VGA_SEQ_I, index); |
| 87 | return (VGA_RD08(par->PVIO, VGA_SEQ_D)); |
| 88 | } |
| 89 | void NVWriteAttr(struct nvidia_par *par, u8 index, u8 value) |
| 90 | { |
| 91 | volatile u8 tmp; |
| 92 | |
| 93 | tmp = VGA_RD08(par->PCIO, par->IOBase + 0x0a); |
| 94 | if (par->paletteEnabled) |
| 95 | index &= ~0x20; |
| 96 | else |
| 97 | index |= 0x20; |
| 98 | VGA_WR08(par->PCIO, VGA_ATT_IW, index); |
| 99 | VGA_WR08(par->PCIO, VGA_ATT_W, value); |
| 100 | } |
| 101 | u8 NVReadAttr(struct nvidia_par *par, u8 index) |
| 102 | { |
| 103 | volatile u8 tmp; |
| 104 | |
| 105 | tmp = VGA_RD08(par->PCIO, par->IOBase + 0x0a); |
| 106 | if (par->paletteEnabled) |
| 107 | index &= ~0x20; |
| 108 | else |
| 109 | index |= 0x20; |
| 110 | VGA_WR08(par->PCIO, VGA_ATT_IW, index); |
| 111 | return (VGA_RD08(par->PCIO, VGA_ATT_R)); |
| 112 | } |
| 113 | void NVWriteMiscOut(struct nvidia_par *par, u8 value) |
| 114 | { |
| 115 | VGA_WR08(par->PVIO, VGA_MIS_W, value); |
| 116 | } |
| 117 | u8 NVReadMiscOut(struct nvidia_par *par) |
| 118 | { |
| 119 | return (VGA_RD08(par->PVIO, VGA_MIS_R)); |
| 120 | } |
| 121 | #if 0 |
| 122 | void NVEnablePalette(struct nvidia_par *par) |
| 123 | { |
| 124 | volatile u8 tmp; |
| 125 | |
| 126 | tmp = VGA_RD08(par->PCIO, par->IOBase + 0x0a); |
| 127 | VGA_WR08(par->PCIO, VGA_ATT_IW, 0x00); |
| 128 | par->paletteEnabled = 1; |
| 129 | } |
| 130 | void NVDisablePalette(struct nvidia_par *par) |
| 131 | { |
| 132 | volatile u8 tmp; |
| 133 | |
| 134 | tmp = VGA_RD08(par->PCIO, par->IOBase + 0x0a); |
| 135 | VGA_WR08(par->PCIO, VGA_ATT_IW, 0x20); |
| 136 | par->paletteEnabled = 0; |
| 137 | } |
| 138 | #endif /* 0 */ |
| 139 | void NVWriteDacMask(struct nvidia_par *par, u8 value) |
| 140 | { |
| 141 | VGA_WR08(par->PDIO, VGA_PEL_MSK, value); |
| 142 | } |
| 143 | #if 0 |
| 144 | u8 NVReadDacMask(struct nvidia_par *par) |
| 145 | { |
| 146 | return (VGA_RD08(par->PDIO, VGA_PEL_MSK)); |
| 147 | } |
| 148 | #endif /* 0 */ |
| 149 | void NVWriteDacReadAddr(struct nvidia_par *par, u8 value) |
| 150 | { |
| 151 | VGA_WR08(par->PDIO, VGA_PEL_IR, value); |
| 152 | } |
| 153 | void NVWriteDacWriteAddr(struct nvidia_par *par, u8 value) |
| 154 | { |
| 155 | VGA_WR08(par->PDIO, VGA_PEL_IW, value); |
| 156 | } |
| 157 | void NVWriteDacData(struct nvidia_par *par, u8 value) |
| 158 | { |
| 159 | VGA_WR08(par->PDIO, VGA_PEL_D, value); |
| 160 | } |
| 161 | u8 NVReadDacData(struct nvidia_par *par) |
| 162 | { |
| 163 | return (VGA_RD08(par->PDIO, VGA_PEL_D)); |
| 164 | } |
| 165 | |
| 166 | static int NVIsConnected(struct nvidia_par *par, int output) |
| 167 | { |
| 168 | volatile u32 __iomem *PRAMDAC = par->PRAMDAC0; |
| 169 | u32 reg52C, reg608; |
| 170 | int present; |
| 171 | |
| 172 | if (output) |
| 173 | PRAMDAC += 0x800; |
| 174 | |
| 175 | reg52C = NV_RD32(PRAMDAC, 0x052C); |
| 176 | reg608 = NV_RD32(PRAMDAC, 0x0608); |
| 177 | |
| 178 | NV_WR32(PRAMDAC, 0x0608, reg608 & ~0x00010000); |
| 179 | |
| 180 | NV_WR32(PRAMDAC, 0x052C, reg52C & 0x0000FEEE); |
| 181 | msleep(1); |
| 182 | NV_WR32(PRAMDAC, 0x052C, NV_RD32(PRAMDAC, 0x052C) | 1); |
| 183 | |
| 184 | NV_WR32(par->PRAMDAC0, 0x0610, 0x94050140); |
| 185 | NV_WR32(par->PRAMDAC0, 0x0608, NV_RD32(par->PRAMDAC0, 0x0608) | |
| 186 | 0x00001000); |
| 187 | |
| 188 | msleep(1); |
| 189 | |
| 190 | present = (NV_RD32(PRAMDAC, 0x0608) & (1 << 28)) ? 1 : 0; |
| 191 | |
| 192 | if (present) |
| 193 | printk("nvidiafb: CRTC%i found\n", output); |
| 194 | else |
| 195 | printk("nvidiafb: CRTC%i not found\n", output); |
| 196 | |
| 197 | NV_WR32(par->PRAMDAC0, 0x0608, NV_RD32(par->PRAMDAC0, 0x0608) & |
| 198 | 0x0000EFFF); |
| 199 | |
| 200 | NV_WR32(PRAMDAC, 0x052C, reg52C); |
| 201 | NV_WR32(PRAMDAC, 0x0608, reg608); |
| 202 | |
| 203 | return present; |
| 204 | } |
| 205 | |
| 206 | static void NVSelectHeadRegisters(struct nvidia_par *par, int head) |
| 207 | { |
| 208 | if (head) { |
| 209 | par->PCIO = par->PCIO0 + 0x2000; |
| 210 | par->PCRTC = par->PCRTC0 + 0x800; |
| 211 | par->PRAMDAC = par->PRAMDAC0 + 0x800; |
| 212 | par->PDIO = par->PDIO0 + 0x2000; |
| 213 | } else { |
| 214 | par->PCIO = par->PCIO0; |
| 215 | par->PCRTC = par->PCRTC0; |
| 216 | par->PRAMDAC = par->PRAMDAC0; |
| 217 | par->PDIO = par->PDIO0; |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | static void nv4GetConfig(struct nvidia_par *par) |
| 222 | { |
| 223 | if (NV_RD32(par->PFB, 0x0000) & 0x00000100) { |
| 224 | par->RamAmountKBytes = |
| 225 | ((NV_RD32(par->PFB, 0x0000) >> 12) & 0x0F) * 1024 * 2 + |
| 226 | 1024 * 2; |
| 227 | } else { |
| 228 | switch (NV_RD32(par->PFB, 0x0000) & 0x00000003) { |
| 229 | case 0: |
| 230 | par->RamAmountKBytes = 1024 * 32; |
| 231 | break; |
| 232 | case 1: |
| 233 | par->RamAmountKBytes = 1024 * 4; |
| 234 | break; |
| 235 | case 2: |
| 236 | par->RamAmountKBytes = 1024 * 8; |
| 237 | break; |
| 238 | case 3: |
| 239 | default: |
| 240 | par->RamAmountKBytes = 1024 * 16; |
| 241 | break; |
| 242 | } |
| 243 | } |
| 244 | par->CrystalFreqKHz = (NV_RD32(par->PEXTDEV, 0x0000) & 0x00000040) ? |
| 245 | 14318 : 13500; |
| 246 | par->CURSOR = &par->PRAMIN[0x1E00]; |
| 247 | par->MinVClockFreqKHz = 12000; |
| 248 | par->MaxVClockFreqKHz = 350000; |
| 249 | } |
| 250 | |
| 251 | static void nv10GetConfig(struct nvidia_par *par) |
| 252 | { |
| 253 | struct pci_dev *dev; |
| 254 | u32 implementation = par->Chipset & 0x0ff0; |
| 255 | |
| 256 | #ifdef __BIG_ENDIAN |
| 257 | /* turn on big endian register access */ |
| 258 | if (!(NV_RD32(par->PMC, 0x0004) & 0x01000001)) { |
| 259 | NV_WR32(par->PMC, 0x0004, 0x01000001); |
| 260 | mb(); |
| 261 | } |
| 262 | #endif |
| 263 | |
| 264 | dev = pci_find_slot(0, 1); |
| 265 | if ((par->Chipset && 0xffff) == 0x01a0) { |
| 266 | int amt = 0; |
| 267 | |
| 268 | pci_read_config_dword(dev, 0x7c, &amt); |
| 269 | par->RamAmountKBytes = (((amt >> 6) & 31) + 1) * 1024; |
| 270 | } else if ((par->Chipset & 0xffff) == 0x01f0) { |
| 271 | int amt = 0; |
| 272 | |
| 273 | pci_read_config_dword(dev, 0x84, &amt); |
| 274 | par->RamAmountKBytes = (((amt >> 4) & 127) + 1) * 1024; |
| 275 | } else { |
| 276 | par->RamAmountKBytes = |
| 277 | (NV_RD32(par->PFB, 0x020C) & 0xFFF00000) >> 10; |
| 278 | } |
| 279 | |
| 280 | par->CrystalFreqKHz = (NV_RD32(par->PEXTDEV, 0x0000) & (1 << 6)) ? |
| 281 | 14318 : 13500; |
| 282 | |
| 283 | if (par->twoHeads && (implementation != 0x0110)) { |
| 284 | if (NV_RD32(par->PEXTDEV, 0x0000) & (1 << 22)) |
| 285 | par->CrystalFreqKHz = 27000; |
| 286 | } |
| 287 | |
| 288 | par->CursorStart = (par->RamAmountKBytes - 96) * 1024; |
| 289 | par->CURSOR = NULL; /* can't set this here */ |
| 290 | par->MinVClockFreqKHz = 12000; |
| 291 | par->MaxVClockFreqKHz = par->twoStagePLL ? 400000 : 350000; |
| 292 | } |
| 293 | |
| 294 | void NVCommonSetup(struct fb_info *info) |
| 295 | { |
| 296 | struct nvidia_par *par = info->par; |
| 297 | struct fb_var_screeninfo var; |
| 298 | u16 implementation = par->Chipset & 0x0ff0; |
| 299 | u8 *edidA = NULL, *edidB = NULL; |
| 300 | struct fb_monspecs monitorA, monitorB; |
| 301 | struct fb_monspecs *monA = NULL, *monB = NULL; |
| 302 | int mobile = 0; |
| 303 | int tvA = 0; |
| 304 | int tvB = 0; |
| 305 | int FlatPanel = -1; /* really means the CRTC is slaved */ |
| 306 | int Television = 0; |
| 307 | |
| 308 | par->PRAMIN = par->REGS + (0x00710000 / 4); |
| 309 | par->PCRTC0 = par->REGS + (0x00600000 / 4); |
| 310 | par->PRAMDAC0 = par->REGS + (0x00680000 / 4); |
| 311 | par->PFB = par->REGS + (0x00100000 / 4); |
| 312 | par->PFIFO = par->REGS + (0x00002000 / 4); |
| 313 | par->PGRAPH = par->REGS + (0x00400000 / 4); |
| 314 | par->PEXTDEV = par->REGS + (0x00101000 / 4); |
| 315 | par->PTIMER = par->REGS + (0x00009000 / 4); |
| 316 | par->PMC = par->REGS + (0x00000000 / 4); |
| 317 | par->FIFO = par->REGS + (0x00800000 / 4); |
| 318 | |
| 319 | /* 8 bit registers */ |
| 320 | par->PCIO0 = (u8 __iomem *) par->REGS + 0x00601000; |
| 321 | par->PDIO0 = (u8 __iomem *) par->REGS + 0x00681000; |
| 322 | par->PVIO = (u8 __iomem *) par->REGS + 0x000C0000; |
| 323 | |
| 324 | par->twoHeads = (par->Architecture >= NV_ARCH_10) && |
| 325 | (implementation != 0x0100) && |
| 326 | (implementation != 0x0150) && |
| 327 | (implementation != 0x01A0) && (implementation != 0x0200); |
| 328 | |
| 329 | par->fpScaler = (par->FpScale && par->twoHeads && |
| 330 | (implementation != 0x0110)); |
| 331 | |
| 332 | par->twoStagePLL = (implementation == 0x0310) || |
| 333 | (implementation == 0x0340) || (par->Architecture >= NV_ARCH_40); |
| 334 | |
| 335 | par->WaitVSyncPossible = (par->Architecture >= NV_ARCH_10) && |
| 336 | (implementation != 0x0100); |
| 337 | |
| 338 | par->BlendingPossible = ((par->Chipset & 0xffff) != 0x0020); |
| 339 | |
| 340 | /* look for known laptop chips */ |
| 341 | switch (par->Chipset & 0xffff) { |
| 342 | case 0x0112: |
| 343 | case 0x0174: |
| 344 | case 0x0175: |
| 345 | case 0x0176: |
| 346 | case 0x0177: |
| 347 | case 0x0179: |
| 348 | case 0x017C: |
| 349 | case 0x017D: |
| 350 | case 0x0186: |
| 351 | case 0x0187: |
| 352 | case 0x018D: |
| 353 | case 0x0286: |
| 354 | case 0x028C: |
| 355 | case 0x0316: |
| 356 | case 0x0317: |
| 357 | case 0x031A: |
| 358 | case 0x031B: |
| 359 | case 0x031C: |
| 360 | case 0x031D: |
| 361 | case 0x031E: |
| 362 | case 0x031F: |
| 363 | case 0x0324: |
| 364 | case 0x0325: |
| 365 | case 0x0328: |
| 366 | case 0x0329: |
| 367 | case 0x032C: |
| 368 | case 0x032D: |
| 369 | case 0x0347: |
| 370 | case 0x0348: |
| 371 | case 0x0349: |
| 372 | case 0x034B: |
| 373 | case 0x034C: |
| 374 | case 0x0160: |
| 375 | case 0x0166: |
| 376 | case 0x00C8: |
| 377 | case 0x00CC: |
| 378 | case 0x0144: |
| 379 | case 0x0146: |
| 380 | case 0x0147: |
| 381 | case 0x0148: |
| 382 | mobile = 1; |
| 383 | break; |
| 384 | default: |
| 385 | break; |
| 386 | } |
| 387 | |
| 388 | if (par->Architecture == NV_ARCH_04) |
| 389 | nv4GetConfig(par); |
| 390 | else |
| 391 | nv10GetConfig(par); |
| 392 | |
| 393 | NVSelectHeadRegisters(par, 0); |
| 394 | |
| 395 | NVLockUnlock(par, 0); |
| 396 | |
| 397 | par->IOBase = (NVReadMiscOut(par) & 0x01) ? 0x3d0 : 0x3b0; |
| 398 | |
| 399 | par->Television = 0; |
| 400 | |
| 401 | nvidia_create_i2c_busses(par); |
| 402 | if (!par->twoHeads) { |
| 403 | par->CRTCnumber = 0; |
| 404 | nvidia_probe_i2c_connector(par, 1, &edidA); |
| 405 | if (edidA && !fb_parse_edid(edidA, &var)) { |
| 406 | printk("nvidiafb: EDID found from BUS1\n"); |
| 407 | monA = &monitorA; |
| 408 | fb_edid_to_monspecs(edidA, monA); |
| 409 | FlatPanel = (monA->input & FB_DISP_DDI) ? 1 : 0; |
| 410 | |
| 411 | /* NV4 doesn't support FlatPanels */ |
| 412 | if ((par->Chipset & 0x0fff) <= 0x0020) |
| 413 | FlatPanel = 0; |
| 414 | } else { |
| 415 | VGA_WR08(par->PCIO, 0x03D4, 0x28); |
| 416 | if (VGA_RD08(par->PCIO, 0x03D5) & 0x80) { |
| 417 | VGA_WR08(par->PCIO, 0x03D4, 0x33); |
| 418 | if (!(VGA_RD08(par->PCIO, 0x03D5) & 0x01)) |
| 419 | Television = 1; |
| 420 | FlatPanel = 1; |
| 421 | } else { |
| 422 | FlatPanel = 0; |
| 423 | } |
| 424 | printk("nvidiafb: HW is currently programmed for %s\n", |
| 425 | FlatPanel ? (Television ? "TV" : "DFP") : |
| 426 | "CRT"); |
| 427 | } |
| 428 | |
| 429 | if (par->FlatPanel == -1) { |
| 430 | par->FlatPanel = FlatPanel; |
| 431 | par->Television = Television; |
| 432 | } else { |
| 433 | printk("nvidiafb: Forcing display type to %s as " |
| 434 | "specified\n", par->FlatPanel ? "DFP" : "CRT"); |
| 435 | } |
| 436 | } else { |
| 437 | u8 outputAfromCRTC, outputBfromCRTC; |
| 438 | int CRTCnumber = -1; |
| 439 | u8 slaved_on_A, slaved_on_B; |
| 440 | int analog_on_A, analog_on_B; |
| 441 | u32 oldhead; |
| 442 | u8 cr44; |
| 443 | |
| 444 | if (implementation != 0x0110) { |
| 445 | if (NV_RD32(par->PRAMDAC0, 0x0000052C) & 0x100) |
| 446 | outputAfromCRTC = 1; |
| 447 | else |
| 448 | outputAfromCRTC = 0; |
| 449 | if (NV_RD32(par->PRAMDAC0, 0x0000252C) & 0x100) |
| 450 | outputBfromCRTC = 1; |
| 451 | else |
| 452 | outputBfromCRTC = 0; |
| 453 | analog_on_A = NVIsConnected(par, 0); |
| 454 | analog_on_B = NVIsConnected(par, 1); |
| 455 | } else { |
| 456 | outputAfromCRTC = 0; |
| 457 | outputBfromCRTC = 1; |
| 458 | analog_on_A = 0; |
| 459 | analog_on_B = 0; |
| 460 | } |
| 461 | |
| 462 | VGA_WR08(par->PCIO, 0x03D4, 0x44); |
| 463 | cr44 = VGA_RD08(par->PCIO, 0x03D5); |
| 464 | |
| 465 | VGA_WR08(par->PCIO, 0x03D5, 3); |
| 466 | NVSelectHeadRegisters(par, 1); |
| 467 | NVLockUnlock(par, 0); |
| 468 | |
| 469 | VGA_WR08(par->PCIO, 0x03D4, 0x28); |
| 470 | slaved_on_B = VGA_RD08(par->PCIO, 0x03D5) & 0x80; |
| 471 | if (slaved_on_B) { |
| 472 | VGA_WR08(par->PCIO, 0x03D4, 0x33); |
| 473 | tvB = !(VGA_RD08(par->PCIO, 0x03D5) & 0x01); |
| 474 | } |
| 475 | |
| 476 | VGA_WR08(par->PCIO, 0x03D4, 0x44); |
| 477 | VGA_WR08(par->PCIO, 0x03D5, 0); |
| 478 | NVSelectHeadRegisters(par, 0); |
| 479 | NVLockUnlock(par, 0); |
| 480 | |
| 481 | VGA_WR08(par->PCIO, 0x03D4, 0x28); |
| 482 | slaved_on_A = VGA_RD08(par->PCIO, 0x03D5) & 0x80; |
| 483 | if (slaved_on_A) { |
| 484 | VGA_WR08(par->PCIO, 0x03D4, 0x33); |
| 485 | tvA = !(VGA_RD08(par->PCIO, 0x03D5) & 0x01); |
| 486 | } |
| 487 | |
| 488 | oldhead = NV_RD32(par->PCRTC0, 0x00000860); |
| 489 | NV_WR32(par->PCRTC0, 0x00000860, oldhead | 0x00000010); |
| 490 | |
| 491 | nvidia_probe_i2c_connector(par, 1, &edidA); |
| 492 | if (edidA && !fb_parse_edid(edidA, &var)) { |
| 493 | printk("nvidiafb: EDID found from BUS1\n"); |
| 494 | monA = &monitorA; |
| 495 | fb_edid_to_monspecs(edidA, monA); |
| 496 | } |
| 497 | |
| 498 | nvidia_probe_i2c_connector(par, 2, &edidB); |
| 499 | if (edidB && !fb_parse_edid(edidB, &var)) { |
| 500 | printk("nvidiafb: EDID found from BUS2\n"); |
| 501 | monB = &monitorB; |
| 502 | fb_edid_to_monspecs(edidB, monB); |
| 503 | } |
| 504 | |
| 505 | if (slaved_on_A && !tvA) { |
| 506 | CRTCnumber = 0; |
| 507 | FlatPanel = 1; |
| 508 | printk("nvidiafb: CRTC 0 is currently programmed for " |
| 509 | "DFP\n"); |
| 510 | } else if (slaved_on_B && !tvB) { |
| 511 | CRTCnumber = 1; |
| 512 | FlatPanel = 1; |
| 513 | printk("nvidiafb: CRTC 1 is currently programmed " |
| 514 | "for DFP\n"); |
| 515 | } else if (analog_on_A) { |
| 516 | CRTCnumber = outputAfromCRTC; |
| 517 | FlatPanel = 0; |
| 518 | printk("nvidiafb: CRTC %i appears to have a " |
| 519 | "CRT attached\n", CRTCnumber); |
| 520 | } else if (analog_on_B) { |
| 521 | CRTCnumber = outputBfromCRTC; |
| 522 | FlatPanel = 0; |
| 523 | printk("nvidiafb: CRTC %i" |
| 524 | "appears to have a " |
| 525 | "CRT attached\n", CRTCnumber); |
| 526 | } else if (slaved_on_A) { |
| 527 | CRTCnumber = 0; |
| 528 | FlatPanel = 1; |
| 529 | Television = 1; |
| 530 | printk("nvidiafb: CRTC 0 is currently programmed " |
| 531 | "for TV\n"); |
| 532 | } else if (slaved_on_B) { |
| 533 | CRTCnumber = 1; |
| 534 | FlatPanel = 1; |
| 535 | Television = 1; |
| 536 | printk("nvidiafb: CRTC 1 is currently programmed for " |
| 537 | "TV\n"); |
| 538 | } else if (monA) { |
| 539 | FlatPanel = (monA->input & FB_DISP_DDI) ? 1 : 0; |
| 540 | } else if (monB) { |
| 541 | FlatPanel = (monB->input & FB_DISP_DDI) ? 1 : 0; |
| 542 | } |
| 543 | |
| 544 | if (par->FlatPanel == -1) { |
| 545 | if (FlatPanel != -1) { |
| 546 | par->FlatPanel = FlatPanel; |
| 547 | par->Television = Television; |
| 548 | } else { |
| 549 | printk("nvidiafb: Unable to detect display " |
| 550 | "type...\n"); |
| 551 | if (mobile) { |
| 552 | printk("...On a laptop, assuming " |
| 553 | "DFP\n"); |
| 554 | par->FlatPanel = 1; |
| 555 | } else { |
| 556 | printk("...Using default of CRT\n"); |
| 557 | par->FlatPanel = 0; |
| 558 | } |
| 559 | } |
| 560 | } else { |
| 561 | printk("nvidiafb: Forcing display type to %s as " |
| 562 | "specified\n", par->FlatPanel ? "DFP" : "CRT"); |
| 563 | } |
| 564 | |
| 565 | if (par->CRTCnumber == -1) { |
| 566 | if (CRTCnumber != -1) |
| 567 | par->CRTCnumber = CRTCnumber; |
| 568 | else { |
| 569 | printk("nvidiafb: Unable to detect which " |
| 570 | "CRTCNumber...\n"); |
| 571 | if (par->FlatPanel) |
| 572 | par->CRTCnumber = 1; |
| 573 | else |
| 574 | par->CRTCnumber = 0; |
| 575 | printk("...Defaulting to CRTCNumber %i\n", |
| 576 | par->CRTCnumber); |
| 577 | } |
| 578 | } else { |
| 579 | printk("nvidiafb: Forcing CRTCNumber %i as " |
| 580 | "specified\n", par->CRTCnumber); |
| 581 | } |
| 582 | |
| 583 | if (monA) { |
| 584 | if (((monA->input & FB_DISP_DDI) && |
| 585 | par->FlatPanel) || |
| 586 | ((!(monA->input & FB_DISP_DDI)) && |
| 587 | !par->FlatPanel)) { |
| 588 | if (monB) { |
| 589 | fb_destroy_modedb(monB->modedb); |
| 590 | monB = NULL; |
| 591 | } |
| 592 | } else { |
| 593 | fb_destroy_modedb(monA->modedb); |
| 594 | monA = NULL; |
| 595 | } |
| 596 | } |
| 597 | |
| 598 | if (monB) { |
| 599 | if (((monB->input & FB_DISP_DDI) && |
| 600 | !par->FlatPanel) || |
| 601 | ((!(monB->input & FB_DISP_DDI)) && |
| 602 | par->FlatPanel)) { |
| 603 | fb_destroy_modedb(monB->modedb); |
| 604 | monB = NULL; |
| 605 | } else |
| 606 | monA = monB; |
| 607 | } |
| 608 | |
| 609 | if (implementation == 0x0110) |
| 610 | cr44 = par->CRTCnumber * 0x3; |
| 611 | |
| 612 | NV_WR32(par->PCRTC0, 0x00000860, oldhead); |
| 613 | |
| 614 | VGA_WR08(par->PCIO, 0x03D4, 0x44); |
| 615 | VGA_WR08(par->PCIO, 0x03D5, cr44); |
| 616 | NVSelectHeadRegisters(par, par->CRTCnumber); |
| 617 | } |
| 618 | |
| 619 | printk("nvidiafb: Using %s on CRTC %i\n", |
| 620 | par->FlatPanel ? (par->Television ? "TV" : "DFP") : "CRT", |
| 621 | par->CRTCnumber); |
| 622 | |
| 623 | if (par->FlatPanel && !par->Television) { |
| 624 | par->fpWidth = NV_RD32(par->PRAMDAC, 0x0820) + 1; |
| 625 | par->fpHeight = NV_RD32(par->PRAMDAC, 0x0800) + 1; |
| 626 | par->fpSyncs = NV_RD32(par->PRAMDAC, 0x0848) & 0x30000033; |
| 627 | |
| 628 | printk("Panel size is %i x %i\n", par->fpWidth, par->fpHeight); |
| 629 | } |
| 630 | |
| 631 | if (monA) |
| 632 | info->monspecs = *monA; |
| 633 | |
| 634 | kfree(edidA); |
| 635 | kfree(edidB); |
| 636 | } |