NV50: Add some debugging statements to important functions.
[nouveau] / src / nv50_sor.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 #define DPMS_SERVER
25 #include <X11/extensions/dpms.h>
26 #include <X11/Xatom.h>
27
28 #include "nv_include.h"
29 #include "nv50_type.h"
30 #include "nv50_display.h"
31 #include "nv50_output.h"
32
33 void
34 NV50SorSetPClk(xf86OutputPtr output, int pclk)
35 {
36         ScrnInfoPtr pScrn = output->scrn;
37         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorSetPClk is called.\n");
38
39         NVPtr pNv = NVPTR(pScrn);
40         const int limit = 165000;
41
42         /* 0x70000 was a late addition to nv, mentioned as fixing tmds initialisation on certain gpu's. */
43         /* This *may* have solved my shaking image problem, but i am not sure. */
44         /* I presume it's some kind of clock setting, but what precisely i do not know. */
45         NVWrite(pNv, NV50_SOR0_CLK_CTRL + NV50OrOffset(output) * 0x800, 0x70000 | ((pclk > limit) ? 0x101 : 0));
46 }
47
48 static void
49 NV50SorDPMSSet(xf86OutputPtr output, int mode)
50 {
51         ScrnInfoPtr pScrn = output->scrn;
52         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorDPMSSet is called with mode %d.\n", mode);
53
54         NVPtr pNv = NVPTR(pScrn);
55         CARD32 tmp;
56
57         while((NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800) & NV50_SOR_DPMS_CTRL_PENDING));
58
59         tmp = NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
60         tmp |= NV50_SOR_DPMS_CTRL_PENDING;
61
62         if(mode == DPMSModeOn)
63                 tmp |= NV50_SOR_DPMS_CTRL_MODE_ON;
64         else
65                 tmp &= ~NV50_SOR_DPMS_CTRL_MODE_ON;
66
67         NVWrite(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800, tmp);
68         while((NVRead(pNv, 0x0061c030 + NV50OrOffset(output) * 0x800) & 0x10000000));
69 }
70
71 static int
72 NV50TMDSModeValid(xf86OutputPtr output, DisplayModePtr mode)
73 {
74         NVOutputPrivatePtr nv_output = output->driver_private;
75
76         // Disable dual-link modes until I can find a way to make them work
77         // reliably.
78         if (mode->Clock > 165000)
79                 return MODE_CLOCK_HIGH;
80
81         if (mode->HDisplay > nv_output->fpWidth || mode->VDisplay > nv_output->fpHeight)
82                 return MODE_PANEL;
83
84         return NV50OutputModeValid(output, mode);
85 }
86
87 static int
88 NV50LVDSModeValid(xf86OutputPtr output, DisplayModePtr mode)
89 {
90         NVOutputPrivatePtr nv_output = output->driver_private;
91         DisplayModePtr native = nv_output->native_mode;
92
93         // Ignore modes larger than the native res.
94         if (mode->HDisplay > native->HDisplay || mode->VDisplay > native->VDisplay)
95                 return MODE_PANEL;
96
97         return NV50OutputModeValid(output, mode);
98 }
99
100 static void
101 NV50SorModeSet(xf86OutputPtr output, DisplayModePtr mode,
102                 DisplayModePtr adjusted_mode)
103 {
104         ScrnInfoPtr pScrn = output->scrn;
105         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorModeSet is called.\n");
106
107         NVOutputPrivatePtr nv_output = output->driver_private;
108         const int sorOff = 0x40 * NV50OrOffset(output);
109         uint32_t mode_ctl = NV50_SOR_MODE_CTRL_OFF;
110
111         if (!adjusted_mode) {
112                 /* Disconnect the SOR */
113                 NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
114                 return;
115         }
116
117         if (nv_output->type == OUTPUT_LVDS) {
118                 mode_ctl |= NV50_SOR_MODE_CTRL_LVDS;
119         } else {
120                 mode_ctl |= NV50_SOR_MODE_CTRL_TMDS;
121                 if (adjusted_mode->Clock > 165000)
122                         mode_ctl |= NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK;
123         }
124
125         if (output->crtc) {
126                 NVCrtcPrivatePtr nv_crtc = output->crtc->driver_private;
127                 if (nv_crtc->head == 1)
128                         mode_ctl |= NV50_SOR_MODE_CTRL_CRTC1;
129                 else
130                         mode_ctl |= NV50_SOR_MODE_CTRL_CRTC0;
131         } else {
132                 return;
133         }
134
135         if (adjusted_mode->Flags & V_NHSYNC)
136                 mode_ctl |= NV50_SOR_MODE_CTRL_NHSYNC;
137
138         if (adjusted_mode->Flags & V_NVSYNC)
139                 mode_ctl |= NV50_SOR_MODE_CTRL_NVSYNC;
140
141         // This wouldn't be necessary, but the server is stupid and calls
142         // NV50SorDPMSSet after the output is disconnected, even though the hardware
143         // turns it off automatically.
144         NV50SorDPMSSet(output, DPMSModeOn);
145
146         NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
147
148         NV50CrtcSetScale(output->crtc, mode, adjusted_mode, nv_output->scaling_mode);
149 }
150
151 static xf86OutputStatus
152 NV50SorDetect(xf86OutputPtr output)
153 {
154         ScrnInfoPtr pScrn = output->scrn;
155         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorDetect is called.\n");
156
157         NVOutputPrivatePtr nv_output = output->driver_private;
158         xf86MonPtr ddc_mon;
159
160         if (nv_output->pDDCBus == NULL)
161                 return XF86OutputStatusDisconnected;
162
163         ddc_mon = NV50OutputGetEDID(output, nv_output->pDDCBus);
164         if (!ddc_mon)
165                 return XF86OutputStatusDisconnected;
166
167         if (!ddc_mon->features.input_type) /* Analog? */
168                 return XF86OutputStatusDisconnected;
169
170         return XF86OutputStatusConnected;
171 }
172
173 static xf86OutputStatus
174 NV50SorLVDSDetect(xf86OutputPtr output)
175 {
176         ScrnInfoPtr pScrn = output->scrn;
177         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorLVDSDetect is called.\n");
178
179         /* Assume LVDS is always connected */
180         return XF86OutputStatusConnected;
181 }
182
183 static void
184 NV50SorDestroy(xf86OutputPtr output)
185 {
186         NVOutputPrivatePtr nv_output = output->driver_private;
187
188         NV50OutputDestroy(output);
189
190         xf86DeleteMode(&nv_output->native_mode, nv_output->native_mode);
191
192         xfree(output->driver_private);
193         output->driver_private = NULL;
194 }
195
196 static Bool
197 NV50SorModeFixup(xf86OutputPtr output, DisplayModePtr mode,
198                  DisplayModePtr adjusted_mode)
199 {
200         ScrnInfoPtr pScrn = output->scrn;
201         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorModeFixup is called.\n");
202
203         NVOutputPrivatePtr nv_output = output->driver_private;
204
205         if (nv_output->native_mode && nv_output->scaling_mode != SCALE_PANEL) {
206                 adjusted_mode->HDisplay = nv_output->native_mode->HDisplay;
207                 adjusted_mode->HSkew = nv_output->native_mode->HSkew;
208                 adjusted_mode->HSyncStart = nv_output->native_mode->HSyncStart;
209                 adjusted_mode->HSyncEnd = nv_output->native_mode->HSyncEnd;
210                 adjusted_mode->HTotal = nv_output->native_mode->HTotal;
211                 adjusted_mode->VDisplay = nv_output->native_mode->VDisplay;
212                 adjusted_mode->VScan = nv_output->native_mode->VScan;
213                 adjusted_mode->VSyncStart = nv_output->native_mode->VSyncStart;
214                 adjusted_mode->VSyncEnd = nv_output->native_mode->VSyncEnd;
215                 adjusted_mode->VTotal = nv_output->native_mode->VTotal;
216                 adjusted_mode->Clock = nv_output->native_mode->Clock;
217                 adjusted_mode->Flags = nv_output->native_mode->Flags;
218
219                 /* No INTERLACE_HALVE_V, because we manually correct. */
220                 xf86SetModeCrtc(adjusted_mode, 0);
221         }
222         return TRUE;
223 }
224
225 static DisplayModePtr
226 NV50SorGetLVDSModes(xf86OutputPtr output)
227 {
228         NVOutputPrivatePtr nv_output = output->driver_private;
229         return xf86DuplicateMode(nv_output->native_mode);
230 }
231
232 static const xf86OutputFuncsRec NV50SorTMDSOutputFuncs = {
233         .dpms = NV50SorDPMSSet,
234         .save = NULL,
235         .restore = NULL,
236         .mode_valid = NV50TMDSModeValid,
237         .mode_fixup = NV50SorModeFixup,
238         .prepare = NV50OutputPrepare,
239         .commit = NV50OutputCommit,
240         .mode_set = NV50SorModeSet,
241         .detect = NV50SorDetect,
242         .get_modes = NV50OutputGetDDCModes,
243         .create_resources = nv_digital_output_create_resources,
244         .set_property = nv_digital_output_set_property,
245         .destroy = NV50SorDestroy,
246 };
247
248 static const xf86OutputFuncsRec NV50SorLVDSOutputFuncs = {
249         .dpms = NV50SorDPMSSet,
250         .save = NULL,
251         .restore = NULL,
252         .mode_valid = NV50LVDSModeValid,
253         .mode_fixup = NV50SorModeFixup,
254         .prepare = NV50OutputPrepare,
255         .commit = NV50OutputCommit,
256         .mode_set = NV50SorModeSet,
257         .detect = NV50SorLVDSDetect,
258         .get_modes = NV50SorGetLVDSModes,
259         .create_resources = nv_digital_output_create_resources,
260         .set_property = nv_digital_output_set_property,
261         .destroy = NV50SorDestroy,
262 };
263
264 const xf86OutputFuncsRec * nv50_get_tmds_output_funcs()
265 {
266         return &NV50SorTMDSOutputFuncs;
267 }
268
269 const xf86OutputFuncsRec * nv50_get_lvds_output_funcs()
270 {
271         return &NV50SorLVDSOutputFuncs;
272 }
273
274 static DisplayModePtr
275 ReadLVDSNativeMode(ScrnInfoPtr pScrn, const int off)
276 {
277         NVPtr pNv = NVPTR(pScrn);
278         DisplayModePtr mode = xnfcalloc(1, sizeof(DisplayModeRec));
279         const CARD32 size = NVRead(pNv, 0x00610b4c + off);
280         const int width = size & 0x3fff;
281         const int height = (size >> 16) & 0x3fff;
282
283         mode->HDisplay = width;
284         mode->VDisplay = height;
285         mode->Clock = NVRead(pNv, 0x00610ad4 + off) & 0x3fffff;
286
287         /* We should investigate what else is found in these register ranges. */
288         uint32_t unk1 = NVRead(pNv, 0x00610afc + off);
289         uint32_t unk2 = NVRead(pNv, 0x00610b04 + off);
290         uint32_t unk3 = NVRead(pNv, 0x00610ae8 + off);
291         /*uint32_t unk4 = NVRead(pNv, 0x00610af4 + off);*/
292
293         /* Recontruct our mode, so it can be handled normally. */
294         mode->HTotal = (unk1 & 0xFFFF);
295         mode->VTotal = (unk1 >> 16);
296
297         /* Assuming no interlacing. */
298         mode->HSyncStart = mode->HTotal - (unk3 & 0xFFFF) - 1;
299         mode->VSyncStart = mode->VTotal - (unk3 >> 16) - 1;
300
301         mode->HSyncEnd = mode->HSyncStart + (unk2 & 0xFFFF) + 1;
302         mode->VSyncEnd = mode->VSyncStart + (unk2 >> 16) + 1;
303
304         mode->next = mode->prev = NULL;
305         mode->status = MODE_OK;
306         mode->type = M_T_DRIVER | M_T_PREFERRED;
307
308         xf86SetModeDefaultName(mode);
309
310         return mode;
311 }
312
313 DisplayModePtr
314 GetLVDSNativeMode(ScrnInfoPtr pScrn)
315 {
316         NVPtr pNv = NVPTR(pScrn);
317         CARD32 val = NVRead(pNv, 0x00610050);
318
319         if ((val & 0x3) == 0x2) {
320                 return ReadLVDSNativeMode(pScrn, 0);
321         } else if ((val & 0x300) == 0x200) {
322                 return ReadLVDSNativeMode(pScrn, 0x540);
323         }
324
325         return NULL;
326 }