diff options
Diffstat (limited to 'drivers/video/fbdev/nvidia/nv_setup.c')
-rw-r--r-- | drivers/video/fbdev/nvidia/nv_setup.c | 675 |
1 files changed, 675 insertions, 0 deletions
diff --git a/drivers/video/fbdev/nvidia/nv_setup.c b/drivers/video/fbdev/nvidia/nv_setup.c new file mode 100644 index 000000000000..2f2e162134fa --- /dev/null +++ b/drivers/video/fbdev/nvidia/nv_setup.c | |||
@@ -0,0 +1,675 @@ | |||
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 <linux/slab.h> | ||
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; | ||
170 | u32 reg52C, reg608, dac0_reg608 = 0; | ||
171 | int present; | ||
172 | |||
173 | if (output) { | ||
174 | dac0_reg608 = NV_RD32(PRAMDAC, 0x0608); | ||
175 | PRAMDAC += 0x800; | ||
176 | } | ||
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) | ||
196 | printk("nvidiafb: CRTC%i analog found\n", output); | ||
197 | else | ||
198 | printk("nvidiafb: CRTC%i analog not found\n", output); | ||
199 | |||
200 | if (output) | ||
201 | NV_WR32(par->PRAMDAC0, 0x0608, dac0_reg608); | ||
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 | |||
267 | dev = pci_get_bus_and_slot(0, 1); | ||
268 | if ((par->Chipset & 0xffff) == 0x01a0) { | ||
269 | u32 amt; | ||
270 | |||
271 | pci_read_config_dword(dev, 0x7c, &amt); | ||
272 | par->RamAmountKBytes = (((amt >> 6) & 31) + 1) * 1024; | ||
273 | } else if ((par->Chipset & 0xffff) == 0x01f0) { | ||
274 | u32 amt; | ||
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 | } | ||
282 | pci_dev_put(dev); | ||
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 | |||
292 | par->CURSOR = NULL; /* can't set this here */ | ||
293 | par->MinVClockFreqKHz = 12000; | ||
294 | par->MaxVClockFreqKHz = par->twoStagePLL ? 400000 : 350000; | ||
295 | } | ||
296 | |||
297 | int NVCommonSetup(struct fb_info *info) | ||
298 | { | ||
299 | struct nvidia_par *par = info->par; | ||
300 | struct fb_var_screeninfo *var; | ||
301 | u16 implementation = par->Chipset & 0x0ff0; | ||
302 | u8 *edidA = NULL, *edidB = NULL; | ||
303 | struct fb_monspecs *monitorA, *monitorB; | ||
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; | ||
310 | int err = 0; | ||
311 | |||
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 | } | ||
320 | |||
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: | ||
366 | case 0x01D7: | ||
367 | case 0x0228: | ||
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: | ||
391 | case 0x0169: | ||
392 | case 0x016B: | ||
393 | case 0x016C: | ||
394 | case 0x016D: | ||
395 | case 0x00C8: | ||
396 | case 0x00CC: | ||
397 | case 0x0144: | ||
398 | case 0x0146: | ||
399 | case 0x0147: | ||
400 | case 0x0148: | ||
401 | case 0x0098: | ||
402 | case 0x0099: | ||
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; | ||
425 | if (nvidia_probe_i2c_connector(info, 1, &edidA)) | ||
426 | nvidia_probe_of_connector(info, 1, &edidA); | ||
427 | if (edidA && !fb_parse_edid(edidA, var)) { | ||
428 | printk("nvidiafb: EDID found from BUS1\n"); | ||
429 | monA = monitorA; | ||
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 | |||
513 | if (nvidia_probe_i2c_connector(info, 1, &edidA)) | ||
514 | nvidia_probe_of_connector(info, 1, &edidA); | ||
515 | if (edidA && !fb_parse_edid(edidA, var)) { | ||
516 | printk("nvidiafb: EDID found from BUS1\n"); | ||
517 | monA = monitorA; | ||
518 | fb_edid_to_monspecs(edidA, monA); | ||
519 | } | ||
520 | |||
521 | if (nvidia_probe_i2c_connector(info, 2, &edidB)) | ||
522 | nvidia_probe_of_connector(info, 2, &edidB); | ||
523 | if (edidB && !fb_parse_edid(edidB, var)) { | ||
524 | printk("nvidiafb: EDID found from BUS2\n"); | ||
525 | monB = monitorB; | ||
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; | ||
547 | printk("nvidiafb: CRTC %i appears to have a " | ||
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 | |||
651 | printk("nvidiafb: Panel size is %i x %i\n", par->fpWidth, par->fpHeight); | ||
652 | } | ||
653 | |||
654 | if (monA) | ||
655 | info->monspecs = *monA; | ||
656 | |||
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); | ||
663 | if (NV_RD32(par->PRAMDAC0, 0x08b4) & 1) | ||
664 | par->LVDS = 1; | ||
665 | printk("nvidiafb: Panel is %s\n", par->LVDS ? "LVDS" : "TMDS"); | ||
666 | } | ||
667 | |||
668 | kfree(edidA); | ||
669 | kfree(edidB); | ||
670 | done: | ||
671 | kfree(var); | ||
672 | kfree(monitorA); | ||
673 | kfree(monitorB); | ||
674 | return err; | ||
675 | } | ||