Commit | Line | Data |
---|---|---|
1da177e4 LT |
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; | |
ac1ae162 | 169 | u32 reg52C, reg608, dac0_reg608 = 0; |
1da177e4 LT |
170 | int present; |
171 | ||
ac1ae162 AD |
172 | if (output) { |
173 | dac0_reg608 = NV_RD32(PRAMDAC, 0x0608); | |
174 | PRAMDAC += 0x800; | |
175 | } | |
1da177e4 LT |
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) | |
85f1503a | 195 | printk("nvidiafb: CRTC%i analog found\n", output); |
1da177e4 | 196 | else |
85f1503a | 197 | printk("nvidiafb: CRTC%i analog not found\n", output); |
1da177e4 | 198 | |
ac1ae162 AD |
199 | if (output) |
200 | NV_WR32(par->PRAMDAC0, 0x0608, dac0_reg608); | |
1da177e4 LT |
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 | ||
d3736340 | 266 | dev = pci_get_bus_and_slot(0, 1); |
d6e89cb6 | 267 | if ((par->Chipset & 0xffff) == 0x01a0) { |
1da177e4 LT |
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 | } | |
d3736340 | 281 | pci_dev_put(dev); |
1da177e4 LT |
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 | ||
1da177e4 LT |
291 | par->CURSOR = NULL; /* can't set this here */ |
292 | par->MinVClockFreqKHz = 12000; | |
293 | par->MaxVClockFreqKHz = par->twoStagePLL ? 400000 : 350000; | |
294 | } | |
295 | ||
918799ab | 296 | int NVCommonSetup(struct fb_info *info) |
1da177e4 LT |
297 | { |
298 | struct nvidia_par *par = info->par; | |
918799ab | 299 | struct fb_var_screeninfo *var; |
1da177e4 LT |
300 | u16 implementation = par->Chipset & 0x0ff0; |
301 | u8 *edidA = NULL, *edidB = NULL; | |
918799ab | 302 | struct fb_monspecs *monitorA, *monitorB; |
1da177e4 LT |
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; | |
918799ab | 309 | int err = 0; |
1da177e4 | 310 | |
918799ab AD |
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 | } | |
85f1503a | 319 | |
1da177e4 LT |
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: | |
e40c6759 | 365 | case 0x0228: |
1da177e4 LT |
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: | |
e40c6759 WS |
389 | case 0x0169: |
390 | case 0x016B: | |
391 | case 0x016C: | |
392 | case 0x016D: | |
1da177e4 LT |
393 | case 0x00C8: |
394 | case 0x00CC: | |
395 | case 0x0144: | |
396 | case 0x0146: | |
397 | case 0x0147: | |
398 | case 0x0148: | |
0137ecfd BH |
399 | case 0x0098: |
400 | case 0x0099: | |
1da177e4 LT |
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; | |
85f1503a BH |
423 | if (nvidia_probe_i2c_connector(info, 1, &edidA)) |
424 | nvidia_probe_of_connector(info, 1, &edidA); | |
918799ab | 425 | if (edidA && !fb_parse_edid(edidA, var)) { |
1da177e4 | 426 | printk("nvidiafb: EDID found from BUS1\n"); |
918799ab | 427 | monA = monitorA; |
1da177e4 LT |
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 | ||
85f1503a BH |
511 | if (nvidia_probe_i2c_connector(info, 1, &edidA)) |
512 | nvidia_probe_of_connector(info, 1, &edidA); | |
918799ab | 513 | if (edidA && !fb_parse_edid(edidA, var)) { |
1da177e4 | 514 | printk("nvidiafb: EDID found from BUS1\n"); |
918799ab | 515 | monA = monitorA; |
1da177e4 LT |
516 | fb_edid_to_monspecs(edidA, monA); |
517 | } | |
518 | ||
85f1503a BH |
519 | if (nvidia_probe_i2c_connector(info, 2, &edidB)) |
520 | nvidia_probe_of_connector(info, 2, &edidB); | |
918799ab | 521 | if (edidB && !fb_parse_edid(edidB, var)) { |
1da177e4 | 522 | printk("nvidiafb: EDID found from BUS2\n"); |
918799ab | 523 | monB = monitorB; |
1da177e4 LT |
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 | ||
e40c6759 | 650 | printk("nvidiafb: Panel size is %i x %i\n", par->fpWidth, par->fpHeight); |
1da177e4 LT |
651 | } |
652 | ||
653 | if (monA) | |
654 | info->monspecs = *monA; | |
655 | ||
e40c6759 WS |
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); | |
6cf059e1 | 662 | if (NV_RD32(par->PRAMDAC0, 0x08b4) & 1) |
e40c6759 WS |
663 | par->LVDS = 1; |
664 | printk("nvidiafb: Panel is %s\n", par->LVDS ? "LVDS" : "TMDS"); | |
665 | } | |
666 | ||
1da177e4 LT |
667 | kfree(edidA); |
668 | kfree(edidB); | |
918799ab AD |
669 | done: |
670 | kfree(var); | |
671 | kfree(monitorA); | |
672 | kfree(monitorB); | |
673 | return err; | |
1da177e4 | 674 | } |