Fix minor issue.
[nouveau] / src / nv_output.c
1 /*
2  * Copyright 2006 Dave Airlie
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *  Dave Airlie
25  */
26 /*
27  * this code uses ideas taken from the NVIDIA nv driver - the nvidia license
28  * decleration is at the bottom of this file as it is rather ugly 
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include "xf86.h"
36 #include "os.h"
37 #include "mibank.h"
38 #include "globals.h"
39 #include "xf86.h"
40 #include "xf86Priv.h"
41 #include "xf86DDC.h"
42 #include "mipointer.h"
43 #include "windowstr.h"
44 #include <randrstr.h>
45 #include <X11/extensions/render.h>
46
47 #include "xf86Crtc.h"
48 #include "nv_include.h"
49
50 const char *OutputType[] = {
51     "None",
52     "VGA",
53     "DVI",
54     "LVDS",
55     "S-video",
56     "Composite",
57 };
58
59 const char *MonTypeName[7] = {
60     "AUTO",
61     "NONE",
62     "CRT",
63     "LVDS",
64     "TMDS",
65     "CTV",
66     "STV"
67 };
68
69 void NVOutputWriteRAMDAC(xf86OutputPtr output, CARD32 ramdac_reg, CARD32 val)
70 {
71     NVOutputPrivatePtr nv_output = output->driver_private;
72     ScrnInfoPtr pScrn = output->scrn;
73     NVPtr pNv = NVPTR(pScrn);
74
75     nvWriteRAMDAC(pNv, nv_output->ramdac, ramdac_reg, val);
76 }
77
78 CARD32 NVOutputReadRAMDAC(xf86OutputPtr output, CARD32 ramdac_reg)
79 {
80     NVOutputPrivatePtr nv_output = output->driver_private;
81     ScrnInfoPtr pScrn = output->scrn;
82     NVPtr pNv = NVPTR(pScrn);
83
84     return nvReadRAMDAC(pNv, nv_output->ramdac, ramdac_reg);
85 }
86
87 static void nv_output_backlight_enable(xf86OutputPtr output,  Bool on)
88 {
89     ScrnInfoPtr pScrn = output->scrn;
90     NVPtr pNv = NVPTR(pScrn);   
91
92     /* This is done differently on each laptop.  Here we
93        define the ones we know for sure. */
94     
95 #if defined(__powerpc__)
96     if((pNv->Chipset == 0x10DE0179) || 
97        (pNv->Chipset == 0x10DE0189) || 
98        (pNv->Chipset == 0x10DE0329))
99     {
100         /* NV17,18,34 Apple iMac, iBook, PowerBook */
101         CARD32 tmp_pmc, tmp_pcrt;
102         tmp_pmc = nvReadMC(pNv, 0x10F0) & 0x7FFFFFFF;
103         tmp_pcrt = nvReadCRTC0(pNv, NV_CRTC_081C) & 0xFFFFFFFC;
104         if(on) {
105             tmp_pmc |= (1 << 31);
106             tmp_pcrt |= 0x1;
107         }
108         nvWriteMC(pNv, 0x10F0, tmp_pmc);
109         nvWriteCRTC0(pNv, NV_CRTC_081C, tmp_pcrt);
110     }
111 #endif
112     
113     if(pNv->twoHeads && ((pNv->Chipset & 0x0ff0) != CHIPSET_NV11))
114         nvWriteMC(pNv, 0x130C, on ? 3 : 7);
115 }
116
117 static void
118 nv_panel_output_dpms(xf86OutputPtr output, int mode)
119 {
120
121     switch(mode) {
122     case DPMSModeStandby:
123     case DPMSModeSuspend:
124     case DPMSModeOff:
125         nv_output_backlight_enable(output, 0);
126         break;
127     case DPMSModeOn:
128         nv_output_backlight_enable(output, 1);
129     default:
130         break;
131     }
132 }
133
134 static void
135 nv_analog_output_dpms(xf86OutputPtr output, int mode)
136 {
137
138 }
139
140 static void
141 nv_digital_output_dpms(xf86OutputPtr output, int mode)
142 {
143         xf86CrtcPtr crtc = output->crtc;
144
145         if (crtc) {
146                 NVPtr pNv = NVPTR(output->scrn);
147                 NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
148
149                 CARD32 fpcontrol = nvReadRAMDAC(pNv, nv_crtc->crtc, NV_RAMDAC_FP_CONTROL);      
150                 switch(mode) {
151                         case DPMSModeStandby:
152                         case DPMSModeSuspend:
153                         case DPMSModeOff:
154                                 /* cut the TMDS output */           
155                                 fpcontrol |= 0x20000022;
156                                 break;
157                         case DPMSModeOn:
158                                 /* disable cutting the TMDS output */
159                                 fpcontrol &= ~0x20000022;
160                                 break;
161                 }
162                 nvWriteRAMDAC(pNv, nv_crtc->crtc, NV_RAMDAC_FP_CONTROL, fpcontrol);
163         }
164 }
165
166 void nv_output_save_state_ext(xf86OutputPtr output, RIVA_HW_STATE *state)
167 {
168     NVOutputPrivatePtr nv_output = output->driver_private;
169     ScrnInfoPtr pScrn = output->scrn;
170     NVPtr pNv = NVPTR(pScrn);
171     NVOutputRegPtr regp;
172
173     regp = &state->dac_reg[nv_output->ramdac];
174     regp->general       = NVOutputReadRAMDAC(output, NV_RAMDAC_GENERAL_CONTROL);
175     regp->fp_control    = NVOutputReadRAMDAC(output, NV_RAMDAC_FP_CONTROL);
176     regp->debug_0       = NVOutputReadRAMDAC(output, NV_RAMDAC_FP_DEBUG_0);
177     state->config       = nvReadFB(pNv, NV_PFB_CFG0);
178     
179     regp->output = NVOutputReadRAMDAC(output, NV_RAMDAC_OUTPUT);
180     
181     if((pNv->Chipset & 0x0ff0) == CHIPSET_NV11) {
182         regp->dither = NVOutputReadRAMDAC(output, NV_RAMDAC_DITHER_NV11);
183     } else if(pNv->twoHeads) {
184         regp->dither = NVOutputReadRAMDAC(output, NV_RAMDAC_FP_DITHER);
185     }
186     //    regp->crtcSync = NVOutputReadRAMDAC(output, NV_RAMDAC_FP_HCRTC);
187     regp->nv10_cursync = NVOutputReadRAMDAC(output, NV_RAMDAC_NV10_CURSYNC);
188
189     if (nv_output->type == OUTPUT_DIGITAL) {
190         int i;
191
192         for (i = 0; i < 7; i++) {
193             uint32_t ramdac_reg = NV_RAMDAC_FP_HDISP_END + (i * 4);
194             
195             regp->fp_horiz_regs[i] = NVOutputReadRAMDAC(output, ramdac_reg);
196         }
197         
198         for (i = 0; i < 7; i++) {
199             uint32_t ramdac_reg = NV_RAMDAC_FP_VDISP_END + (i * 4);
200             
201             regp->fp_vert_regs[i] = NVOutputReadRAMDAC(output, ramdac_reg);
202         }
203     }
204
205 }
206
207 void nv_output_load_state_ext(xf86OutputPtr output, RIVA_HW_STATE *state)
208 {
209     NVOutputPrivatePtr nv_output = output->driver_private;
210     ScrnInfoPtr pScrn = output->scrn;
211     NVPtr pNv = NVPTR(pScrn);
212     NVOutputRegPtr regp;
213   
214     regp = &state->dac_reg[nv_output->ramdac];
215   
216     NVOutputWriteRAMDAC(output, NV_RAMDAC_FP_DEBUG_0, regp->debug_0);
217     NVOutputWriteRAMDAC(output, NV_RAMDAC_OUTPUT, regp->output);
218     NVOutputWriteRAMDAC(output, NV_RAMDAC_FP_CONTROL, regp->fp_control);
219     //    NVOutputWriteRAMDAC(output, NV_RAMDAC_FP_HCRTC, regp->crtcSync);
220   
221
222     if((pNv->Chipset & 0x0ff0) == CHIPSET_NV11) {
223         NVOutputWriteRAMDAC(output, NV_RAMDAC_DITHER_NV11, regp->dither);
224     } else if(pNv->twoHeads) {
225         NVOutputWriteRAMDAC(output, NV_RAMDAC_FP_DITHER, regp->dither);
226     }
227   
228     NVOutputWriteRAMDAC(output, NV_RAMDAC_GENERAL_CONTROL, regp->general);
229     NVOutputWriteRAMDAC(output, NV_RAMDAC_NV10_CURSYNC, regp->nv10_cursync);
230
231     if (nv_output->type == OUTPUT_DIGITAL) {
232         int i;
233
234         for (i = 0; i < 7; i++) {
235             uint32_t ramdac_reg = NV_RAMDAC_FP_HDISP_END + (i * 4);
236             NVOutputWriteRAMDAC(output, ramdac_reg, regp->fp_horiz_regs[i]);
237         }
238         
239         for (i = 0; i < 7; i++) {
240             uint32_t ramdac_reg = NV_RAMDAC_FP_VDISP_END + (i * 4);
241             
242             NVOutputWriteRAMDAC(output, ramdac_reg, regp->fp_vert_regs[i]);
243         }
244     }
245
246 }
247
248
249 static void
250 nv_output_save (xf86OutputPtr output)
251 {
252     ScrnInfoPtr pScrn = output->scrn;
253     NVPtr pNv = NVPTR(pScrn);
254     RIVA_HW_STATE *state;
255   
256     state = &pNv->SavedReg;
257   
258     nv_output_save_state_ext(output, state);    
259   
260 }
261
262 static void
263 nv_output_restore (xf86OutputPtr output)
264 {
265     ScrnInfoPtr pScrn = output->scrn;
266     NVPtr pNv = NVPTR(pScrn);
267     RIVA_HW_STATE *state;
268   
269     state = &pNv->SavedReg;
270   
271     nv_output_load_state_ext(output, state);
272 }
273
274 static int
275 nv_output_mode_valid(xf86OutputPtr output, DisplayModePtr pMode)
276 {
277     if (pMode->Flags & V_DBLSCAN)
278         return MODE_NO_DBLESCAN;
279   
280     if (pMode->Clock > 400000 || pMode->Clock < 25000)
281         return MODE_CLOCK_RANGE;
282   
283     return MODE_OK;
284 }
285
286
287 static Bool
288 nv_output_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
289                      DisplayModePtr adjusted_mode)
290 {
291     return TRUE;
292 }
293
294 static int
295 nv_output_tweak_panel(xf86OutputPtr output, NVRegPtr state)
296 {
297     NVOutputPrivatePtr nv_output = output->driver_private;
298     ScrnInfoPtr pScrn = output->scrn;
299     NVPtr pNv = NVPTR(pScrn);
300     NVOutputRegPtr regp;
301     int tweak = 0;
302   
303     regp = &state->dac_reg[nv_output->ramdac];
304     if (pNv->usePanelTweak) {
305         tweak = pNv->PanelTweak;
306     } else {
307         /* begin flat panel hacks */
308         /* This is unfortunate, but some chips need this register
309            tweaked or else you get artifacts where adjacent pixels are
310            swapped.  There are no hard rules for what to set here so all
311            we can do is experiment and apply hacks. */
312     
313         if(((pNv->Chipset & 0xffff) == 0x0328) && (regp->bpp == 32)) {
314             /* At least one NV34 laptop needs this workaround. */
315             tweak = -1;
316         }
317                 
318         if((pNv->Chipset & 0xfff0) == CHIPSET_NV31) {
319             tweak = 1;
320         }
321         /* end flat panel hacks */
322     }
323     return tweak;
324 }
325
326 static void
327 nv_output_mode_set_regs(xf86OutputPtr output, DisplayModePtr mode)
328 {
329     NVOutputPrivatePtr nv_output = output->driver_private;
330     ScrnInfoPtr pScrn = output->scrn;
331     int bpp;
332     NVPtr pNv = NVPTR(pScrn);
333     NVFBLayout *pLayout = &pNv->CurrentLayout;
334     RIVA_HW_STATE *state, *sv_state;
335     Bool is_fp = FALSE;
336     NVOutputRegPtr regp, savep;
337     xf86CrtcConfigPtr   config = XF86_CRTC_CONFIG_PTR(pScrn);
338     int i;
339
340     state = &pNv->ModeReg;
341     regp = &state->dac_reg[nv_output->ramdac];
342
343     sv_state = &pNv->SavedReg;
344     savep = &sv_state->dac_reg[nv_output->ramdac];
345             
346     if ((nv_output->type == OUTPUT_PANEL) || (nv_output->type == OUTPUT_DIGITAL))
347     {
348         is_fp = TRUE;
349
350         for (i = 0; i < 7; i++) {
351             regp->fp_horiz_regs[i] = savep->fp_horiz_regs[i];
352             regp->fp_vert_regs[i] = savep->fp_vert_regs[i];
353         }
354
355         regp->fp_horiz_regs[REG_DISP_END] = mode->CrtcHDisplay - 1;
356         regp->fp_horiz_regs[REG_DISP_TOTAL] = mode->CrtcHTotal - 1;
357         regp->fp_horiz_regs[REG_DISP_CRTC] = mode->CrtcHDisplay;
358         regp->fp_horiz_regs[REG_DISP_SYNC_START] = mode->CrtcHSyncStart - 1;
359         regp->fp_horiz_regs[REG_DISP_SYNC_END] = mode->CrtcHSyncEnd - 1;
360         regp->fp_horiz_regs[REG_DISP_VALID_START] = mode->CrtcHSkew;
361         regp->fp_horiz_regs[REG_DISP_VALID_END] = mode->CrtcHDisplay - 1;
362         
363         regp->fp_vert_regs[REG_DISP_END] = mode->CrtcVDisplay - 1;
364         regp->fp_vert_regs[REG_DISP_TOTAL] = mode->CrtcVTotal - 1;
365         regp->fp_vert_regs[REG_DISP_CRTC] = mode->CrtcVDisplay;
366         regp->fp_vert_regs[REG_DISP_SYNC_START] = mode->CrtcVSyncStart - 1;
367         regp->fp_vert_regs[REG_DISP_SYNC_END] = mode->CrtcVSyncEnd - 1;
368         regp->fp_vert_regs[REG_DISP_VALID_START] = 0;
369         regp->fp_vert_regs[REG_DISP_VALID_END] = mode->CrtcVDisplay - 1;
370     
371     }
372
373     if (pNv->Architecture >= NV_ARCH_10) 
374         regp->nv10_cursync = savep->nv10_cursync | (1<<25);
375
376     regp->bpp    = bpp;    /* this is not bitsPerPixel, it's 8,15,16,32 */
377
378     regp->debug_0 = savep->debug_0;
379     regp->fp_control = savep->fp_control & 0xfff000ff;
380     if(is_fp == 1) {
381         if(!pNv->fpScaler || (nv_output->fpWidth <= mode->HDisplay)
382            || (nv_output->fpHeight <= mode->VDisplay))
383         {
384             regp->fp_control |= (1 << 8) ;
385         }
386         regp->crtcSync = savep->crtcSync;
387         regp->crtcSync += nv_output_tweak_panel(output, state);
388
389         regp->debug_0 &= ~NV_RAMDAC_FP_DEBUG_0_PWRDOWN_BOTH;
390     }
391     else
392         regp->debug_0 |= NV_RAMDAC_FP_DEBUG_0_PWRDOWN_BOTH;
393
394     ErrorF("output %d debug_0 %08X\n", nv_output->ramdac, regp->debug_0);
395
396     if(pNv->twoHeads) {
397         if((pNv->Chipset & 0x0ff0) == CHIPSET_NV11) {
398             regp->dither = savep->dither & ~0x00010000;
399             if(pNv->FPDither)
400                 regp->dither |= 0x00010000;
401         } else {
402             ErrorF("savep->dither %08X\n", savep->dither);
403             regp->dither = savep->dither & ~1;
404             if(pNv->FPDither)
405                 regp->dither |= 1;
406         } 
407     }
408
409     if(pLayout->depth < 24) 
410         bpp = pLayout->depth;
411     else bpp = 32;    
412
413     regp->general  = bpp == 16 ? 0x00101100 : 0x00100100;
414
415     if (pNv->alphaCursor)
416         regp->general |= (1<<29);
417
418     if(bpp != 8) /* DirectColor */
419         regp->general |= 0x00000030;
420
421     if (output->crtc) {
422         NVCrtcPrivatePtr nv_crtc = output->crtc->driver_private;
423         int two_crt = FALSE;
424         int two_mon = FALSE;
425
426         for (i = 0; i < config->num_output; i++) {
427             NVOutputPrivatePtr nv_output2 = config->output[i]->driver_private;
428
429             /* is it this output ?? */
430             if (config->output[i] == output)
431                 continue;
432
433             /* it the output connected */
434             if (config->output[i]->crtc == NULL)
435                 continue;
436
437             two_mon = TRUE;
438             if ((nv_output2->type == OUTPUT_ANALOG) && (nv_output->type == OUTPUT_ANALOG))
439                 two_crt = TRUE;
440         }
441
442         if (is_fp == TRUE)
443             regp->output = 0x0;
444         else 
445             regp->output = NV_RAMDAC_OUTPUT_DAC_ENABLE;
446
447         if (nv_crtc->crtc == 1 && two_mon)
448           regp->output |= NV_RAMDAC_OUTPUT_SELECT_CRTC2;
449
450         ErrorF("%d: crtc %d output%d: %04X: twocrt %d twomon %d\n", is_fp, nv_crtc->crtc, nv_output->ramdac, regp->output, two_crt, two_mon);
451     }
452 }
453
454 static void
455 nv_output_mode_set(xf86OutputPtr output, DisplayModePtr mode,
456                    DisplayModePtr adjusted_mode)
457 {
458     ScrnInfoPtr pScrn = output->scrn;
459     NVPtr pNv = NVPTR(pScrn);
460     RIVA_HW_STATE *state;
461
462     state = &pNv->ModeReg;
463
464     nv_output_mode_set_regs(output, mode);
465     nv_output_load_state_ext(output, state);
466 }
467
468 static Bool
469 nv_ddc_detect(xf86OutputPtr output)
470 {
471     /* no use for shared DDC output */
472     NVOutputPrivatePtr nv_output = output->driver_private;
473     xf86MonPtr ddc_mon;
474
475     ddc_mon = xf86OutputGetEDID(output, nv_output->pDDCBus);
476     if (!ddc_mon)
477         return 0;
478
479     if (ddc_mon->features.input_type && (nv_output->type == OUTPUT_ANALOG))
480         return 0;
481
482     if ((!ddc_mon->features.input_type) && (nv_output->type == OUTPUT_DIGITAL))
483         return 0;
484
485     return 1;
486 }
487
488 static Bool
489 nv_crt_load_detect(xf86OutputPtr output)
490 {
491     ScrnInfoPtr pScrn = output->scrn;
492     CARD32 reg_output, reg_test_ctrl, temp;
493     int present = FALSE;
494           
495     reg_output = NVOutputReadRAMDAC(output, NV_RAMDAC_OUTPUT);
496     reg_test_ctrl = NVOutputReadRAMDAC(output, NV_RAMDAC_TEST_CONTROL);
497
498     NVOutputWriteRAMDAC(output, NV_RAMDAC_TEST_CONTROL, (reg_test_ctrl & ~0x00010000));
499     
500     NVOutputWriteRAMDAC(output, NV_RAMDAC_OUTPUT, (reg_output & 0x0000FEEE));
501     usleep(1000);
502           
503     temp = NVOutputReadRAMDAC(output, NV_RAMDAC_OUTPUT);
504     NVOutputWriteRAMDAC(output, NV_RAMDAC_OUTPUT, temp | 1);
505
506     NVOutputWriteRAMDAC(output, NV_RAMDAC_TEST_DATA, 0x94050140);
507     temp = NVOutputReadRAMDAC(output, NV_RAMDAC_TEST_CONTROL);
508     NVOutputWriteRAMDAC(output, NV_RAMDAC_TEST_CONTROL, temp | 0x1000);
509
510     usleep(1000);
511           
512     present = (NVOutputReadRAMDAC(output, NV_RAMDAC_TEST_CONTROL) & (1 << 28)) ? TRUE : FALSE;
513           
514     temp = NVOutputReadRAMDAC(output, NV_RAMDAC_TEST_CONTROL);
515     NVOutputWriteRAMDAC(output, NV_RAMDAC_TEST_CONTROL, temp & 0x000EFFF);
516           
517     NVOutputWriteRAMDAC(output, NV_RAMDAC_OUTPUT, reg_output);
518     NVOutputWriteRAMDAC(output, NV_RAMDAC_TEST_CONTROL, reg_test_ctrl);
519     
520     xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "CRT detect returned %d\n",
521                present);
522
523     return present;
524
525 }
526
527 static xf86OutputStatus
528 nv_digital_output_detect(xf86OutputPtr output)
529 {
530     NVOutputPrivatePtr nv_output = output->driver_private;
531
532     if (nv_ddc_detect(output))
533         return XF86OutputStatusConnected;
534
535     return XF86OutputStatusDisconnected;
536 }
537
538
539 static xf86OutputStatus
540 nv_analog_output_detect(xf86OutputPtr output)
541 {
542     NVOutputPrivatePtr nv_output = output->driver_private;
543
544     if (nv_ddc_detect(output))
545         return XF86OutputStatusConnected;
546
547     /* seems a bit flaky on ramdac 1 */
548     if ((nv_output->ramdac==0) && nv_crt_load_detect(output))
549         return XF86OutputStatusConnected;
550     
551     return XF86OutputStatusDisconnected;
552 }
553
554 static DisplayModePtr
555 nv_output_get_modes(xf86OutputPtr output)
556 {
557     ScrnInfoPtr pScrn = output->scrn;
558     NVOutputPrivatePtr nv_output = output->driver_private;
559     xf86MonPtr ddc_mon;
560     DisplayModePtr ddc_modes, mode;
561     int i;
562
563
564     ddc_mon = xf86OutputGetEDID(output, nv_output->pDDCBus);
565
566     if (ddc_mon == NULL) {
567         xf86OutputSetEDID(output, ddc_mon);
568         return NULL;
569     }
570
571     if (ddc_mon->features.input_type && (nv_output->type == OUTPUT_ANALOG)) {
572         xf86OutputSetEDID(output, NULL);
573         return NULL;
574     }
575
576     if ((!ddc_mon->features.input_type) && (nv_output->type == OUTPUT_DIGITAL)) {
577         xf86OutputSetEDID(output, NULL);
578         return NULL;
579     }
580
581     xf86OutputSetEDID(output, ddc_mon);
582
583     ddc_modes = xf86OutputGetEDIDModes (output);          
584     return ddc_modes;
585
586 }
587
588 static void
589 nv_output_destroy (xf86OutputPtr output)
590 {
591     if (output->driver_private)
592         xfree (output->driver_private);
593
594 }
595
596 static void
597 nv_output_prepare(xf86OutputPtr output)
598 {
599
600 }
601
602 static void
603 nv_output_commit(xf86OutputPtr output)
604 {
605
606
607 }
608
609 static const xf86OutputFuncsRec nv_analog_output_funcs = {
610     .dpms = nv_analog_output_dpms,
611     .save = nv_output_save,
612     .restore = nv_output_restore,
613     .mode_valid = nv_output_mode_valid,
614     .mode_fixup = nv_output_mode_fixup,
615     .mode_set = nv_output_mode_set,
616     .detect = nv_analog_output_detect,
617     .get_modes = nv_output_get_modes,
618     .destroy = nv_output_destroy,
619     .prepare = nv_output_prepare,
620     .commit = nv_output_commit,
621 };
622
623 static const xf86OutputFuncsRec nv_digital_output_funcs = {
624     .dpms = nv_digital_output_dpms,
625     .save = nv_output_save,
626     .restore = nv_output_restore,
627     .mode_valid = nv_output_mode_valid,
628     .mode_fixup = nv_output_mode_fixup,
629     .mode_set = nv_output_mode_set,
630     .detect = nv_digital_output_detect,
631     .get_modes = nv_output_get_modes,
632     .destroy = nv_output_destroy,
633     .prepare = nv_output_prepare,
634     .commit = nv_output_commit,
635 };
636
637 static xf86OutputStatus
638 nv_output_lvds_detect(xf86OutputPtr output)
639 {
640     return XF86OutputStatusUnknown;    
641 }
642
643 static DisplayModePtr
644 nv_output_lvds_get_modes(xf86OutputPtr output)
645 {
646     ScrnInfoPtr pScrn = output->scrn;
647     NVOutputPrivatePtr nv_output = output->driver_private;
648
649     //    nv_output->fpWidth = NVOutputReadRAMDAC(output, NV_RAMDAC_FP_HDISP_END) + 1;
650     //    nv_output->fpHeight = NVOutputReadRAMDAC(output, NV_RAMDAC_FP_VDISP_END) + 1;
651     nv_output->fpSyncs = NVOutputReadRAMDAC(output, NV_RAMDAC_FP_CONTROL) & 0x30000033;
652     //    xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Panel size is %i x %i\n",
653     //         nv_output->fpWidth, nv_output->fpHeight);
654
655     return NULL;
656
657 }
658
659 static const xf86OutputFuncsRec nv_lvds_output_funcs = {
660     .dpms = nv_panel_output_dpms,
661     .save = nv_output_save,
662     .restore = nv_output_restore,
663     .mode_valid = nv_output_mode_valid,
664     .mode_fixup = nv_output_mode_fixup,
665     .mode_set = nv_output_mode_set,
666     .detect = nv_output_lvds_detect,
667     .get_modes = nv_output_lvds_get_modes,
668     .destroy = nv_output_destroy,
669     .prepare = nv_output_prepare,
670     .commit = nv_output_commit,
671 };
672
673
674 static void nv_add_analog_output(ScrnInfoPtr pScrn, int i2c_index)
675 {
676   NVPtr pNv = NVPTR(pScrn);
677   xf86OutputPtr     output;
678   NVOutputPrivatePtr    nv_output;
679   char outputname[20];
680   int   crtc_mask = (1<<0) | (1<<1);
681
682   sprintf(outputname, "Analog-%d", pNv->analog_count);
683   output = xf86OutputCreate (pScrn, &nv_analog_output_funcs, outputname);
684   if (!output)
685     return;
686   nv_output = xnfcalloc (sizeof (NVOutputPrivateRec), 1);
687   if (!nv_output)
688   {
689     xf86OutputDestroy (output);
690     return;
691   }
692   
693   output->driver_private = nv_output;
694   nv_output->type = OUTPUT_ANALOG;
695
696   nv_output->ramdac = pNv->analog_count;
697
698   nv_output->pDDCBus = pNv->pI2CBus[i2c_index];
699
700   output->possible_crtcs = crtc_mask;
701   xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Adding output %s\n", outputname);
702   
703   pNv->analog_count++;
704 }
705
706
707 static void nv_add_digital_output(ScrnInfoPtr pScrn, int i2c_index, int lvds)
708 {
709   NVPtr pNv = NVPTR(pScrn);
710   xf86OutputPtr     output;
711   NVOutputPrivatePtr    nv_output;
712   char outputname[20];
713   int   crtc_mask = (1<<0) | (1<<1);
714
715   sprintf(outputname, "Digital-%d", pNv->digital_count);
716   if (lvds)
717     output = xf86OutputCreate (pScrn, &nv_lvds_output_funcs, outputname);
718   else
719     output = xf86OutputCreate (pScrn, &nv_digital_output_funcs, outputname);
720   if (!output)
721     return;
722   nv_output = xnfcalloc (sizeof (NVOutputPrivateRec), 1);
723   if (!nv_output)
724   {
725     xf86OutputDestroy (output);
726     return;
727   }
728   
729   output->driver_private = nv_output;
730   nv_output->type = OUTPUT_DIGITAL;
731   
732   nv_output->ramdac = pNv->digital_count;
733   
734   nv_output->pDDCBus = pNv->pI2CBus[i2c_index];
735   
736   output->possible_crtcs = crtc_mask;
737   xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Adding output %s\n", outputname);
738
739   pNv->digital_count++;
740 }
741 /**
742  * Set up the outputs according to what type of chip we are.
743  *
744  * Some outputs may not initialize, due to allocation failure or because a
745  * controller chip isn't found.
746  */
747
748 void Nv20SetupOutputs(ScrnInfoPtr pScrn)
749 {
750     NVPtr pNv = NVPTR(pScrn);
751     xf86OutputPtr           output;
752     NVOutputPrivatePtr    nv_output;
753     int i;
754     int num_analog_outputs = pNv->twoHeads ? 2 : 1;
755     int num_digital_outputs = 1;
756
757     for (i = 0 ; i < num_analog_outputs; i++) {
758       nv_add_analog_output(pScrn, i);
759     }
760
761     for (i = 0 ; i < num_digital_outputs; i++) {
762       nv_add_digital_output(pScrn, i, 0);
763     }
764 }
765
766 void NvDCBSetupOutputs(ScrnInfoPtr pScrn)
767 {
768   unsigned char type, port, or;
769   NVPtr pNv = NVPTR(pScrn);
770   int i;
771
772   /* we setup the outputs up from the BIOS table */
773   if (pNv->dcb_entries) {
774     for (i = 0 ; i < pNv->dcb_entries; i++) {
775       type = pNv->dcb_table[i] & 0xf;
776       port = (pNv->dcb_table[i] >> 4) & 0xf;
777       or = ffs((pNv->dcb_table[i] >> 24) & 0xf) - 1;
778      
779       if (type < 4)
780         xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "DCB entry: %d: %08X type: %d, port %d:, or %d\n", i, pNv->dcb_table[i], type, port, or);
781       if (type < 4 && port != 0xf) {
782         switch(type) {
783         case 0: /* analog */
784           nv_add_analog_output(pScrn, port);
785           break;
786         case 2:
787           nv_add_digital_output(pScrn, port, 0);
788           break;
789         case 3:
790           nv_add_digital_output(pScrn, port, 1);
791           break;
792         default:
793           break;
794         }
795       }
796     }
797   } else
798     Nv20SetupOutputs(pScrn);
799
800 }
801
802 struct nv_i2c_struct {
803     int reg;
804     char *name;
805 } nv_i2c_buses[] = { 
806     { 0x36, "DDC1" },
807     { 0x3e, "DDC2" },
808     { 0x50, "TV" },
809 };
810
811
812 void NvSetupOutputs(ScrnInfoPtr pScrn)
813 {
814     int i;
815     NVPtr pNv = NVPTR(pScrn);
816     xf86OutputPtr           output;
817     NVOutputPrivatePtr    nv_output;
818
819     int num_outputs = pNv->twoHeads ? 2 : 1;
820     char outputname[20];
821     pNv->Television = FALSE;
822
823     /* add the 3 I2C buses */
824     for (i = 0; i < NV_I2C_BUSES; i++) {
825         NV_I2CInit(pScrn, &pNv->pI2CBus[i], nv_i2c_buses[i].reg, nv_i2c_buses[i].name);
826     }
827
828     NvDCBSetupOutputs(pScrn);
829
830 #if 0
831     if (pNv->Mobile) {
832         output = xf86OutputCreate(pScrn, &nv_output_funcs, OutputType[OUTPUT_LVDS]);
833         if (!output)
834             return;
835
836         nv_output = xnfcalloc(sizeof(NVOutputPrivateRec), 1);
837         if (!nv_output) {
838             xf86OutputDestroy(output);
839             return;
840         }
841
842         output->driver_private = nv_output;
843         nv_output->type = output_type;
844
845         output->possible_crtcs = i ? 1 : crtc_mask;
846     }
847 #endif
848 }
849
850
851 /*************************************************************************** \
852 |*                                                                           *|
853 |*       Copyright 1993-2003 NVIDIA, Corporation.  All rights reserved.      *|
854 |*                                                                           *|
855 |*     NOTICE TO USER:   The source code  is copyrighted under  U.S. and     *|
856 |*     international laws.  Users and possessors of this source code are     *|
857 |*     hereby granted a nonexclusive,  royalty-free copyright license to     *|
858 |*     use this code in individual and commercial software.                  *|
859 |*                                                                           *|
860 |*     Any use of this source code must include,  in the user documenta-     *|
861 |*     tion and  internal comments to the code,  notices to the end user     *|
862 |*     as follows:                                                           *|
863 |*                                                                           *|
864 |*       Copyright 1993-1999 NVIDIA, Corporation.  All rights reserved.      *|
865 |*                                                                           *|
866 |*     NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY     *|
867 |*     OF  THIS SOURCE  CODE  FOR ANY PURPOSE.  IT IS  PROVIDED  "AS IS"     *|
868 |*     WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND.  NVIDIA, CORPOR-     *|
869 |*     ATION DISCLAIMS ALL WARRANTIES  WITH REGARD  TO THIS SOURCE CODE,     *|
870 |*     INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE-     *|
871 |*     MENT,  AND FITNESS  FOR A PARTICULAR PURPOSE.   IN NO EVENT SHALL     *|
872 |*     NVIDIA, CORPORATION  BE LIABLE FOR ANY SPECIAL,  INDIRECT,  INCI-     *|
873 |*     DENTAL, OR CONSEQUENTIAL DAMAGES,  OR ANY DAMAGES  WHATSOEVER RE-     *|
874 |*     SULTING FROM LOSS OF USE,  DATA OR PROFITS,  WHETHER IN AN ACTION     *|
875 |*     OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  ARISING OUT OF     *|
876 |*     OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE.     *|
877 |*                                                                           *|
878 |*     U.S. Government  End  Users.   This source code  is a "commercial     *|
879 |*     item,"  as that  term is  defined at  48 C.F.R. 2.101 (OCT 1995),     *|
880 |*     consisting  of "commercial  computer  software"  and  "commercial     *|
881 |*     computer  software  documentation,"  as such  terms  are  used in     *|
882 |*     48 C.F.R. 12.212 (SEPT 1995)  and is provided to the U.S. Govern-     *|
883 |*     ment only as  a commercial end item.   Consistent with  48 C.F.R.     *|
884 |*     12.212 and  48 C.F.R. 227.7202-1 through  227.7202-4 (JUNE 1995),     *|
885 |*     all U.S. Government End Users  acquire the source code  with only     *|
886 |*     those rights set forth herein.                                        *|
887 |*                                                                           *|
888  \***************************************************************************/