randr12: Revert dither value to that used by nv.
[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         CARD32 load, tmp, tmp2;
127
128         xf86DrvMsg(scrnIndex, X_PROBED, "Trying load detection on VGA%i ... ",
129                 nv_output->or);
130
131         NV50OutputWrite(output, 0xa010, 0x00000001);
132         tmp2 = NV50OutputRead(output, 0xa004);
133
134         NV50OutputWrite(output, 0xa004, 0x80150000);
135         while(NV50OutputRead(output, 0xa004) & 0x80000000);
136         tmp = (pNv->NVArch == 0x50) ? 420 : 340;
137         NV50OutputWrite(output, 0xa00c, tmp | 0x100000);
138         usleep(4500);
139         load = NV50OutputRead(output, 0xa00c);
140         NV50OutputWrite(output, 0xa00c, 0);
141         NV50OutputWrite(output, 0xa004, 0x80000000 | tmp2);
142
143         // Use this DAC if all three channels show load.
144         if((load & 0x38000000) == 0x38000000) {
145                 xf86ErrorF("found one!\n");
146                 return TRUE;
147         }
148
149         xf86ErrorF("nothing.\n");
150         return FALSE;
151 }
152
153 static void
154 NV50DacDestroy(xf86OutputPtr output)
155 {
156         NV50OutputDestroy(output);
157
158         xfree(output->driver_private);
159         output->driver_private = NULL;
160 }
161
162 static const xf86OutputFuncsRec NV50DacOutputFuncs = {
163         .dpms = NV50DacDPMSSet,
164         .save = NULL,
165         .restore = NULL,
166         .mode_valid = NV50OutputModeValid,
167         .mode_fixup = NV50DacModeFixup,
168         .prepare = NV50OutputPrepare,
169         .commit = NV50OutputCommit,
170         .mode_set = NV50DacModeSet,
171         .detect = NV50DacDetect,
172         .get_modes = NV50OutputGetDDCModes,
173         .destroy = NV50DacDestroy,
174 };
175
176 xf86OutputPtr
177 NV50CreateDac(ScrnInfoPtr pScrn, ORNum or)
178 {
179         NV50OutputPrivPtr nv_output = xnfcalloc(sizeof(*nv_output), 1);
180         xf86OutputPtr output;
181         char orName[5];
182
183         if(!nv_output)
184                 return FALSE;
185
186         snprintf(orName, 5, "VGA%i", or);
187         output = xf86OutputCreate(pScrn, &NV50DacOutputFuncs, orName);
188
189         nv_output->type = DAC;
190         nv_output->or = or;
191         nv_output->cached_status = XF86OutputStatusUnknown;
192         nv_output->set_pclk = NV50DacSetPClk;
193         output->driver_private = nv_output;
194         output->interlaceAllowed = TRUE;
195         output->doubleScanAllowed = TRUE;
196
197         return output;
198 }
199