Merging XORG-CURRENT into trunk
[nouveau] / src / nv_setup.c
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 /* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nv_setup.c,v 1.39 2003/11/07 23:56:28 mvojkovi Exp $ */
41
42 #include "nv_include.h"
43
44 /*
45  * Override VGA I/O routines.
46  */
47 static void NVWriteCrtc(vgaHWPtr pVga, CARD8 index, CARD8 value)
48 {
49     NVPtr pNv = (NVPtr)pVga->MMIOBase;
50     VGA_WR08(pNv->PCIO, pVga->IOBase + VGA_CRTC_INDEX_OFFSET, index);
51     VGA_WR08(pNv->PCIO, pVga->IOBase + VGA_CRTC_DATA_OFFSET,  value);
52 }
53 static CARD8 NVReadCrtc(vgaHWPtr pVga, CARD8 index)
54 {
55     NVPtr pNv = (NVPtr)pVga->MMIOBase;
56     VGA_WR08(pNv->PCIO, pVga->IOBase + VGA_CRTC_INDEX_OFFSET, index);
57     return (VGA_RD08(pNv->PCIO, pVga->IOBase + VGA_CRTC_DATA_OFFSET));
58 }
59 static void NVWriteGr(vgaHWPtr pVga, CARD8 index, CARD8 value)
60 {
61     NVPtr pNv = (NVPtr)pVga->MMIOBase;
62     VGA_WR08(pNv->PVIO, VGA_GRAPH_INDEX, index);
63     VGA_WR08(pNv->PVIO, VGA_GRAPH_DATA,  value);
64 }
65 static CARD8 NVReadGr(vgaHWPtr pVga, CARD8 index)
66 {
67     NVPtr pNv = (NVPtr)pVga->MMIOBase;
68     VGA_WR08(pNv->PVIO, VGA_GRAPH_INDEX, index);
69     return (VGA_RD08(pNv->PVIO, VGA_GRAPH_DATA));
70 }
71 static void NVWriteSeq(vgaHWPtr pVga, CARD8 index, CARD8 value)
72 {
73     NVPtr pNv = (NVPtr)pVga->MMIOBase;
74     VGA_WR08(pNv->PVIO, VGA_SEQ_INDEX, index);
75     VGA_WR08(pNv->PVIO, VGA_SEQ_DATA,  value);
76 }
77 static CARD8 NVReadSeq(vgaHWPtr pVga, CARD8 index)
78 {
79     NVPtr pNv = (NVPtr)pVga->MMIOBase;
80     VGA_WR08(pNv->PVIO, VGA_SEQ_INDEX, index);
81     return (VGA_RD08(pNv->PVIO, VGA_SEQ_DATA));
82 }
83 static void NVWriteAttr(vgaHWPtr pVga, CARD8 index, CARD8 value)
84 {
85     NVPtr pNv = (NVPtr)pVga->MMIOBase;
86     volatile CARD8 tmp;
87
88     tmp = VGA_RD08(pNv->PCIO, pVga->IOBase + VGA_IN_STAT_1_OFFSET);
89     if (pVga->paletteEnabled)
90         index &= ~0x20;
91     else
92         index |= 0x20;
93     VGA_WR08(pNv->PCIO, VGA_ATTR_INDEX,  index);
94     VGA_WR08(pNv->PCIO, VGA_ATTR_DATA_W, value);
95 }
96 static CARD8 NVReadAttr(vgaHWPtr pVga, CARD8 index)
97 {
98     NVPtr pNv = (NVPtr)pVga->MMIOBase;
99     volatile CARD8 tmp;
100
101     tmp = VGA_RD08(pNv->PCIO, pVga->IOBase + VGA_IN_STAT_1_OFFSET);
102     if (pVga->paletteEnabled)
103         index &= ~0x20;
104     else
105         index |= 0x20;
106     VGA_WR08(pNv->PCIO, VGA_ATTR_INDEX, index);
107     return (VGA_RD08(pNv->PCIO, VGA_ATTR_DATA_R));
108 }
109 static void NVWriteMiscOut(vgaHWPtr pVga, CARD8 value)
110 {
111     NVPtr pNv = (NVPtr)pVga->MMIOBase;
112     VGA_WR08(pNv->PVIO, VGA_MISC_OUT_W, value);
113 }
114 static CARD8 NVReadMiscOut(vgaHWPtr pVga)
115 {
116     NVPtr pNv = (NVPtr)pVga->MMIOBase;
117     return (VGA_RD08(pNv->PVIO, VGA_MISC_OUT_R));
118 }
119 static void NVEnablePalette(vgaHWPtr pVga)
120 {
121     NVPtr pNv = (NVPtr)pVga->MMIOBase;
122     volatile CARD8 tmp;
123
124     tmp = VGA_RD08(pNv->PCIO, pVga->IOBase + VGA_IN_STAT_1_OFFSET);
125     VGA_WR08(pNv->PCIO, VGA_ATTR_INDEX, 0x00);
126     pVga->paletteEnabled = TRUE;
127 }
128 static void NVDisablePalette(vgaHWPtr pVga)
129 {
130     NVPtr pNv = (NVPtr)pVga->MMIOBase;
131     volatile CARD8 tmp;
132
133     tmp = VGA_RD08(pNv->PCIO, pVga->IOBase + VGA_IN_STAT_1_OFFSET);
134     VGA_WR08(pNv->PCIO, VGA_ATTR_INDEX, 0x20);
135     pVga->paletteEnabled = FALSE;
136 }
137 static void NVWriteDacMask(vgaHWPtr pVga, CARD8 value)
138 {
139     NVPtr pNv = (NVPtr)pVga->MMIOBase;
140     VGA_WR08(pNv->PDIO, VGA_DAC_MASK, value);
141 }
142 static CARD8 NVReadDacMask(vgaHWPtr pVga)
143 {
144     NVPtr pNv = (NVPtr)pVga->MMIOBase;
145     return (VGA_RD08(pNv->PDIO, VGA_DAC_MASK));
146 }
147 static void NVWriteDacReadAddr(vgaHWPtr pVga, CARD8 value)
148 {
149     NVPtr pNv = (NVPtr)pVga->MMIOBase;
150     VGA_WR08(pNv->PDIO, VGA_DAC_READ_ADDR, value);
151 }
152 static void NVWriteDacWriteAddr(vgaHWPtr pVga, CARD8 value)
153 {
154     NVPtr pNv = (NVPtr)pVga->MMIOBase;
155     VGA_WR08(pNv->PDIO, VGA_DAC_WRITE_ADDR, value);
156 }
157 static void NVWriteDacData(vgaHWPtr pVga, CARD8 value)
158 {
159     NVPtr pNv = (NVPtr)pVga->MMIOBase;
160     VGA_WR08(pNv->PDIO, VGA_DAC_DATA, value);
161 }
162 static CARD8 NVReadDacData(vgaHWPtr pVga)
163 {
164     NVPtr pNv = (NVPtr)pVga->MMIOBase;
165     return (VGA_RD08(pNv->PDIO, VGA_DAC_DATA));
166 }
167
168 static Bool 
169 NVIsConnected (ScrnInfoPtr pScrn, int output)
170 {
171     NVPtr pNv = NVPTR(pScrn);
172     volatile U032 *PRAMDAC = pNv->PRAMDAC0;
173     CARD32 reg52C, reg608;
174     Bool present;
175
176     xf86DrvMsg(pScrn->scrnIndex, X_INFO,
177                "Probing for analog device on output %s...\n", 
178                 output ? "B" : "A");
179
180     if(output) PRAMDAC += 0x800;
181
182     reg52C = PRAMDAC[0x052C/4];
183     reg608 = PRAMDAC[0x0608/4];
184
185     PRAMDAC[0x0608/4] = reg608 & ~0x00010000;
186
187     PRAMDAC[0x052C/4] = reg52C & 0x0000FEEE;
188     usleep(1000);
189     PRAMDAC[0x052C/4] |= 1;
190
191     pNv->PRAMDAC0[0x0610/4] = 0x94050140;
192     pNv->PRAMDAC0[0x0608/4] |= 0x00001000;
193
194     usleep(1000);
195
196     present = (PRAMDAC[0x0608/4] & (1 << 28)) ? TRUE : FALSE;
197
198     if(present)
199        xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "  ...found one\n");
200     else
201        xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "  ...can't find one\n");
202
203     pNv->PRAMDAC0[0x0608/4] &= 0x0000EFFF;
204
205     PRAMDAC[0x052C/4] = reg52C;
206     PRAMDAC[0x0608/4] = reg608;
207
208     return present;
209 }
210
211 static void
212 NVSelectHeadRegisters(ScrnInfoPtr pScrn, int head)
213 {
214     NVPtr pNv = NVPTR(pScrn);
215
216     if(head) {
217        pNv->PCIO = pNv->PCIO0 + 0x2000;
218        pNv->PCRTC = pNv->PCRTC0 + 0x800;
219        pNv->PRAMDAC = pNv->PRAMDAC0 + 0x800;
220        pNv->PDIO = pNv->PDIO0 + 0x2000;
221     } else {
222        pNv->PCIO = pNv->PCIO0;
223        pNv->PCRTC = pNv->PCRTC0;
224        pNv->PRAMDAC = pNv->PRAMDAC0;
225        pNv->PDIO = pNv->PDIO0;
226     }
227 }
228
229 static xf86MonPtr 
230 NVProbeDDC (ScrnInfoPtr pScrn, int bus)
231 {
232     NVPtr pNv = NVPTR(pScrn);
233     xf86MonPtr MonInfo = NULL;
234
235     if(!pNv->I2C) return NULL;
236
237     pNv->DDCBase = bus ? 0x36 : 0x3e;
238
239     xf86DrvMsg(pScrn->scrnIndex, X_INFO, 
240                "Probing for EDID on I2C bus %s...\n", bus ? "B" : "A");
241
242     if ((MonInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, pNv->I2C))) {
243        xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
244                   "DDC detected a %s:\n", MonInfo->features.input_type ?
245                   "DFP" : "CRT");
246        xf86PrintEDID( MonInfo );
247     } else {
248        xf86DrvMsg(pScrn->scrnIndex, X_INFO, 
249                   "  ... none found\n");
250     }
251
252     return MonInfo;
253 }
254
255 static void nv4GetConfig (NVPtr pNv)
256 {
257     if (pNv->PFB[0x0000/4] & 0x00000100) {
258         pNv->RamAmountKBytes = ((pNv->PFB[0x0000/4] >> 12) & 0x0F) * 1024 * 2
259                               + 1024 * 2;
260     } else {
261         switch (pNv->PFB[0x0000/4] & 0x00000003) {
262         case 0:
263             pNv->RamAmountKBytes = 1024 * 32;
264             break;
265         case 1:
266             pNv->RamAmountKBytes = 1024 * 4;
267             break;
268         case 2:
269             pNv->RamAmountKBytes = 1024 * 8;
270             break;
271         case 3:
272         default:
273             pNv->RamAmountKBytes = 1024 * 16;
274             break;
275         }
276     }
277     pNv->CrystalFreqKHz = (pNv->PEXTDEV[0x0000/4] & 0x00000040) ? 14318 : 13500;
278     pNv->CURSOR         = &(pNv->PRAMIN[0x1E00]);
279     pNv->MinVClockFreqKHz = 12000;
280     pNv->MaxVClockFreqKHz = 350000;
281 }
282
283 static void nv10GetConfig (NVPtr pNv)
284 {
285     CARD32 implementation = pNv->Chipset & 0x0ff0;
286
287 #if X_BYTE_ORDER == X_BIG_ENDIAN
288     /* turn on big endian register access */
289     if(!(pNv->PMC[0x0004/4] & 0x01000001)) {
290        pNv->PMC[0x0004/4] = 0x01000001;
291        mem_barrier();
292     }
293 #endif
294
295     if((pNv->Chipset & 0xffff) == 0x01a0) {
296         int amt = pciReadLong(pciTag(0, 0, 1), 0x7C);
297         pNv->RamAmountKBytes = (((amt >> 6) & 31) + 1) * 1024;
298     } else if((pNv->Chipset & 0xffff) == 0x01f0) {
299         int amt = pciReadLong(pciTag(0, 0, 1), 0x84);
300         pNv->RamAmountKBytes = (((amt >> 4) & 127) + 1) * 1024;
301     } else {
302         pNv->RamAmountKBytes = (pNv->PFB[0x020C/4] & 0xFFF00000) >> 10;
303     }
304
305     pNv->CrystalFreqKHz = (pNv->PEXTDEV[0x0000/4] & (1 << 6)) ? 14318 : 13500;
306     
307     if((implementation == 0x0170) ||
308        (implementation == 0x0180) ||
309        (implementation == 0x01F0) ||
310        (implementation >= 0x0250))
311     {
312        if(pNv->PEXTDEV[0x0000/4] & (1 << 22))
313            pNv->CrystalFreqKHz = 27000;
314     }
315
316     pNv->CursorStart      = (pNv->RamAmountKBytes - 96) * 1024;
317     pNv->CURSOR           = NULL;  /* can't set this here */
318     pNv->MinVClockFreqKHz = 12000;
319     pNv->MaxVClockFreqKHz = pNv->twoStagePLL ? 400000 : 350000;
320 }
321
322
323 void
324 NVCommonSetup(ScrnInfoPtr pScrn)
325 {
326     NVPtr pNv = NVPTR(pScrn);
327     vgaHWPtr pVga = VGAHWPTR(pScrn);
328     CARD16 implementation = pNv->Chipset & 0x0ff0;
329     xf86MonPtr monitorA, monitorB;
330     Bool mobile = FALSE;
331     Bool tvA = FALSE;
332     Bool tvB = FALSE;
333     int FlatPanel = -1;   /* really means the CRTC is slaved */
334     Bool Television = FALSE;
335     
336     /*
337      * Override VGA I/O routines.
338      */
339     pVga->writeCrtc         = NVWriteCrtc;
340     pVga->readCrtc          = NVReadCrtc;
341     pVga->writeGr           = NVWriteGr;
342     pVga->readGr            = NVReadGr;
343     pVga->writeAttr         = NVWriteAttr;
344     pVga->readAttr          = NVReadAttr;
345     pVga->writeSeq          = NVWriteSeq;
346     pVga->readSeq           = NVReadSeq;
347     pVga->writeMiscOut      = NVWriteMiscOut;
348     pVga->readMiscOut       = NVReadMiscOut;
349     pVga->enablePalette     = NVEnablePalette;
350     pVga->disablePalette    = NVDisablePalette;
351     pVga->writeDacMask      = NVWriteDacMask;
352     pVga->readDacMask       = NVReadDacMask;
353     pVga->writeDacWriteAddr = NVWriteDacWriteAddr;
354     pVga->writeDacReadAddr  = NVWriteDacReadAddr;
355     pVga->writeDacData      = NVWriteDacData;
356     pVga->readDacData       = NVReadDacData;
357     /*
358      * Note: There are different pointers to the CRTC/AR and GR/SEQ registers.
359      * Bastardize the intended uses of these to make it work.
360      */
361     pVga->MMIOBase   = (CARD8 *)pNv;
362     pVga->MMIOOffset = 0;
363     
364     pNv->REGS = xf86MapPciMem(pScrn->scrnIndex, 
365                               VIDMEM_MMIO | VIDMEM_READSIDEEFFECT, 
366                               pNv->PciTag, pNv->IOAddress, 0x01000000);
367
368     pNv->PRAMIN   = pNv->REGS + (0x00710000/4);
369     pNv->PCRTC0   = pNv->REGS + (0x00600000/4);
370     pNv->PRAMDAC0 = pNv->REGS + (0x00680000/4);
371     pNv->PFB      = pNv->REGS + (0x00100000/4);
372     pNv->PFIFO    = pNv->REGS + (0x00002000/4);
373     pNv->PGRAPH   = pNv->REGS + (0x00400000/4);
374     pNv->PEXTDEV  = pNv->REGS + (0x00101000/4);
375     pNv->PTIMER   = pNv->REGS + (0x00009000/4);
376     pNv->PMC      = pNv->REGS + (0x00000000/4);
377     pNv->FIFO     = pNv->REGS + (0x00800000/4);
378
379     /* 8 bit registers */
380     pNv->PCIO0    = (U008*)pNv->REGS + 0x00601000;
381     pNv->PDIO0    = (U008*)pNv->REGS + 0x00681000;
382     pNv->PVIO     = (U008*)pNv->REGS + 0x000C0000;
383
384     pNv->twoHeads =  (implementation >= 0x0110) &&
385                      (implementation != 0x0150) &&
386                      (implementation != 0x01A0) &&
387                      (implementation != 0x0200);
388
389     pNv->fpScaler = (pNv->twoHeads && (implementation != 0x0110));
390
391     pNv->twoStagePLL = (implementation == 0x0310) ||
392                        (implementation == 0x0340);
393
394     /* look for known laptop chips */
395     switch(pNv->Chipset & 0xffff) {
396     case 0x0112:
397     case 0x0174:
398     case 0x0175:
399     case 0x0176:
400     case 0x0177:
401     case 0x0179:
402     case 0x017C:
403     case 0x017D:
404     case 0x0186:
405     case 0x0187:
406     case 0x0189:
407     case 0x0286:
408     case 0x028C:
409     case 0x0316:
410     case 0x0317:
411     case 0x031A:
412     case 0x031B:
413     case 0x031C:
414     case 0x031D:
415     case 0x031E:
416     case 0x031F:
417     case 0x0324:
418     case 0x0325:
419     case 0x0328:
420     case 0x0329:
421     case 0x032C:
422     case 0x032D:
423     case 0x0347:
424     case 0x0348:
425     case 0x0349:
426     case 0x034B:
427     case 0x034C:
428         mobile = TRUE;
429         break;
430     default:
431         break;
432     }
433
434     if(pNv->Architecture == NV_ARCH_04)
435         nv4GetConfig(pNv);
436     else
437         nv10GetConfig(pNv);
438
439     NVSelectHeadRegisters(pScrn, 0);
440
441     NVLockUnlock(pNv, 0);
442
443     NVI2CInit(pScrn);
444
445     pNv->Television = FALSE;
446
447     if(!pNv->twoHeads) {
448        pNv->CRTCnumber = 0;
449        if((monitorA = NVProbeDDC(pScrn, 0))) {
450            FlatPanel = monitorA->features.input_type ? 1 : 0;
451
452            /* NV4 doesn't support FlatPanels */
453            if((pNv->Chipset & 0x0fff) <= 0x0020)
454               FlatPanel = 0;
455        } else {
456            VGA_WR08(pNv->PCIO, 0x03D4, 0x28);
457            if(VGA_RD08(pNv->PCIO, 0x03D5) & 0x80) {
458               VGA_WR08(pNv->PCIO, 0x03D4, 0x33);
459               if(!(VGA_RD08(pNv->PCIO, 0x03D5) & 0x01)) 
460                  Television = TRUE;
461               FlatPanel = 1;
462            } else {
463               FlatPanel = 0;
464            }
465            xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
466                          "HW is currently programmed for %s\n",
467                           FlatPanel ? (Television ? "TV" : "DFP") : "CRT");
468        } 
469
470        if(pNv->FlatPanel == -1) {
471            pNv->FlatPanel = FlatPanel;
472            pNv->Television = Television;
473        } else {
474            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
475                       "Forcing display type to %s as specified\n", 
476                        pNv->FlatPanel ? "DFP" : "CRT");
477        }
478     } else {
479        CARD8 outputAfromCRTC, outputBfromCRTC;
480        int CRTCnumber = -1;
481        CARD8 slaved_on_A, slaved_on_B;
482        Bool analog_on_A, analog_on_B;
483        CARD32 oldhead;
484        CARD8 cr44;
485       
486        if(implementation != 0x0110) {
487            if(pNv->PRAMDAC0[0x0000052C/4] & 0x100)
488                outputAfromCRTC = 1;
489            else            
490                outputAfromCRTC = 0;
491            if(pNv->PRAMDAC0[0x0000252C/4] & 0x100)
492                outputBfromCRTC = 1;
493            else
494                outputBfromCRTC = 0;
495           analog_on_A = NVIsConnected(pScrn, 0);
496           analog_on_B = NVIsConnected(pScrn, 1);
497        } else {
498           outputAfromCRTC = 0;
499           outputBfromCRTC = 1;
500           analog_on_A = FALSE;
501           analog_on_B = FALSE;
502        }
503
504        VGA_WR08(pNv->PCIO, 0x03D4, 0x44);
505        cr44 = VGA_RD08(pNv->PCIO, 0x03D5);
506
507        VGA_WR08(pNv->PCIO, 0x03D5, 3);
508        NVSelectHeadRegisters(pScrn, 1);
509        NVLockUnlock(pNv, 0);
510
511        VGA_WR08(pNv->PCIO, 0x03D4, 0x28);
512        slaved_on_B = VGA_RD08(pNv->PCIO, 0x03D5) & 0x80;
513        if(slaved_on_B) {
514            VGA_WR08(pNv->PCIO, 0x03D4, 0x33);
515            tvB = !(VGA_RD08(pNv->PCIO, 0x03D5) & 0x01);
516        }
517
518        VGA_WR08(pNv->PCIO, 0x03D4, 0x44);
519        VGA_WR08(pNv->PCIO, 0x03D5, 0);
520        NVSelectHeadRegisters(pScrn, 0);
521        NVLockUnlock(pNv, 0);
522
523        VGA_WR08(pNv->PCIO, 0x03D4, 0x28);
524        slaved_on_A = VGA_RD08(pNv->PCIO, 0x03D5) & 0x80; 
525        if(slaved_on_A) {
526            VGA_WR08(pNv->PCIO, 0x03D4, 0x33);
527            tvA = !(VGA_RD08(pNv->PCIO, 0x03D5) & 0x01);
528        }
529
530        oldhead = pNv->PCRTC0[0x00000860/4];
531        pNv->PCRTC0[0x00000860/4] = oldhead | 0x00000010;
532
533        monitorA = NVProbeDDC(pScrn, 0);
534        monitorB = NVProbeDDC(pScrn, 1);
535
536        if(slaved_on_A && !tvA) {
537           CRTCnumber = 0;
538           FlatPanel = 1;
539           xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
540                     "CRTC 0 is currently programmed for DFP\n");
541        } else 
542        if(slaved_on_B && !tvB) {
543           CRTCnumber = 1;
544           FlatPanel = 1;
545           xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
546                     "CRTC 1 is currently programmed for DFP\n");
547        } else
548        if(analog_on_A) {
549           CRTCnumber = outputAfromCRTC;
550           FlatPanel = 0;
551           xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
552                     "CRTC %i appears to have a CRT attached\n", CRTCnumber);
553        } else
554        if(analog_on_B) {
555            CRTCnumber = outputBfromCRTC;
556            FlatPanel = 0;
557            xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
558                     "CRTC %i appears to have a CRT attached\n", CRTCnumber);
559        } else
560        if(slaved_on_A) {
561           CRTCnumber = 0;
562           FlatPanel = 1;
563           Television = 1;
564           xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
565                     "CRTC 0 is currently programmed for TV\n");
566        } else
567        if(slaved_on_B) {
568           CRTCnumber = 1;
569           FlatPanel = 1;
570           Television = 1;
571           xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
572                     "CRTC 1 is currently programmed for TV\n");
573        } else
574        if(monitorA) {
575            FlatPanel = monitorA->features.input_type ? 1 : 0;
576        } else 
577        if(monitorB) {
578            FlatPanel = monitorB->features.input_type ? 1 : 0;
579        }
580
581        if(pNv->FlatPanel == -1) {
582           if(FlatPanel != -1) {
583              pNv->FlatPanel = FlatPanel;
584              pNv->Television = Television;
585           } else {
586              xf86DrvMsg(pScrn->scrnIndex, X_INFO,
587                         "Unable to detect display type...\n");
588              if(mobile) {
589                  xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT,
590                             "...On a laptop, assuming DFP\n");
591                  pNv->FlatPanel = 1;
592              } else {
593                  xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT,
594                             "...Using default of CRT\n");
595                  pNv->FlatPanel = 0;
596              }
597           }
598        } else {
599            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
600                       "Forcing display type to %s as specified\n", 
601                        pNv->FlatPanel ? "DFP" : "CRT");
602        }
603
604        if(pNv->CRTCnumber == -1) {
605           if(CRTCnumber != -1) pNv->CRTCnumber = CRTCnumber;
606           else {
607              xf86DrvMsg(pScrn->scrnIndex, X_INFO,
608                         "Unable to detect which CRTCNumber...\n");
609              if(pNv->FlatPanel) pNv->CRTCnumber = 1;
610              else pNv->CRTCnumber = 0;
611              xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT,
612                         "...Defaulting to CRTCNumber %i\n", pNv->CRTCnumber);
613           }
614        } else {
615            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
616                       "Forcing CRTCNumber %i as specified\n", pNv->CRTCnumber);
617        }
618      
619        if(monitorA) {
620            if((monitorA->features.input_type && pNv->FlatPanel) ||
621               (!monitorA->features.input_type && !pNv->FlatPanel))
622            {
623                if(monitorB) { 
624                   xfree(monitorB);
625                   monitorB = NULL;
626                }
627            } else {
628               xfree(monitorA);
629               monitorA = NULL;
630            }
631        }
632
633        if(monitorB) {
634            if((monitorB->features.input_type && !pNv->FlatPanel) ||
635               (!monitorB->features.input_type && pNv->FlatPanel)) 
636            {
637               xfree(monitorB);
638            } else {
639               monitorA = monitorB;
640            }
641            monitorB = NULL;
642        }
643
644        if(implementation == 0x0110)
645            cr44 = pNv->CRTCnumber * 0x3;
646
647        pNv->PCRTC0[0x00000860/4] = oldhead;
648
649        VGA_WR08(pNv->PCIO, 0x03D4, 0x44);
650        VGA_WR08(pNv->PCIO, 0x03D5, cr44);
651        NVSelectHeadRegisters(pScrn, pNv->CRTCnumber);
652     }
653
654     xf86DrvMsg(pScrn->scrnIndex, X_INFO,
655               "Using %s on CRTC %i\n",
656               pNv->FlatPanel ? (pNv->Television ? "TV" : "DFP") : "CRT", 
657               pNv->CRTCnumber);
658
659     if(pNv->FlatPanel && !pNv->Television) {
660        pNv->fpWidth = pNv->PRAMDAC[0x0820/4] + 1;
661        pNv->fpHeight = pNv->PRAMDAC[0x0800/4] + 1;
662        xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Panel size is %i x %i\n",
663                   pNv->fpWidth, pNv->fpHeight);
664     }
665
666     if(monitorA)
667       xf86SetDDCproperties(pScrn, monitorA);
668
669     if(!pNv->FlatPanel || (pScrn->depth != 24))
670         pNv->FPDither = FALSE;
671 }
672