NV50: Some more educated guesses at register names.
[nouveau] / src / nv50_dac.c
1 /*
2  * Copyright (c) 2007 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 #include <unistd.h>
25
26 #define DPMS_SERVER
27 #include <X11/extensions/dpms.h>
28
29 #include "nv_include.h"
30 #include "nv50_display.h"
31 #include "nv50_output.h"
32
33 void
34 NV50DacSetPClk(xf86OutputPtr output, int pclk)
35 {
36         NVOutputPrivatePtr nv_output = output->driver_private;
37         ScrnInfoPtr pScrn = output->scrn;
38         NVPtr pNv = NVPTR(pScrn);
39         NVWrite(pNv, 0x00614280 + nv_output->output_resource * 0x800, 0);
40 }
41
42 static void
43 NV50DacDPMSSet(xf86OutputPtr output, int mode)
44 {
45         CARD32 tmp;
46         NVOutputPrivatePtr nv_output = output->driver_private;
47         ScrnInfoPtr pScrn = output->scrn;
48         NVPtr pNv = NVPTR(pScrn);
49
50         /*
51          * DPMSModeOn       everything on
52          * DPMSModeStandby  hsync disabled, vsync enabled
53          * DPMSModeSuspend  hsync enabled, vsync disabled
54          * DPMSModeOff      sync disabled
55          */
56         while(NVRead(pNv, 0x0061a004 + nv_output->output_resource * 0x800) & 0x80000000);
57
58         tmp = NVRead(pNv, 0x0061a004 + nv_output->output_resource * 0x800);
59         tmp &= ~0x7f;
60         tmp |= 0x80000000;
61
62         if(mode == DPMSModeStandby || mode == DPMSModeOff)
63                 tmp |= 1;
64         if(mode == DPMSModeSuspend || mode == DPMSModeOff)
65                 tmp |= 4;
66         if(mode != DPMSModeOn)
67                 tmp |= 0x10;
68         if(mode == DPMSModeOff)
69                 tmp |= 0x40;
70
71         NVWrite(pNv, 0x0061a004 + nv_output->output_resource * 0x800, tmp);
72 }
73
74 Bool
75 NV50DacModeFixup(xf86OutputPtr output, DisplayModePtr mode,
76                  DisplayModePtr adjusted_mode)
77 {
78         return TRUE;
79 }
80
81 static void
82 NV50DacModeSet(xf86OutputPtr output, DisplayModePtr mode,
83                 DisplayModePtr adjusted_mode)
84 {
85         ScrnInfoPtr pScrn = output->scrn;
86         NVOutputPrivatePtr nv_output = output->driver_private;
87         const int dacOff = 0x80 * nv_output->output_resource;
88
89         if(!adjusted_mode) {
90                 NV50DisplayCommand(pScrn, 0x400 + dacOff, 0);
91                 return;
92         }
93
94         // This wouldn't be necessary, but the server is stupid and calls
95         // NV50DacDPMSSet after the output is disconnected, even though the hardware
96         // turns it off automatically.
97         NV50DacDPMSSet(output, DPMSModeOn);
98
99         NV50DisplayCommand(pScrn, 0x400 + dacOff,
100                 (NV50CrtcGetHead(output->crtc) == HEAD0 ? 1 : 2) | 0x40);
101
102         NV50DisplayCommand(pScrn, 0x404 + dacOff,
103                 (adjusted_mode->Flags & V_NHSYNC) ? 1 : 0 |
104                 (adjusted_mode->Flags & V_NVSYNC) ? 2 : 0);
105
106         NV50CrtcSetScale(output->crtc, adjusted_mode, SCALE_PANEL);
107 }
108
109 /*
110  * Perform DAC load detection to determine if there is a connected display.
111  */
112 static xf86OutputStatus
113 NV50DacDetect(xf86OutputPtr output)
114 {
115         NVOutputPrivatePtr nv_output = output->driver_private;
116         xf86MonPtr ddc_mon;
117
118         if (nv_output->pDDCBus == NULL)
119                 return XF86OutputStatusDisconnected;
120
121         ddc_mon = xf86OutputGetEDID(output, nv_output->pDDCBus);
122         if (!ddc_mon && !NV50DacLoadDetect(output))
123                 return XF86OutputStatusDisconnected;
124
125         if (ddc_mon && ddc_mon->features.input_type) /* DVI? */
126                 return XF86OutputStatusDisconnected;
127
128         if (ddc_mon)
129                 xf86OutputSetEDID(output, ddc_mon);
130
131         return XF86OutputStatusConnected;
132 }
133
134 Bool
135 NV50DacLoadDetect(xf86OutputPtr output)
136 {
137         ScrnInfoPtr pScrn = output->scrn;
138         NVPtr pNv = NVPTR(pScrn);
139         NVOutputPrivatePtr nv_output = output->driver_private;
140         const int scrnIndex = pScrn->scrnIndex;
141         int sigstate;
142         CARD32 load, tmp, tmp2;
143
144         xf86DrvMsg(scrnIndex, X_PROBED, "Trying load detection on VGA%i ... ",
145                 nv_output->output_resource);
146
147         NVWrite(pNv, 0x0061a010 + nv_output->output_resource * 0x800, 0x00000001);
148         tmp2 = NVRead(pNv, 0x0061a004 + nv_output->output_resource * 0x800);
149
150         NVWrite(pNv, 0x0061a004 + nv_output->output_resource * 0x800, 0x80150000);
151         while(NVRead(pNv, 0x0061a004 + nv_output->output_resource * 0x800) & 0x80000000);
152         tmp = (pNv->NVArch == 0x50) ? 420 : 340;
153         NVWrite(pNv, 0x0061a00c + nv_output->output_resource * 0x800, tmp | 0x100000);
154         sigstate = xf86BlockSIGIO();
155         usleep(45000);
156         xf86UnblockSIGIO(sigstate);
157         load = NVRead(pNv, 0x0061a00c + nv_output->output_resource * 0x800);
158         NVWrite(pNv, 0x0061a00c + nv_output->output_resource * 0x800, 0);
159         NVWrite(pNv, 0x0061a004 + nv_output->output_resource * 0x800, 0x80000000 | tmp2);
160
161         // Use this DAC if all three channels show load.
162         if((load & 0x38000000) == 0x38000000) {
163                 xf86ErrorF("found one!\n");
164                 return TRUE;
165         }
166
167         xf86ErrorF("nothing.\n");
168         return FALSE;
169 }
170
171 static void
172 NV50DacDestroy(xf86OutputPtr output)
173 {
174         NV50OutputDestroy(output);
175
176         xfree(output->driver_private);
177         output->driver_private = NULL;
178 }
179
180 static const xf86OutputFuncsRec NV50DacOutputFuncs = {
181         .dpms = NV50DacDPMSSet,
182         .save = NULL,
183         .restore = NULL,
184         .mode_valid = NV50OutputModeValid,
185         .mode_fixup = NV50DacModeFixup,
186         .prepare = NV50OutputPrepare,
187         .commit = NV50OutputCommit,
188         .mode_set = NV50DacModeSet,
189         .detect = NV50DacDetect,
190         .get_modes = NV50OutputGetDDCModes,
191         .destroy = NV50DacDestroy,
192 };
193
194 const xf86OutputFuncsRec * nv50_get_analog_output_funcs()
195 {
196         return &NV50DacOutputFuncs;
197 }
198