port "G80: Reduce load detection false positives." from xf86-video-nv commit d483ecd8...
[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 static void
34 NV50DacSetPClk(xf86OutputPtr output, int pclk)
35 {
36         NV50OutputWrite(output, 0x4280, 0);
37 }
38
39 static void
40 NV50DacDPMSSet(xf86OutputPtr output, int mode)
41 {
42         CARD32 tmp;
43
44         /*
45          * DPMSModeOn       everything on
46          * DPMSModeStandby  hsync disabled, vsync enabled
47          * DPMSModeSuspend  hsync enabled, vsync disabled
48          * DPMSModeOff      sync disabled
49          */
50         while(NV50OutputRead(output, 0xa004) & 0x80000000);
51
52         tmp = NV50OutputRead(output, 0xa004);
53         tmp &= ~0x7f;
54         tmp |= 0x80000000;
55
56         if(mode == DPMSModeStandby || mode == DPMSModeOff)
57                 tmp |= 1;
58         if(mode == DPMSModeSuspend || mode == DPMSModeOff)
59                 tmp |= 4;
60         if(mode != DPMSModeOn)
61                 tmp |= 0x10;
62         if(mode == DPMSModeOff)
63                 tmp |= 0x40;
64
65         NV50OutputWrite(output, 0xa004, tmp);
66 }
67
68 Bool
69 NV50DacModeFixup(xf86OutputPtr output, DisplayModePtr mode,
70                  DisplayModePtr adjusted_mode)
71 {
72         return TRUE;
73 }
74
75 static void
76 NV50DacModeSet(xf86OutputPtr output, DisplayModePtr mode,
77                 DisplayModePtr adjusted_mode)
78 {
79         ScrnInfoPtr pScrn = output->scrn;
80         NV50OutputPrivPtr nv_output = output->driver_private;
81         const int dacOff = 0x80 * nv_output->or;
82
83         if(!adjusted_mode) {
84                 NV50DisplayCommand(pScrn, 0x400 + dacOff, 0);
85                 return;
86         }
87
88         // This wouldn't be necessary, but the server is stupid and calls
89         // NV50DacDPMSSet after the output is disconnected, even though the hardware
90         // turns it off automatically.
91         NV50DacDPMSSet(output, DPMSModeOn);
92
93         NV50DisplayCommand(pScrn, 0x400 + dacOff,
94                 (NV50CrtcGetHead(output->crtc) == HEAD0 ? 1 : 2) | 0x40);
95
96         NV50DisplayCommand(pScrn, 0x404 + dacOff,
97                 (adjusted_mode->Flags & V_NHSYNC) ? 1 : 0 |
98                 (adjusted_mode->Flags & V_NVSYNC) ? 2 : 0);
99
100         NV50CrtcSetScale(output->crtc, adjusted_mode, NV50_SCALE_OFF);
101 }
102
103 /*
104  * Perform DAC load detection to determine if there is a connected display.
105  */
106 static xf86OutputStatus
107 NV50DacDetect(xf86OutputPtr output)
108 {
109         NV50OutputPrivPtr nv_output = output->driver_private;
110
111         /* Assume physical status isn't going to change before the BlockHandler */
112         if(nv_output->cached_status != XF86OutputStatusUnknown)
113                 return nv_output->cached_status;
114
115         NV50OutputPartnersDetect(output, nv_output->partner, nv_output->i2c);
116         return nv_output->cached_status;
117 }
118
119 Bool
120 NV50DacLoadDetect(xf86OutputPtr output)
121 {
122         ScrnInfoPtr pScrn = output->scrn;
123         NVPtr pNv = NVPTR(pScrn);
124         NV50OutputPrivPtr nv_output = output->driver_private;
125         const int scrnIndex = pScrn->scrnIndex;
126         int sigstate;
127         CARD32 load, tmp, tmp2;
128
129         xf86DrvMsg(scrnIndex, X_PROBED, "Trying load detection on VGA%i ... ",
130                 nv_output->or);
131
132         NV50OutputWrite(output, 0xa010, 0x00000001);
133         tmp2 = NV50OutputRead(output, 0xa004);
134
135         NV50OutputWrite(output, 0xa004, 0x80150000);
136         while(NV50OutputRead(output, 0xa004) & 0x80000000);
137         tmp = (pNv->NVArch == 0x50) ? 420 : 340;
138         NV50OutputWrite(output, 0xa00c, tmp | 0x100000);
139         sigstate = xf86BlockSIGIO();
140         usleep(45000);
141         xf86UnblockSIGIO(sigstate);
142         load = NV50OutputRead(output, 0xa00c);
143         NV50OutputWrite(output, 0xa00c, 0);
144         NV50OutputWrite(output, 0xa004, 0x80000000 | tmp2);
145
146         // Use this DAC if all three channels show load.
147         if((load & 0x38000000) == 0x38000000) {
148                 xf86ErrorF("found one!\n");
149                 return TRUE;
150         }
151
152         xf86ErrorF("nothing.\n");
153         return FALSE;
154 }
155
156 static void
157 NV50DacDestroy(xf86OutputPtr output)
158 {
159         NV50OutputDestroy(output);
160
161         xfree(output->driver_private);
162         output->driver_private = NULL;
163 }
164
165 static const xf86OutputFuncsRec NV50DacOutputFuncs = {
166         .dpms = NV50DacDPMSSet,
167         .save = NULL,
168         .restore = NULL,
169         .mode_valid = NV50OutputModeValid,
170         .mode_fixup = NV50DacModeFixup,
171         .prepare = NV50OutputPrepare,
172         .commit = NV50OutputCommit,
173         .mode_set = NV50DacModeSet,
174         .detect = NV50DacDetect,
175         .get_modes = NV50OutputGetDDCModes,
176         .destroy = NV50DacDestroy,
177 };
178
179 xf86OutputPtr
180 NV50CreateDac(ScrnInfoPtr pScrn, ORNum or)
181 {
182         NV50OutputPrivPtr nv_output = xnfcalloc(sizeof(*nv_output), 1);
183         xf86OutputPtr output;
184         char orName[5];
185
186         if(!nv_output)
187                 return NULL;
188
189         snprintf(orName, 5, "VGA%i", or);
190         output = xf86OutputCreate(pScrn, &NV50DacOutputFuncs, orName);
191
192         nv_output->type = DAC;
193         nv_output->or = or;
194         nv_output->cached_status = XF86OutputStatusUnknown;
195         nv_output->set_pclk = NV50DacSetPClk;
196         output->driver_private = nv_output;
197         output->interlaceAllowed = TRUE;
198         output->doubleScanAllowed = TRUE;
199
200         return output;
201 }
202