NV50: rename randr-1.2 functions to be nv50_something_function
[nouveau] / src / nv50_sor.c
1 /*
2  * Copyright (c) 2007 NVIDIA, Corporation
3  * Copyright (c) 2008 Maarten Maathuis
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24
25 #define DPMS_SERVER
26 #include <X11/extensions/dpms.h>
27 #include <X11/Xatom.h>
28
29 #include "nv_include.h"
30
31 void
32 NV50SorSetPClk(xf86OutputPtr output, int pclk)
33 {
34         ScrnInfoPtr pScrn = output->scrn;
35         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorSetPClk is called.\n");
36
37         NVPtr pNv = NVPTR(pScrn);
38         const int limit = 165000;
39
40         /* 0x70000 was a late addition to nv, mentioned as fixing tmds initialisation on certain gpu's. */
41         /* I presume it's some kind of clock setting, but what precisely i do not know. */
42         NVWrite(pNv, NV50_SOR0_CLK_CTRL1 + NV50OrOffset(output) * 0x800, 0x70000 | ((pclk > limit) ? 0x101 : 0));
43 }
44
45 static void
46 nv50_sor_dpms(xf86OutputPtr output, int mode)
47 {
48         ScrnInfoPtr pScrn = output->scrn;
49         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_sor_dpms is called with mode %d.\n", mode);
50
51         NVPtr pNv = NVPTR(pScrn);
52         CARD32 tmp;
53
54         while((NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800) & NV50_SOR_DPMS_CTRL_PENDING));
55
56         tmp = NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
57         tmp |= NV50_SOR_DPMS_CTRL_PENDING;
58
59         if(mode == DPMSModeOn)
60                 tmp |= NV50_SOR_DPMS_CTRL_MODE_ON;
61         else
62                 tmp &= ~NV50_SOR_DPMS_CTRL_MODE_ON;
63
64         NVWrite(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800, tmp);
65         while((NVRead(pNv, 0x0061c030 + NV50OrOffset(output) * 0x800) & 0x10000000));
66 }
67
68 static int
69 nv50_tmds_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
70 {
71         NVOutputPrivatePtr nv_output = output->driver_private;
72
73         // Disable dual-link modes until I can find a way to make them work
74         // reliably.
75         if (mode->Clock > 165000)
76                 return MODE_CLOCK_HIGH;
77
78         if (mode->HDisplay > nv_output->fpWidth || mode->VDisplay > nv_output->fpHeight)
79                 return MODE_PANEL;
80
81         return nv50_output_mode_valid(output, mode);
82 }
83
84 static int
85 nv50_lvds_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
86 {
87         NVOutputPrivatePtr nv_output = output->driver_private;
88         DisplayModePtr native = nv_output->native_mode;
89
90         // Ignore modes larger than the native res.
91         if (mode->HDisplay > native->HDisplay || mode->VDisplay > native->VDisplay)
92                 return MODE_PANEL;
93
94         return nv50_output_mode_valid(output, mode);
95 }
96
97 static void
98 nv50_sor_mode_set(xf86OutputPtr output, DisplayModePtr mode,
99                 DisplayModePtr adjusted_mode)
100 {
101         ScrnInfoPtr pScrn = output->scrn;
102         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_sor_mode_set is called.\n");
103
104         NVOutputPrivatePtr nv_output = output->driver_private;
105         const int sorOff = 0x40 * NV50OrOffset(output);
106         uint32_t mode_ctl = NV50_SOR_MODE_CTRL_OFF;
107
108         if (!adjusted_mode) {
109                 /* Disconnect the SOR */
110                 NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
111                 return;
112         }
113
114         if (nv_output->type == OUTPUT_LVDS) {
115                 mode_ctl |= NV50_SOR_MODE_CTRL_LVDS;
116         } else {
117                 mode_ctl |= NV50_SOR_MODE_CTRL_TMDS;
118                 if (adjusted_mode->Clock > 165000)
119                         mode_ctl |= NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK;
120         }
121
122         if (output->crtc) {
123                 NVCrtcPrivatePtr nv_crtc = output->crtc->driver_private;
124                 if (nv_crtc->head == 1)
125                         mode_ctl |= NV50_SOR_MODE_CTRL_CRTC1;
126                 else
127                         mode_ctl |= NV50_SOR_MODE_CTRL_CRTC0;
128         } else {
129                 return;
130         }
131
132         if (adjusted_mode->Flags & V_NHSYNC)
133                 mode_ctl |= NV50_SOR_MODE_CTRL_NHSYNC;
134
135         if (adjusted_mode->Flags & V_NVSYNC)
136                 mode_ctl |= NV50_SOR_MODE_CTRL_NVSYNC;
137
138         // This wouldn't be necessary, but the server is stupid and calls
139         // nv50_sor_dpms after the output is disconnected, even though the hardware
140         // turns it off automatically.
141         nv50_sor_dpms(output, DPMSModeOn);
142
143         NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
144
145         NV50CrtcSetScale(output->crtc, mode, adjusted_mode, nv_output->scaling_mode);
146 }
147
148 static xf86OutputStatus
149 nv50_sor_detect(xf86OutputPtr output)
150 {
151         ScrnInfoPtr pScrn = output->scrn;
152         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_sor_detect is called.\n");
153
154         NVOutputPrivatePtr nv_output = output->driver_private;
155         xf86MonPtr ddc_mon;
156
157         if (nv_output->pDDCBus == NULL)
158                 return XF86OutputStatusDisconnected;
159
160         ddc_mon = NV50OutputGetEDID(output, nv_output->pDDCBus);
161         if (!ddc_mon)
162                 return XF86OutputStatusDisconnected;
163
164         if (!ddc_mon->features.input_type) /* Analog? */
165                 return XF86OutputStatusDisconnected;
166
167         return XF86OutputStatusConnected;
168 }
169
170 static xf86OutputStatus
171 nv50_lvds_detect(xf86OutputPtr output)
172 {
173         ScrnInfoPtr pScrn = output->scrn;
174         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_lvds_detect is called.\n");
175
176         /* Assume LVDS is always connected */
177         return XF86OutputStatusConnected;
178 }
179
180 static void
181 nv50_sor_destroy(xf86OutputPtr output)
182 {
183         NVOutputPrivatePtr nv_output = output->driver_private;
184
185         NV50OutputDestroy(output);
186
187         xf86DeleteMode(&nv_output->native_mode, nv_output->native_mode);
188
189         xfree(output->driver_private);
190         output->driver_private = NULL;
191 }
192
193 static Bool
194 nv50_sor_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
195                  DisplayModePtr adjusted_mode)
196 {
197         ScrnInfoPtr pScrn = output->scrn;
198         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_sor_mode_fixup is called.\n");
199
200         NVOutputPrivatePtr nv_output = output->driver_private;
201
202         if (nv_output->native_mode && nv_output->scaling_mode != SCALE_PANEL) {
203                 adjusted_mode->HDisplay = nv_output->native_mode->HDisplay;
204                 adjusted_mode->HSkew = nv_output->native_mode->HSkew;
205                 adjusted_mode->HSyncStart = nv_output->native_mode->HSyncStart;
206                 adjusted_mode->HSyncEnd = nv_output->native_mode->HSyncEnd;
207                 adjusted_mode->HTotal = nv_output->native_mode->HTotal;
208                 adjusted_mode->VDisplay = nv_output->native_mode->VDisplay;
209                 adjusted_mode->VScan = nv_output->native_mode->VScan;
210                 adjusted_mode->VSyncStart = nv_output->native_mode->VSyncStart;
211                 adjusted_mode->VSyncEnd = nv_output->native_mode->VSyncEnd;
212                 adjusted_mode->VTotal = nv_output->native_mode->VTotal;
213                 adjusted_mode->Clock = nv_output->native_mode->Clock;
214                 adjusted_mode->Flags = nv_output->native_mode->Flags;
215
216                 /* No INTERLACE_HALVE_V, because we manually correct. */
217                 xf86SetModeCrtc(adjusted_mode, 0);
218         }
219         return TRUE;
220 }
221
222 static DisplayModePtr
223 nv50_lvds_get_modes(xf86OutputPtr output)
224 {
225         NVOutputPrivatePtr nv_output = output->driver_private;
226         return xf86DuplicateMode(nv_output->native_mode);
227 }
228
229 static const xf86OutputFuncsRec NV50SorTMDSOutputFuncs = {
230         .dpms = nv50_sor_dpms,
231         .save = NULL,
232         .restore = NULL,
233         .mode_valid = nv50_tmds_mode_valid,
234         .mode_fixup = nv50_sor_mode_fixup,
235         .prepare = nv50_output_prepare,
236         .commit = nv50_output_commit,
237         .mode_set = nv50_sor_mode_set,
238         .detect = nv50_sor_detect,
239         .get_modes = nv50_output_get_ddc_modes,
240         .create_resources = nv_digital_output_create_resources,
241         .set_property = nv_digital_output_set_property,
242         .destroy = nv50_sor_destroy,
243 };
244
245 static const xf86OutputFuncsRec NV50SorLVDSOutputFuncs = {
246         .dpms = nv50_sor_dpms,
247         .save = NULL,
248         .restore = NULL,
249         .mode_valid = nv50_lvds_mode_valid,
250         .mode_fixup = nv50_sor_mode_fixup,
251         .prepare = nv50_output_prepare,
252         .commit = nv50_output_commit,
253         .mode_set = nv50_sor_mode_set,
254         .detect = nv50_lvds_detect,
255         .get_modes = nv50_lvds_get_modes,
256         .create_resources = nv_digital_output_create_resources,
257         .set_property = nv_digital_output_set_property,
258         .destroy = nv50_sor_destroy,
259 };
260
261 const xf86OutputFuncsRec * nv50_get_tmds_output_funcs()
262 {
263         return &NV50SorTMDSOutputFuncs;
264 }
265
266 const xf86OutputFuncsRec * nv50_get_lvds_output_funcs()
267 {
268         return &NV50SorLVDSOutputFuncs;
269 }
270
271 static DisplayModePtr
272 ReadLVDSNativeMode(ScrnInfoPtr pScrn, const int off)
273 {
274         NVPtr pNv = NVPTR(pScrn);
275         DisplayModePtr mode = xnfcalloc(1, sizeof(DisplayModeRec));
276         const CARD32 size = NVRead(pNv, 0x00610b4c + off);
277         const int width = size & 0x3fff;
278         const int height = (size >> 16) & 0x3fff;
279
280         mode->HDisplay = width;
281         mode->VDisplay = height;
282         mode->Clock = NVRead(pNv, 0x00610ad4 + off) & 0x3fffff;
283
284         /* We should investigate what else is found in these register ranges. */
285         uint32_t unk1 = NVRead(pNv, NV50_CRTC0_DISPLAY_TOTAL_VAL + off);
286         uint32_t unk2 = NVRead(pNv, NV50_CRTC0_SYNC_DURATION_VAL + off);
287         uint32_t unk3 = NVRead(pNv, NV50_CRTC0_SYNC_START_TO_BLANK_END_VAL + off);
288         /*uint32_t unk4 = NVRead(pNv, NV50_CRTC0_MODE_UNK1_VAL + off);*/
289
290         /* Recontruct our mode, so it can be handled normally. */
291         mode->HTotal = (unk1 & 0xFFFF);
292         mode->VTotal = (unk1 >> 16);
293
294         /* Assuming no interlacing. */
295         mode->HSyncStart = mode->HTotal - (unk3 & 0xFFFF) - 1;
296         mode->VSyncStart = mode->VTotal - (unk3 >> 16) - 1;
297
298         mode->HSyncEnd = mode->HSyncStart + (unk2 & 0xFFFF) + 1;
299         mode->VSyncEnd = mode->VSyncStart + (unk2 >> 16) + 1;
300
301         mode->next = mode->prev = NULL;
302         mode->status = MODE_OK;
303         mode->type = M_T_DRIVER | M_T_PREFERRED;
304
305         xf86SetModeDefaultName(mode);
306
307         return mode;
308 }
309
310 DisplayModePtr
311 GetLVDSNativeMode(ScrnInfoPtr pScrn)
312 {
313         NVPtr pNv = NVPTR(pScrn);
314         uint32_t val = NVRead(pNv, NV50_DISPLAY_UNK50_CTRL);
315
316         /* This is rather crude imo, i wonder if it always works. */
317         if ((val & NV50_DISPLAY_UNK50_CTRL_CRTC0_MASK) == NV50_DISPLAY_UNK50_CTRL_CRTC0_ACTIVE) {
318                 return ReadLVDSNativeMode(pScrn, 0);
319         } else if ((val & NV50_DISPLAY_UNK50_CTRL_CRTC1_MASK) == NV50_DISPLAY_UNK50_CTRL_CRTC1_ACTIVE) {
320                 return ReadLVDSNativeMode(pScrn, 0x540);
321         }
322
323         return NULL;
324 }