nv_setup.c revision bd304fc0
1/*
2 * Copyright (c) 2003 NVIDIA, Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include "nv_include.h"
29
30/*
31 * Override VGA I/O routines.
32 */
33static void NVWriteCrtc(vgaHWPtr pVga, CARD8 index, CARD8 value)
34{
35    NVPtr pNv = (NVPtr)pVga->MMIOBase;
36    VGA_WR08(pNv->PCIO, pVga->IOBase + VGA_CRTC_INDEX_OFFSET, index);
37    VGA_WR08(pNv->PCIO, pVga->IOBase + VGA_CRTC_DATA_OFFSET,  value);
38}
39static CARD8 NVReadCrtc(vgaHWPtr pVga, CARD8 index)
40{
41    NVPtr pNv = (NVPtr)pVga->MMIOBase;
42    VGA_WR08(pNv->PCIO, pVga->IOBase + VGA_CRTC_INDEX_OFFSET, index);
43    return (VGA_RD08(pNv->PCIO, pVga->IOBase + VGA_CRTC_DATA_OFFSET));
44}
45static void NVWriteGr(vgaHWPtr pVga, CARD8 index, CARD8 value)
46{
47    NVPtr pNv = (NVPtr)pVga->MMIOBase;
48    VGA_WR08(pNv->PVIO, VGA_GRAPH_INDEX, index);
49    VGA_WR08(pNv->PVIO, VGA_GRAPH_DATA,  value);
50}
51static CARD8 NVReadGr(vgaHWPtr pVga, CARD8 index)
52{
53    NVPtr pNv = (NVPtr)pVga->MMIOBase;
54    VGA_WR08(pNv->PVIO, VGA_GRAPH_INDEX, index);
55    return (VGA_RD08(pNv->PVIO, VGA_GRAPH_DATA));
56}
57static void NVWriteSeq(vgaHWPtr pVga, CARD8 index, CARD8 value)
58{
59    NVPtr pNv = (NVPtr)pVga->MMIOBase;
60    VGA_WR08(pNv->PVIO, VGA_SEQ_INDEX, index);
61    VGA_WR08(pNv->PVIO, VGA_SEQ_DATA,  value);
62}
63static CARD8 NVReadSeq(vgaHWPtr pVga, CARD8 index)
64{
65    NVPtr pNv = (NVPtr)pVga->MMIOBase;
66    VGA_WR08(pNv->PVIO, VGA_SEQ_INDEX, index);
67    return (VGA_RD08(pNv->PVIO, VGA_SEQ_DATA));
68}
69static void NVWriteAttr(vgaHWPtr pVga, CARD8 index, CARD8 value)
70{
71    NVPtr pNv = (NVPtr)pVga->MMIOBase;
72    volatile CARD8 tmp;
73
74    tmp = VGA_RD08(pNv->PCIO, pVga->IOBase + VGA_IN_STAT_1_OFFSET);
75    if (pVga->paletteEnabled)
76        index &= ~0x20;
77    else
78        index |= 0x20;
79    VGA_WR08(pNv->PCIO, VGA_ATTR_INDEX,  index);
80    VGA_WR08(pNv->PCIO, VGA_ATTR_DATA_W, value);
81}
82static CARD8 NVReadAttr(vgaHWPtr pVga, CARD8 index)
83{
84    NVPtr pNv = (NVPtr)pVga->MMIOBase;
85    volatile CARD8 tmp;
86
87    tmp = VGA_RD08(pNv->PCIO, pVga->IOBase + VGA_IN_STAT_1_OFFSET);
88    if (pVga->paletteEnabled)
89        index &= ~0x20;
90    else
91        index |= 0x20;
92    VGA_WR08(pNv->PCIO, VGA_ATTR_INDEX, index);
93    return (VGA_RD08(pNv->PCIO, VGA_ATTR_DATA_R));
94}
95static void NVWriteMiscOut(vgaHWPtr pVga, CARD8 value)
96{
97    NVPtr pNv = (NVPtr)pVga->MMIOBase;
98    VGA_WR08(pNv->PVIO, VGA_MISC_OUT_W, value);
99}
100static CARD8 NVReadMiscOut(vgaHWPtr pVga)
101{
102    NVPtr pNv = (NVPtr)pVga->MMIOBase;
103    return (VGA_RD08(pNv->PVIO, VGA_MISC_OUT_R));
104}
105static void NVEnablePalette(vgaHWPtr pVga)
106{
107    NVPtr pNv = (NVPtr)pVga->MMIOBase;
108    volatile CARD8 tmp;
109
110    tmp = VGA_RD08(pNv->PCIO, pVga->IOBase + VGA_IN_STAT_1_OFFSET);
111    VGA_WR08(pNv->PCIO, VGA_ATTR_INDEX, 0x00);
112    pVga->paletteEnabled = TRUE;
113}
114static void NVDisablePalette(vgaHWPtr pVga)
115{
116    NVPtr pNv = (NVPtr)pVga->MMIOBase;
117    volatile CARD8 tmp;
118
119    tmp = VGA_RD08(pNv->PCIO, pVga->IOBase + VGA_IN_STAT_1_OFFSET);
120    VGA_WR08(pNv->PCIO, VGA_ATTR_INDEX, 0x20);
121    pVga->paletteEnabled = FALSE;
122}
123static void NVWriteDacMask(vgaHWPtr pVga, CARD8 value)
124{
125    NVPtr pNv = (NVPtr)pVga->MMIOBase;
126    VGA_WR08(pNv->PDIO, VGA_DAC_MASK, value);
127}
128static CARD8 NVReadDacMask(vgaHWPtr pVga)
129{
130    NVPtr pNv = (NVPtr)pVga->MMIOBase;
131    return (VGA_RD08(pNv->PDIO, VGA_DAC_MASK));
132}
133static void NVWriteDacReadAddr(vgaHWPtr pVga, CARD8 value)
134{
135    NVPtr pNv = (NVPtr)pVga->MMIOBase;
136    VGA_WR08(pNv->PDIO, VGA_DAC_READ_ADDR, value);
137}
138static void NVWriteDacWriteAddr(vgaHWPtr pVga, CARD8 value)
139{
140    NVPtr pNv = (NVPtr)pVga->MMIOBase;
141    VGA_WR08(pNv->PDIO, VGA_DAC_WRITE_ADDR, value);
142}
143static void NVWriteDacData(vgaHWPtr pVga, CARD8 value)
144{
145    NVPtr pNv = (NVPtr)pVga->MMIOBase;
146    VGA_WR08(pNv->PDIO, VGA_DAC_DATA, value);
147}
148static CARD8 NVReadDacData(vgaHWPtr pVga)
149{
150    NVPtr pNv = (NVPtr)pVga->MMIOBase;
151    return (VGA_RD08(pNv->PDIO, VGA_DAC_DATA));
152}
153
154static Bool
155NVIsConnected (ScrnInfoPtr pScrn, int output)
156{
157    NVPtr pNv = NVPTR(pScrn);
158    volatile U032 *PRAMDAC = pNv->PRAMDAC0;
159    CARD32 reg52C, reg608, dac0_reg608 = 0;
160    Bool present;
161
162    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
163               "Probing for analog device on output %s...\n",
164                output ? "B" : "A");
165
166    if(output) {
167        dac0_reg608 = PRAMDAC[0x0608/4];
168        PRAMDAC += 0x800;
169    }
170
171    reg52C = PRAMDAC[0x052C/4];
172    reg608 = PRAMDAC[0x0608/4];
173
174    PRAMDAC[0x0608/4] = reg608 & ~0x00010000;
175
176    PRAMDAC[0x052C/4] = reg52C & 0x0000FEEE;
177    usleep(1000);
178    PRAMDAC[0x052C/4] |= 1;
179
180    pNv->PRAMDAC0[0x0610/4] = 0x94050140;
181    pNv->PRAMDAC0[0x0608/4] |= 0x00001000;
182
183    usleep(1000);
184
185    present = (PRAMDAC[0x0608/4] & (1 << 28)) ? TRUE : FALSE;
186
187    if(present)
188       xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "  ...found one\n");
189    else
190       xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "  ...can't find one\n");
191
192    if(output)
193        pNv->PRAMDAC0[0x0608/4] = dac0_reg608;
194
195    PRAMDAC[0x052C/4] = reg52C;
196    PRAMDAC[0x0608/4] = reg608;
197
198    return present;
199}
200
201static void
202NVSelectHeadRegisters(ScrnInfoPtr pScrn, int head)
203{
204    NVPtr pNv = NVPTR(pScrn);
205
206    if(head) {
207       pNv->PCIO = pNv->PCIO0 + 0x2000;
208       pNv->PCRTC = pNv->PCRTC0 + 0x800;
209       pNv->PRAMDAC = pNv->PRAMDAC0 + 0x800;
210       pNv->PDIO = pNv->PDIO0 + 0x2000;
211    } else {
212       pNv->PCIO = pNv->PCIO0;
213       pNv->PCRTC = pNv->PCRTC0;
214       pNv->PRAMDAC = pNv->PRAMDAC0;
215       pNv->PDIO = pNv->PDIO0;
216    }
217}
218
219static xf86MonPtr
220NVProbeDDC (ScrnInfoPtr pScrn, int bus)
221{
222    NVPtr pNv = NVPTR(pScrn);
223    xf86MonPtr MonInfo = NULL;
224
225    if(!pNv->I2C) return NULL;
226
227    pNv->DDCBase = bus ? 0x36 : 0x3e;
228
229    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
230               "Probing for EDID on I2C bus %s...\n", bus ? "B" : "A");
231
232#ifdef EDID_COMPLETE_RAWDATA
233    MonInfo = xf86DoEEDID(XF86_SCRN_ARG(pScrn), pNv->I2C, TRUE);
234#else
235    MonInfo = xf86DoEDID_DDC2(XF86_SCRN_ARG(pScrn), pNv->I2C);
236#endif
237    if (MonInfo) {
238       xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
239                  "DDC detected a %s:\n", MonInfo->features.input_type ?
240                  "DFP" : "CRT");
241       xf86PrintEDID( MonInfo );
242    } else {
243       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
244                  "  ... none found\n");
245    }
246
247    return MonInfo;
248}
249
250static void nv4GetConfig (NVPtr pNv)
251{
252    if (pNv->PFB[0x0000/4] & 0x00000100) {
253        pNv->RamAmountKBytes = ((pNv->PFB[0x0000/4] >> 12) & 0x0F) * 1024 * 2
254                              + 1024 * 2;
255    } else {
256        switch (pNv->PFB[0x0000/4] & 0x00000003) {
257        case 0:
258            pNv->RamAmountKBytes = 1024 * 32;
259            break;
260        case 1:
261            pNv->RamAmountKBytes = 1024 * 4;
262            break;
263        case 2:
264            pNv->RamAmountKBytes = 1024 * 8;
265            break;
266        case 3:
267        default:
268            pNv->RamAmountKBytes = 1024 * 16;
269            break;
270        }
271    }
272    pNv->CrystalFreqKHz = (pNv->PEXTDEV[0x0000/4] & 0x00000040) ? 14318 : 13500;
273    pNv->CURSOR         = &(pNv->PRAMIN[0x1E00]);
274    pNv->MinVClockFreqKHz = 12000;
275    pNv->MaxVClockFreqKHz = 350000;
276}
277
278static void nv10GetConfig (NVPtr pNv)
279{
280    CARD32 implementation = pNv->Chipset & 0x0ff0;
281
282#if X_BYTE_ORDER == X_BIG_ENDIAN
283    /* turn on big endian register access */
284    if(!(pNv->PMC[0x0004/4] & 0x01000001)) {
285       pNv->PMC[0x0004/4] = 0x01000001;
286       mem_barrier();
287    }
288#endif
289
290#if XSERVER_LIBPCIACCESS
291    {
292    /* [AGP]: I don't know if this is correct */
293    struct pci_device *dev = pci_device_find_by_slot(0, 0, 0, 1);
294
295    if(implementation == 0x01a0) {
296        uint32_t amt;
297        pci_device_cfg_read_u32(dev, &amt, 0x7C);
298        pNv->RamAmountKBytes = (((amt >> 6) & 31) + 1) * 1024;
299    } else if(implementation == 0x01f0) {
300        uint32_t amt;
301        pci_device_cfg_read_u32(dev, &amt, 0x84);
302        pNv->RamAmountKBytes = (((amt >> 4) & 127) + 1) * 1024;
303    } else {
304        pNv->RamAmountKBytes = (pNv->PFB[0x020C/4] & 0xFFF00000) >> 10;
305    }
306    }
307#else
308    if(implementation == 0x01a0) {
309        int amt = pciReadLong(pciTag(0, 0, 1), 0x7C);
310        pNv->RamAmountKBytes = (((amt >> 6) & 31) + 1) * 1024;
311    } else if(implementation == 0x01f0) {
312        int amt = pciReadLong(pciTag(0, 0, 1), 0x84);
313        pNv->RamAmountKBytes = (((amt >> 4) & 127) + 1) * 1024;
314    } else {
315        pNv->RamAmountKBytes = (pNv->PFB[0x020C/4] & 0xFFF00000) >> 10;
316    }
317#endif
318
319    if(pNv->RamAmountKBytes > 256*1024)
320        pNv->RamAmountKBytes = 256*1024;
321
322    pNv->CrystalFreqKHz = (pNv->PEXTDEV[0x0000/4] & (1 << 6)) ? 14318 : 13500;
323
324    if(pNv->twoHeads && (implementation != 0x0110))
325    {
326       if(pNv->PEXTDEV[0x0000/4] & (1 << 22))
327           pNv->CrystalFreqKHz = 27000;
328    }
329
330    pNv->CURSOR           = NULL;  /* can't set this here */
331    pNv->MinVClockFreqKHz = 12000;
332    pNv->MaxVClockFreqKHz = pNv->twoStagePLL ? 400000 : 350000;
333}
334
335
336void
337NVCommonSetup(ScrnInfoPtr pScrn)
338{
339    NVPtr pNv = NVPTR(pScrn);
340    vgaHWPtr pVga = VGAHWPTR(pScrn);
341    CARD16 implementation = pNv->Chipset & 0x0ff0;
342    xf86MonPtr monitorA, monitorB;
343    Bool mobile = FALSE;
344    Bool tvA = FALSE;
345    Bool tvB = FALSE;
346    int FlatPanel = -1;   /* really means the CRTC is slaved */
347    Bool Television = FALSE;
348    void *tmp;
349#if XSERVER_LIBPCIACCESS
350    int err;
351#endif
352
353    /*
354     * Override VGA I/O routines.
355     */
356    pVga->writeCrtc         = NVWriteCrtc;
357    pVga->readCrtc          = NVReadCrtc;
358    pVga->writeGr           = NVWriteGr;
359    pVga->readGr            = NVReadGr;
360    pVga->writeAttr         = NVWriteAttr;
361    pVga->readAttr          = NVReadAttr;
362    pVga->writeSeq          = NVWriteSeq;
363    pVga->readSeq           = NVReadSeq;
364    pVga->writeMiscOut      = NVWriteMiscOut;
365    pVga->readMiscOut       = NVReadMiscOut;
366    pVga->enablePalette     = NVEnablePalette;
367    pVga->disablePalette    = NVDisablePalette;
368    pVga->writeDacMask      = NVWriteDacMask;
369    pVga->readDacMask       = NVReadDacMask;
370    pVga->writeDacWriteAddr = NVWriteDacWriteAddr;
371    pVga->writeDacReadAddr  = NVWriteDacReadAddr;
372    pVga->writeDacData      = NVWriteDacData;
373    pVga->readDacData       = NVReadDacData;
374    /*
375     * Note: There are different pointers to the CRTC/AR and GR/SEQ registers.
376     * Bastardize the intended uses of these to make it work.
377     */
378    pVga->MMIOBase   = (CARD8 *)pNv;
379    pVga->MMIOOffset = 0;
380
381#if XSERVER_LIBPCIACCESS
382    err = pci_device_map_range(pNv->PciInfo, pNv->IOAddress, 0x01000000,
383			       PCI_DEV_MAP_FLAG_WRITABLE, &tmp);
384    if (err != 0) {
385	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
386		   "pci_device_map_range failed: %s\n", strerror(err));
387    }
388#else
389    tmp = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_MMIO | VIDMEM_READSIDEEFFECT,
390                        pNv->PciTag, pNv->IOAddress, 0x01000000);
391#endif
392    pNv->REGS = tmp;
393
394    pNv->PRAMIN   = pNv->REGS + (0x00710000/4);
395    pNv->PCRTC0   = pNv->REGS + (0x00600000/4);
396    pNv->PRAMDAC0 = pNv->REGS + (0x00680000/4);
397    pNv->PFB      = pNv->REGS + (0x00100000/4);
398    pNv->PFIFO    = pNv->REGS + (0x00002000/4);
399    pNv->PGRAPH   = pNv->REGS + (0x00400000/4);
400    pNv->PEXTDEV  = pNv->REGS + (0x00101000/4);
401    pNv->PTIMER   = pNv->REGS + (0x00009000/4);
402    pNv->PMC      = pNv->REGS + (0x00000000/4);
403    pNv->FIFO     = pNv->REGS + (0x00800000/4);
404
405    /* 8 bit registers */
406    pNv->PCIO0    = (U008*)pNv->REGS + 0x00601000;
407    pNv->PDIO0    = (U008*)pNv->REGS + 0x00681000;
408    pNv->PVIO     = (U008*)pNv->REGS + 0x000C0000;
409
410    pNv->twoHeads =  (pNv->Architecture >= NV_ARCH_10) &&
411                     (implementation != 0x0100) &&
412                     (implementation != 0x0150) &&
413                     (implementation != 0x01A0) &&
414                     (implementation != 0x0200);
415
416    pNv->fpScaler = (pNv->FpScale && pNv->twoHeads && (implementation!=0x0110));
417
418    pNv->twoStagePLL = (implementation == 0x0310) ||
419                       (implementation == 0x0340) ||
420                       (pNv->Architecture >= NV_ARCH_40);
421
422    pNv->WaitVSyncPossible = (pNv->Architecture >= NV_ARCH_10) &&
423                             (implementation != 0x0100);
424
425    pNv->BlendingPossible = ((pNv->Chipset & 0xffff) != 0x0020);
426
427    /* look for known laptop chips */
428    switch(pNv->Chipset & 0xffff) {
429    case 0x0112:
430    case 0x0174:
431    case 0x0175:
432    case 0x0176:
433    case 0x0177:
434    case 0x0179:
435    case 0x017C:
436    case 0x017D:
437    case 0x0186:
438    case 0x0187:
439    case 0x018D:
440    case 0x0228:
441    case 0x0286:
442    case 0x028C:
443    case 0x0316:
444    case 0x0317:
445    case 0x031A:
446    case 0x031B:
447    case 0x031C:
448    case 0x031D:
449    case 0x031E:
450    case 0x031F:
451    case 0x0324:
452    case 0x0325:
453    case 0x0328:
454    case 0x0329:
455    case 0x032C:
456    case 0x032D:
457    case 0x0347:
458    case 0x0348:
459    case 0x0349:
460    case 0x034B:
461    case 0x034C:
462    case 0x0160:
463    case 0x0166:
464    case 0x0169:
465    case 0x016B:
466    case 0x016C:
467    case 0x016D:
468    case 0x00C8:
469    case 0x00CC:
470    case 0x0144:
471    case 0x0146:
472    case 0x0148:
473    case 0x0098:
474    case 0x0099:
475        mobile = TRUE;
476        break;
477    default:
478        break;
479    }
480
481    if(pNv->Architecture == NV_ARCH_04)
482        nv4GetConfig(pNv);
483    else
484        nv10GetConfig(pNv);
485
486    NVSelectHeadRegisters(pScrn, 0);
487
488    NVLockUnlock(pNv, 0);
489
490    NVI2CInit(pScrn);
491
492    pNv->Television = FALSE;
493
494    if(!pNv->twoHeads) {
495       pNv->CRTCnumber = 0;
496       if((monitorA = NVProbeDDC(pScrn, 0))) {
497           FlatPanel = monitorA->features.input_type ? 1 : 0;
498
499           /* NV4 doesn't support FlatPanels */
500           if((pNv->Chipset & 0x0fff) <= 0x0020)
501              FlatPanel = 0;
502       } else {
503           VGA_WR08(pNv->PCIO, 0x03D4, 0x28);
504           if(VGA_RD08(pNv->PCIO, 0x03D5) & 0x80) {
505              VGA_WR08(pNv->PCIO, 0x03D4, 0x33);
506              if(!(VGA_RD08(pNv->PCIO, 0x03D5) & 0x01))
507                 Television = TRUE;
508              FlatPanel = 1;
509           } else {
510              FlatPanel = 0;
511           }
512           xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
513                         "HW is currently programmed for %s\n",
514                          FlatPanel ? (Television ? "TV" : "DFP") : "CRT");
515       }
516
517       if(pNv->FlatPanel == -1) {
518           pNv->FlatPanel = FlatPanel;
519           pNv->Television = Television;
520       } else {
521           xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
522                      "Forcing display type to %s as specified\n",
523                       pNv->FlatPanel ? "DFP" : "CRT");
524       }
525    } else {
526       CARD8 outputAfromCRTC, outputBfromCRTC;
527       int CRTCnumber = -1;
528       CARD8 slaved_on_A, slaved_on_B;
529       Bool analog_on_A, analog_on_B;
530       CARD32 oldhead;
531       CARD8 cr44;
532
533       if(implementation != 0x0110) {
534           if(pNv->PRAMDAC0[0x0000052C/4] & 0x100)
535               outputAfromCRTC = 1;
536           else
537               outputAfromCRTC = 0;
538           if(pNv->PRAMDAC0[0x0000252C/4] & 0x100)
539               outputBfromCRTC = 1;
540           else
541               outputBfromCRTC = 0;
542          analog_on_A = NVIsConnected(pScrn, 0);
543          analog_on_B = NVIsConnected(pScrn, 1);
544       } else {
545          outputAfromCRTC = 0;
546          outputBfromCRTC = 1;
547          analog_on_A = FALSE;
548          analog_on_B = FALSE;
549       }
550
551       VGA_WR08(pNv->PCIO, 0x03D4, 0x44);
552       cr44 = VGA_RD08(pNv->PCIO, 0x03D5);
553
554       VGA_WR08(pNv->PCIO, 0x03D5, 3);
555       NVSelectHeadRegisters(pScrn, 1);
556       NVLockUnlock(pNv, 0);
557
558       VGA_WR08(pNv->PCIO, 0x03D4, 0x28);
559       slaved_on_B = VGA_RD08(pNv->PCIO, 0x03D5) & 0x80;
560       if(slaved_on_B) {
561           VGA_WR08(pNv->PCIO, 0x03D4, 0x33);
562           tvB = !(VGA_RD08(pNv->PCIO, 0x03D5) & 0x01);
563       }
564
565       VGA_WR08(pNv->PCIO, 0x03D4, 0x44);
566       VGA_WR08(pNv->PCIO, 0x03D5, 0);
567       NVSelectHeadRegisters(pScrn, 0);
568       NVLockUnlock(pNv, 0);
569
570       VGA_WR08(pNv->PCIO, 0x03D4, 0x28);
571       slaved_on_A = VGA_RD08(pNv->PCIO, 0x03D5) & 0x80;
572       if(slaved_on_A) {
573           VGA_WR08(pNv->PCIO, 0x03D4, 0x33);
574           tvA = !(VGA_RD08(pNv->PCIO, 0x03D5) & 0x01);
575       }
576
577       oldhead = pNv->PCRTC0[0x00000860/4];
578       pNv->PCRTC0[0x00000860/4] = oldhead | 0x00000010;
579
580       monitorA = NVProbeDDC(pScrn, 0);
581       monitorB = NVProbeDDC(pScrn, 1);
582
583       if(slaved_on_A && !tvA) {
584          CRTCnumber = 0;
585          FlatPanel = 1;
586          xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
587                    "CRTC 0 is currently programmed for DFP\n");
588       } else
589       if(slaved_on_B && !tvB) {
590          CRTCnumber = 1;
591          FlatPanel = 1;
592          xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
593                    "CRTC 1 is currently programmed for DFP\n");
594       } else
595       if(analog_on_A) {
596          CRTCnumber = outputAfromCRTC;
597          FlatPanel = 0;
598          xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
599                    "CRTC %i appears to have a CRT attached\n", CRTCnumber);
600       } else
601       if(analog_on_B) {
602           CRTCnumber = outputBfromCRTC;
603           FlatPanel = 0;
604           xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
605                    "CRTC %i appears to have a CRT attached\n", CRTCnumber);
606       } else
607       if(slaved_on_A) {
608          CRTCnumber = 0;
609          FlatPanel = 1;
610          Television = 1;
611          xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
612                    "CRTC 0 is currently programmed for TV\n");
613       } else
614       if(slaved_on_B) {
615          CRTCnumber = 1;
616          FlatPanel = 1;
617          Television = 1;
618          xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
619                    "CRTC 1 is currently programmed for TV\n");
620       } else
621       if(monitorA) {
622           FlatPanel = monitorA->features.input_type ? 1 : 0;
623       } else
624       if(monitorB) {
625           FlatPanel = monitorB->features.input_type ? 1 : 0;
626       }
627
628       if(pNv->FlatPanel == -1) {
629          if(FlatPanel != -1) {
630             pNv->FlatPanel = FlatPanel;
631             pNv->Television = Television;
632          } else {
633             xf86DrvMsg(pScrn->scrnIndex, X_INFO,
634                        "Unable to detect display type...\n");
635             if(mobile) {
636                 xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT,
637                            "...On a laptop, assuming DFP\n");
638                 pNv->FlatPanel = 1;
639             } else {
640                 xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT,
641                            "...Using default of CRT\n");
642                 pNv->FlatPanel = 0;
643             }
644          }
645       } else {
646           xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
647                      "Forcing display type to %s as specified\n",
648                       pNv->FlatPanel ? "DFP" : "CRT");
649       }
650
651       if(pNv->CRTCnumber == -1) {
652          if(CRTCnumber != -1) pNv->CRTCnumber = CRTCnumber;
653          else {
654             xf86DrvMsg(pScrn->scrnIndex, X_INFO,
655                        "Unable to detect which CRTCNumber...\n");
656             if(pNv->FlatPanel) pNv->CRTCnumber = 1;
657             else pNv->CRTCnumber = 0;
658             xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT,
659                        "...Defaulting to CRTCNumber %i\n", pNv->CRTCnumber);
660          }
661       } else {
662           xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
663                      "Forcing CRTCNumber %i as specified\n", pNv->CRTCnumber);
664       }
665
666       if(monitorA) {
667           if((monitorA->features.input_type && pNv->FlatPanel) ||
668              (!monitorA->features.input_type && !pNv->FlatPanel))
669           {
670               if(monitorB) {
671                  free(monitorB);
672                  monitorB = NULL;
673               }
674           } else {
675              free(monitorA);
676              monitorA = NULL;
677           }
678       }
679
680       if(monitorB) {
681           if((monitorB->features.input_type && !pNv->FlatPanel) ||
682              (!monitorB->features.input_type && pNv->FlatPanel))
683           {
684              free(monitorB);
685           } else {
686              monitorA = monitorB;
687           }
688           monitorB = NULL;
689       }
690
691       if(implementation == 0x0110)
692           cr44 = pNv->CRTCnumber * 0x3;
693
694       pNv->PCRTC0[0x00000860/4] = oldhead;
695
696       VGA_WR08(pNv->PCIO, 0x03D4, 0x44);
697       VGA_WR08(pNv->PCIO, 0x03D5, cr44);
698       NVSelectHeadRegisters(pScrn, pNv->CRTCnumber);
699    }
700
701    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
702              "Using %s on CRTC %i\n",
703              pNv->FlatPanel ? (pNv->Television ? "TV" : "DFP") : "CRT",
704              pNv->CRTCnumber);
705
706    if(pNv->FlatPanel && !pNv->Television) {
707       pNv->fpWidth = pNv->PRAMDAC[0x0820/4] + 1;
708       pNv->fpHeight = pNv->PRAMDAC[0x0800/4] + 1;
709       pNv->fpVTotal = pNv->PRAMDAC[0x804/4] + 1;
710       pNv->fpSyncs = pNv->PRAMDAC[0x0848/4] & 0x30000033;
711       xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Panel size is %i x %i\n",
712                  pNv->fpWidth, pNv->fpHeight);
713       xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NOTE: This driver cannot "
714                  "reconfigure the BIOS-programmed size.\n");
715       xf86DrvMsg(pScrn->scrnIndex, X_INFO, "These dimensions will be used as "
716                  "the panel size for mode validation.\n");
717    }
718
719    if(monitorA)
720      xf86SetDDCproperties(pScrn, monitorA);
721
722    if(!pNv->FlatPanel || (pScrn->depth != 24) || !pNv->twoHeads)
723        pNv->FPDither = FALSE;
724
725    pNv->LVDS = FALSE;
726    if(pNv->FlatPanel && pNv->twoHeads) {
727        pNv->PRAMDAC0[0x08B0/4] = 0x00010004;
728        if(pNv->PRAMDAC0[0x08B4/4] & 1)
729           pNv->LVDS = TRUE;
730        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel is %s\n",
731                   pNv->LVDS ? "LVDS" : "TMDS");
732    }
733}
734