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