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; |
Antonino A. Daplas | ac1ae16 | 2007-07-17 04:05:30 -0700 | [diff] [blame^] | 169 | u32 reg52C, reg608, dac0_reg608 = 0; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 170 | int present; |
| 171 | |
Antonino A. Daplas | ac1ae16 | 2007-07-17 04:05:30 -0700 | [diff] [blame^] | 172 | if (output) { |
| 173 | dac0_reg608 = NV_RD32(PRAMDAC, 0x0608); |
| 174 | PRAMDAC += 0x800; |
| 175 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 176 | |
| 177 | reg52C = NV_RD32(PRAMDAC, 0x052C); |
| 178 | reg608 = NV_RD32(PRAMDAC, 0x0608); |
| 179 | |
| 180 | NV_WR32(PRAMDAC, 0x0608, reg608 & ~0x00010000); |
| 181 | |
| 182 | NV_WR32(PRAMDAC, 0x052C, reg52C & 0x0000FEEE); |
| 183 | msleep(1); |
| 184 | NV_WR32(PRAMDAC, 0x052C, NV_RD32(PRAMDAC, 0x052C) | 1); |
| 185 | |
| 186 | NV_WR32(par->PRAMDAC0, 0x0610, 0x94050140); |
| 187 | NV_WR32(par->PRAMDAC0, 0x0608, NV_RD32(par->PRAMDAC0, 0x0608) | |
| 188 | 0x00001000); |
| 189 | |
| 190 | msleep(1); |
| 191 | |
| 192 | present = (NV_RD32(PRAMDAC, 0x0608) & (1 << 28)) ? 1 : 0; |
| 193 | |
| 194 | if (present) |
Benjamin Herrenschmidt | 85f1503 | 2005-11-07 01:00:30 -0800 | [diff] [blame] | 195 | printk("nvidiafb: CRTC%i analog found\n", output); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 196 | else |
Benjamin Herrenschmidt | 85f1503 | 2005-11-07 01:00:30 -0800 | [diff] [blame] | 197 | printk("nvidiafb: CRTC%i analog not found\n", output); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 198 | |
Antonino A. Daplas | ac1ae16 | 2007-07-17 04:05:30 -0700 | [diff] [blame^] | 199 | if (output) |
| 200 | NV_WR32(par->PRAMDAC0, 0x0608, dac0_reg608); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 201 | |
| 202 | NV_WR32(PRAMDAC, 0x052C, reg52C); |
| 203 | NV_WR32(PRAMDAC, 0x0608, reg608); |
| 204 | |
| 205 | return present; |
| 206 | } |
| 207 | |
| 208 | static void NVSelectHeadRegisters(struct nvidia_par *par, int head) |
| 209 | { |
| 210 | if (head) { |
| 211 | par->PCIO = par->PCIO0 + 0x2000; |
| 212 | par->PCRTC = par->PCRTC0 + 0x800; |
| 213 | par->PRAMDAC = par->PRAMDAC0 + 0x800; |
| 214 | par->PDIO = par->PDIO0 + 0x2000; |
| 215 | } else { |
| 216 | par->PCIO = par->PCIO0; |
| 217 | par->PCRTC = par->PCRTC0; |
| 218 | par->PRAMDAC = par->PRAMDAC0; |
| 219 | par->PDIO = par->PDIO0; |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | static void nv4GetConfig(struct nvidia_par *par) |
| 224 | { |
| 225 | if (NV_RD32(par->PFB, 0x0000) & 0x00000100) { |
| 226 | par->RamAmountKBytes = |
| 227 | ((NV_RD32(par->PFB, 0x0000) >> 12) & 0x0F) * 1024 * 2 + |
| 228 | 1024 * 2; |
| 229 | } else { |
| 230 | switch (NV_RD32(par->PFB, 0x0000) & 0x00000003) { |
| 231 | case 0: |
| 232 | par->RamAmountKBytes = 1024 * 32; |
| 233 | break; |
| 234 | case 1: |
| 235 | par->RamAmountKBytes = 1024 * 4; |
| 236 | break; |
| 237 | case 2: |
| 238 | par->RamAmountKBytes = 1024 * 8; |
| 239 | break; |
| 240 | case 3: |
| 241 | default: |
| 242 | par->RamAmountKBytes = 1024 * 16; |
| 243 | break; |
| 244 | } |
| 245 | } |
| 246 | par->CrystalFreqKHz = (NV_RD32(par->PEXTDEV, 0x0000) & 0x00000040) ? |
| 247 | 14318 : 13500; |
| 248 | par->CURSOR = &par->PRAMIN[0x1E00]; |
| 249 | par->MinVClockFreqKHz = 12000; |
| 250 | par->MaxVClockFreqKHz = 350000; |
| 251 | } |
| 252 | |
| 253 | static void nv10GetConfig(struct nvidia_par *par) |
| 254 | { |
| 255 | struct pci_dev *dev; |
| 256 | u32 implementation = par->Chipset & 0x0ff0; |
| 257 | |
| 258 | #ifdef __BIG_ENDIAN |
| 259 | /* turn on big endian register access */ |
| 260 | if (!(NV_RD32(par->PMC, 0x0004) & 0x01000001)) { |
| 261 | NV_WR32(par->PMC, 0x0004, 0x01000001); |
| 262 | mb(); |
| 263 | } |
| 264 | #endif |
| 265 | |
Alan Cox | d373634 | 2007-05-08 00:39:28 -0700 | [diff] [blame] | 266 | dev = pci_get_bus_and_slot(0, 1); |
Nathan Lynch | d6e89cb | 2006-11-14 02:03:30 -0800 | [diff] [blame] | 267 | if ((par->Chipset & 0xffff) == 0x01a0) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 268 | int amt = 0; |
| 269 | |
| 270 | pci_read_config_dword(dev, 0x7c, &amt); |
| 271 | par->RamAmountKBytes = (((amt >> 6) & 31) + 1) * 1024; |
| 272 | } else if ((par->Chipset & 0xffff) == 0x01f0) { |
| 273 | int amt = 0; |
| 274 | |
| 275 | pci_read_config_dword(dev, 0x84, &amt); |
| 276 | par->RamAmountKBytes = (((amt >> 4) & 127) + 1) * 1024; |
| 277 | } else { |
| 278 | par->RamAmountKBytes = |
| 279 | (NV_RD32(par->PFB, 0x020C) & 0xFFF00000) >> 10; |
| 280 | } |
Alan Cox | d373634 | 2007-05-08 00:39:28 -0700 | [diff] [blame] | 281 | pci_dev_put(dev); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 282 | |
| 283 | par->CrystalFreqKHz = (NV_RD32(par->PEXTDEV, 0x0000) & (1 << 6)) ? |
| 284 | 14318 : 13500; |
| 285 | |
| 286 | if (par->twoHeads && (implementation != 0x0110)) { |
| 287 | if (NV_RD32(par->PEXTDEV, 0x0000) & (1 << 22)) |
| 288 | par->CrystalFreqKHz = 27000; |
| 289 | } |
| 290 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 291 | par->CURSOR = NULL; /* can't set this here */ |
| 292 | par->MinVClockFreqKHz = 12000; |
| 293 | par->MaxVClockFreqKHz = par->twoStagePLL ? 400000 : 350000; |
| 294 | } |
| 295 | |
Antonino A. Daplas | 918799a | 2006-01-09 20:53:40 -0800 | [diff] [blame] | 296 | int NVCommonSetup(struct fb_info *info) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 297 | { |
| 298 | struct nvidia_par *par = info->par; |
Antonino A. Daplas | 918799a | 2006-01-09 20:53:40 -0800 | [diff] [blame] | 299 | struct fb_var_screeninfo *var; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 300 | u16 implementation = par->Chipset & 0x0ff0; |
| 301 | u8 *edidA = NULL, *edidB = NULL; |
Antonino A. Daplas | 918799a | 2006-01-09 20:53:40 -0800 | [diff] [blame] | 302 | struct fb_monspecs *monitorA, *monitorB; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 303 | struct fb_monspecs *monA = NULL, *monB = NULL; |
| 304 | int mobile = 0; |
| 305 | int tvA = 0; |
| 306 | int tvB = 0; |
| 307 | int FlatPanel = -1; /* really means the CRTC is slaved */ |
| 308 | int Television = 0; |
Antonino A. Daplas | 918799a | 2006-01-09 20:53:40 -0800 | [diff] [blame] | 309 | int err = 0; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 310 | |
Antonino A. Daplas | 918799a | 2006-01-09 20:53:40 -0800 | [diff] [blame] | 311 | var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL); |
| 312 | monitorA = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL); |
| 313 | monitorB = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL); |
| 314 | |
| 315 | if (!var || !monitorA || !monitorB) { |
| 316 | err = -ENOMEM; |
| 317 | goto done; |
| 318 | } |
Benjamin Herrenschmidt | 85f1503 | 2005-11-07 01:00:30 -0800 | [diff] [blame] | 319 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 320 | par->PRAMIN = par->REGS + (0x00710000 / 4); |
| 321 | par->PCRTC0 = par->REGS + (0x00600000 / 4); |
| 322 | par->PRAMDAC0 = par->REGS + (0x00680000 / 4); |
| 323 | par->PFB = par->REGS + (0x00100000 / 4); |
| 324 | par->PFIFO = par->REGS + (0x00002000 / 4); |
| 325 | par->PGRAPH = par->REGS + (0x00400000 / 4); |
| 326 | par->PEXTDEV = par->REGS + (0x00101000 / 4); |
| 327 | par->PTIMER = par->REGS + (0x00009000 / 4); |
| 328 | par->PMC = par->REGS + (0x00000000 / 4); |
| 329 | par->FIFO = par->REGS + (0x00800000 / 4); |
| 330 | |
| 331 | /* 8 bit registers */ |
| 332 | par->PCIO0 = (u8 __iomem *) par->REGS + 0x00601000; |
| 333 | par->PDIO0 = (u8 __iomem *) par->REGS + 0x00681000; |
| 334 | par->PVIO = (u8 __iomem *) par->REGS + 0x000C0000; |
| 335 | |
| 336 | par->twoHeads = (par->Architecture >= NV_ARCH_10) && |
| 337 | (implementation != 0x0100) && |
| 338 | (implementation != 0x0150) && |
| 339 | (implementation != 0x01A0) && (implementation != 0x0200); |
| 340 | |
| 341 | par->fpScaler = (par->FpScale && par->twoHeads && |
| 342 | (implementation != 0x0110)); |
| 343 | |
| 344 | par->twoStagePLL = (implementation == 0x0310) || |
| 345 | (implementation == 0x0340) || (par->Architecture >= NV_ARCH_40); |
| 346 | |
| 347 | par->WaitVSyncPossible = (par->Architecture >= NV_ARCH_10) && |
| 348 | (implementation != 0x0100); |
| 349 | |
| 350 | par->BlendingPossible = ((par->Chipset & 0xffff) != 0x0020); |
| 351 | |
| 352 | /* look for known laptop chips */ |
| 353 | switch (par->Chipset & 0xffff) { |
| 354 | case 0x0112: |
| 355 | case 0x0174: |
| 356 | case 0x0175: |
| 357 | case 0x0176: |
| 358 | case 0x0177: |
| 359 | case 0x0179: |
| 360 | case 0x017C: |
| 361 | case 0x017D: |
| 362 | case 0x0186: |
| 363 | case 0x0187: |
| 364 | case 0x018D: |
Wink Saville | e40c675 | 2006-11-10 12:27:52 -0800 | [diff] [blame] | 365 | case 0x0228: |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 366 | case 0x0286: |
| 367 | case 0x028C: |
| 368 | case 0x0316: |
| 369 | case 0x0317: |
| 370 | case 0x031A: |
| 371 | case 0x031B: |
| 372 | case 0x031C: |
| 373 | case 0x031D: |
| 374 | case 0x031E: |
| 375 | case 0x031F: |
| 376 | case 0x0324: |
| 377 | case 0x0325: |
| 378 | case 0x0328: |
| 379 | case 0x0329: |
| 380 | case 0x032C: |
| 381 | case 0x032D: |
| 382 | case 0x0347: |
| 383 | case 0x0348: |
| 384 | case 0x0349: |
| 385 | case 0x034B: |
| 386 | case 0x034C: |
| 387 | case 0x0160: |
| 388 | case 0x0166: |
Wink Saville | e40c675 | 2006-11-10 12:27:52 -0800 | [diff] [blame] | 389 | case 0x0169: |
| 390 | case 0x016B: |
| 391 | case 0x016C: |
| 392 | case 0x016D: |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 393 | case 0x00C8: |
| 394 | case 0x00CC: |
| 395 | case 0x0144: |
| 396 | case 0x0146: |
| 397 | case 0x0147: |
| 398 | case 0x0148: |
Benjamin Herrenschmidt | 0137ecf | 2006-01-09 20:51:27 -0800 | [diff] [blame] | 399 | case 0x0098: |
| 400 | case 0x0099: |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 401 | mobile = 1; |
| 402 | break; |
| 403 | default: |
| 404 | break; |
| 405 | } |
| 406 | |
| 407 | if (par->Architecture == NV_ARCH_04) |
| 408 | nv4GetConfig(par); |
| 409 | else |
| 410 | nv10GetConfig(par); |
| 411 | |
| 412 | NVSelectHeadRegisters(par, 0); |
| 413 | |
| 414 | NVLockUnlock(par, 0); |
| 415 | |
| 416 | par->IOBase = (NVReadMiscOut(par) & 0x01) ? 0x3d0 : 0x3b0; |
| 417 | |
| 418 | par->Television = 0; |
| 419 | |
| 420 | nvidia_create_i2c_busses(par); |
| 421 | if (!par->twoHeads) { |
| 422 | par->CRTCnumber = 0; |
Benjamin Herrenschmidt | 85f1503 | 2005-11-07 01:00:30 -0800 | [diff] [blame] | 423 | if (nvidia_probe_i2c_connector(info, 1, &edidA)) |
| 424 | nvidia_probe_of_connector(info, 1, &edidA); |
Antonino A. Daplas | 918799a | 2006-01-09 20:53:40 -0800 | [diff] [blame] | 425 | if (edidA && !fb_parse_edid(edidA, var)) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 426 | printk("nvidiafb: EDID found from BUS1\n"); |
Antonino A. Daplas | 918799a | 2006-01-09 20:53:40 -0800 | [diff] [blame] | 427 | monA = monitorA; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 428 | fb_edid_to_monspecs(edidA, monA); |
| 429 | FlatPanel = (monA->input & FB_DISP_DDI) ? 1 : 0; |
| 430 | |
| 431 | /* NV4 doesn't support FlatPanels */ |
| 432 | if ((par->Chipset & 0x0fff) <= 0x0020) |
| 433 | FlatPanel = 0; |
| 434 | } else { |
| 435 | VGA_WR08(par->PCIO, 0x03D4, 0x28); |
| 436 | if (VGA_RD08(par->PCIO, 0x03D5) & 0x80) { |
| 437 | VGA_WR08(par->PCIO, 0x03D4, 0x33); |
| 438 | if (!(VGA_RD08(par->PCIO, 0x03D5) & 0x01)) |
| 439 | Television = 1; |
| 440 | FlatPanel = 1; |
| 441 | } else { |
| 442 | FlatPanel = 0; |
| 443 | } |
| 444 | printk("nvidiafb: HW is currently programmed for %s\n", |
| 445 | FlatPanel ? (Television ? "TV" : "DFP") : |
| 446 | "CRT"); |
| 447 | } |
| 448 | |
| 449 | if (par->FlatPanel == -1) { |
| 450 | par->FlatPanel = FlatPanel; |
| 451 | par->Television = Television; |
| 452 | } else { |
| 453 | printk("nvidiafb: Forcing display type to %s as " |
| 454 | "specified\n", par->FlatPanel ? "DFP" : "CRT"); |
| 455 | } |
| 456 | } else { |
| 457 | u8 outputAfromCRTC, outputBfromCRTC; |
| 458 | int CRTCnumber = -1; |
| 459 | u8 slaved_on_A, slaved_on_B; |
| 460 | int analog_on_A, analog_on_B; |
| 461 | u32 oldhead; |
| 462 | u8 cr44; |
| 463 | |
| 464 | if (implementation != 0x0110) { |
| 465 | if (NV_RD32(par->PRAMDAC0, 0x0000052C) & 0x100) |
| 466 | outputAfromCRTC = 1; |
| 467 | else |
| 468 | outputAfromCRTC = 0; |
| 469 | if (NV_RD32(par->PRAMDAC0, 0x0000252C) & 0x100) |
| 470 | outputBfromCRTC = 1; |
| 471 | else |
| 472 | outputBfromCRTC = 0; |
| 473 | analog_on_A = NVIsConnected(par, 0); |
| 474 | analog_on_B = NVIsConnected(par, 1); |
| 475 | } else { |
| 476 | outputAfromCRTC = 0; |
| 477 | outputBfromCRTC = 1; |
| 478 | analog_on_A = 0; |
| 479 | analog_on_B = 0; |
| 480 | } |
| 481 | |
| 482 | VGA_WR08(par->PCIO, 0x03D4, 0x44); |
| 483 | cr44 = VGA_RD08(par->PCIO, 0x03D5); |
| 484 | |
| 485 | VGA_WR08(par->PCIO, 0x03D5, 3); |
| 486 | NVSelectHeadRegisters(par, 1); |
| 487 | NVLockUnlock(par, 0); |
| 488 | |
| 489 | VGA_WR08(par->PCIO, 0x03D4, 0x28); |
| 490 | slaved_on_B = VGA_RD08(par->PCIO, 0x03D5) & 0x80; |
| 491 | if (slaved_on_B) { |
| 492 | VGA_WR08(par->PCIO, 0x03D4, 0x33); |
| 493 | tvB = !(VGA_RD08(par->PCIO, 0x03D5) & 0x01); |
| 494 | } |
| 495 | |
| 496 | VGA_WR08(par->PCIO, 0x03D4, 0x44); |
| 497 | VGA_WR08(par->PCIO, 0x03D5, 0); |
| 498 | NVSelectHeadRegisters(par, 0); |
| 499 | NVLockUnlock(par, 0); |
| 500 | |
| 501 | VGA_WR08(par->PCIO, 0x03D4, 0x28); |
| 502 | slaved_on_A = VGA_RD08(par->PCIO, 0x03D5) & 0x80; |
| 503 | if (slaved_on_A) { |
| 504 | VGA_WR08(par->PCIO, 0x03D4, 0x33); |
| 505 | tvA = !(VGA_RD08(par->PCIO, 0x03D5) & 0x01); |
| 506 | } |
| 507 | |
| 508 | oldhead = NV_RD32(par->PCRTC0, 0x00000860); |
| 509 | NV_WR32(par->PCRTC0, 0x00000860, oldhead | 0x00000010); |
| 510 | |
Benjamin Herrenschmidt | 85f1503 | 2005-11-07 01:00:30 -0800 | [diff] [blame] | 511 | if (nvidia_probe_i2c_connector(info, 1, &edidA)) |
| 512 | nvidia_probe_of_connector(info, 1, &edidA); |
Antonino A. Daplas | 918799a | 2006-01-09 20:53:40 -0800 | [diff] [blame] | 513 | if (edidA && !fb_parse_edid(edidA, var)) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 514 | printk("nvidiafb: EDID found from BUS1\n"); |
Antonino A. Daplas | 918799a | 2006-01-09 20:53:40 -0800 | [diff] [blame] | 515 | monA = monitorA; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 516 | fb_edid_to_monspecs(edidA, monA); |
| 517 | } |
| 518 | |
Benjamin Herrenschmidt | 85f1503 | 2005-11-07 01:00:30 -0800 | [diff] [blame] | 519 | if (nvidia_probe_i2c_connector(info, 2, &edidB)) |
| 520 | nvidia_probe_of_connector(info, 2, &edidB); |
Antonino A. Daplas | 918799a | 2006-01-09 20:53:40 -0800 | [diff] [blame] | 521 | if (edidB && !fb_parse_edid(edidB, var)) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 522 | printk("nvidiafb: EDID found from BUS2\n"); |
Antonino A. Daplas | 918799a | 2006-01-09 20:53:40 -0800 | [diff] [blame] | 523 | monB = monitorB; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 524 | fb_edid_to_monspecs(edidB, monB); |
| 525 | } |
| 526 | |
| 527 | if (slaved_on_A && !tvA) { |
| 528 | CRTCnumber = 0; |
| 529 | FlatPanel = 1; |
| 530 | printk("nvidiafb: CRTC 0 is currently programmed for " |
| 531 | "DFP\n"); |
| 532 | } else if (slaved_on_B && !tvB) { |
| 533 | CRTCnumber = 1; |
| 534 | FlatPanel = 1; |
| 535 | printk("nvidiafb: CRTC 1 is currently programmed " |
| 536 | "for DFP\n"); |
| 537 | } else if (analog_on_A) { |
| 538 | CRTCnumber = outputAfromCRTC; |
| 539 | FlatPanel = 0; |
| 540 | printk("nvidiafb: CRTC %i appears to have a " |
| 541 | "CRT attached\n", CRTCnumber); |
| 542 | } else if (analog_on_B) { |
| 543 | CRTCnumber = outputBfromCRTC; |
| 544 | FlatPanel = 0; |
| 545 | printk("nvidiafb: CRTC %i" |
| 546 | "appears to have a " |
| 547 | "CRT attached\n", CRTCnumber); |
| 548 | } else if (slaved_on_A) { |
| 549 | CRTCnumber = 0; |
| 550 | FlatPanel = 1; |
| 551 | Television = 1; |
| 552 | printk("nvidiafb: CRTC 0 is currently programmed " |
| 553 | "for TV\n"); |
| 554 | } else if (slaved_on_B) { |
| 555 | CRTCnumber = 1; |
| 556 | FlatPanel = 1; |
| 557 | Television = 1; |
| 558 | printk("nvidiafb: CRTC 1 is currently programmed for " |
| 559 | "TV\n"); |
| 560 | } else if (monA) { |
| 561 | FlatPanel = (monA->input & FB_DISP_DDI) ? 1 : 0; |
| 562 | } else if (monB) { |
| 563 | FlatPanel = (monB->input & FB_DISP_DDI) ? 1 : 0; |
| 564 | } |
| 565 | |
| 566 | if (par->FlatPanel == -1) { |
| 567 | if (FlatPanel != -1) { |
| 568 | par->FlatPanel = FlatPanel; |
| 569 | par->Television = Television; |
| 570 | } else { |
| 571 | printk("nvidiafb: Unable to detect display " |
| 572 | "type...\n"); |
| 573 | if (mobile) { |
| 574 | printk("...On a laptop, assuming " |
| 575 | "DFP\n"); |
| 576 | par->FlatPanel = 1; |
| 577 | } else { |
| 578 | printk("...Using default of CRT\n"); |
| 579 | par->FlatPanel = 0; |
| 580 | } |
| 581 | } |
| 582 | } else { |
| 583 | printk("nvidiafb: Forcing display type to %s as " |
| 584 | "specified\n", par->FlatPanel ? "DFP" : "CRT"); |
| 585 | } |
| 586 | |
| 587 | if (par->CRTCnumber == -1) { |
| 588 | if (CRTCnumber != -1) |
| 589 | par->CRTCnumber = CRTCnumber; |
| 590 | else { |
| 591 | printk("nvidiafb: Unable to detect which " |
| 592 | "CRTCNumber...\n"); |
| 593 | if (par->FlatPanel) |
| 594 | par->CRTCnumber = 1; |
| 595 | else |
| 596 | par->CRTCnumber = 0; |
| 597 | printk("...Defaulting to CRTCNumber %i\n", |
| 598 | par->CRTCnumber); |
| 599 | } |
| 600 | } else { |
| 601 | printk("nvidiafb: Forcing CRTCNumber %i as " |
| 602 | "specified\n", par->CRTCnumber); |
| 603 | } |
| 604 | |
| 605 | if (monA) { |
| 606 | if (((monA->input & FB_DISP_DDI) && |
| 607 | par->FlatPanel) || |
| 608 | ((!(monA->input & FB_DISP_DDI)) && |
| 609 | !par->FlatPanel)) { |
| 610 | if (monB) { |
| 611 | fb_destroy_modedb(monB->modedb); |
| 612 | monB = NULL; |
| 613 | } |
| 614 | } else { |
| 615 | fb_destroy_modedb(monA->modedb); |
| 616 | monA = NULL; |
| 617 | } |
| 618 | } |
| 619 | |
| 620 | if (monB) { |
| 621 | if (((monB->input & FB_DISP_DDI) && |
| 622 | !par->FlatPanel) || |
| 623 | ((!(monB->input & FB_DISP_DDI)) && |
| 624 | par->FlatPanel)) { |
| 625 | fb_destroy_modedb(monB->modedb); |
| 626 | monB = NULL; |
| 627 | } else |
| 628 | monA = monB; |
| 629 | } |
| 630 | |
| 631 | if (implementation == 0x0110) |
| 632 | cr44 = par->CRTCnumber * 0x3; |
| 633 | |
| 634 | NV_WR32(par->PCRTC0, 0x00000860, oldhead); |
| 635 | |
| 636 | VGA_WR08(par->PCIO, 0x03D4, 0x44); |
| 637 | VGA_WR08(par->PCIO, 0x03D5, cr44); |
| 638 | NVSelectHeadRegisters(par, par->CRTCnumber); |
| 639 | } |
| 640 | |
| 641 | printk("nvidiafb: Using %s on CRTC %i\n", |
| 642 | par->FlatPanel ? (par->Television ? "TV" : "DFP") : "CRT", |
| 643 | par->CRTCnumber); |
| 644 | |
| 645 | if (par->FlatPanel && !par->Television) { |
| 646 | par->fpWidth = NV_RD32(par->PRAMDAC, 0x0820) + 1; |
| 647 | par->fpHeight = NV_RD32(par->PRAMDAC, 0x0800) + 1; |
| 648 | par->fpSyncs = NV_RD32(par->PRAMDAC, 0x0848) & 0x30000033; |
| 649 | |
Wink Saville | e40c675 | 2006-11-10 12:27:52 -0800 | [diff] [blame] | 650 | printk("nvidiafb: Panel size is %i x %i\n", par->fpWidth, par->fpHeight); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 651 | } |
| 652 | |
| 653 | if (monA) |
| 654 | info->monspecs = *monA; |
| 655 | |
Wink Saville | e40c675 | 2006-11-10 12:27:52 -0800 | [diff] [blame] | 656 | if (!par->FlatPanel || !par->twoHeads) |
| 657 | par->FPDither = 0; |
| 658 | |
| 659 | par->LVDS = 0; |
| 660 | if (par->FlatPanel && par->twoHeads) { |
| 661 | NV_WR32(par->PRAMDAC0, 0x08B0, 0x00010004); |
Antonino A. Daplas | 6cf059e | 2007-05-08 00:38:41 -0700 | [diff] [blame] | 662 | if (NV_RD32(par->PRAMDAC0, 0x08b4) & 1) |
Wink Saville | e40c675 | 2006-11-10 12:27:52 -0800 | [diff] [blame] | 663 | par->LVDS = 1; |
| 664 | printk("nvidiafb: Panel is %s\n", par->LVDS ? "LVDS" : "TMDS"); |
| 665 | } |
| 666 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 667 | kfree(edidA); |
| 668 | kfree(edidB); |
Antonino A. Daplas | 918799a | 2006-01-09 20:53:40 -0800 | [diff] [blame] | 669 | done: |
| 670 | kfree(var); |
| 671 | kfree(monitorA); |
| 672 | kfree(monitorB); |
| 673 | return err; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 674 | } |