2 * Copyright (c) 2007 NVIDIA, Corporation
3 * Copyright (c) 2008 Maarten Maathuis
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:
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
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.
26 #include <X11/extensions/dpms.h>
27 #include <X11/Xatom.h>
29 #include "nv_include.h"
32 NV50SorSetPClk(xf86OutputPtr output, int pclk)
34 ScrnInfoPtr pScrn = output->scrn;
35 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorSetPClk is called.\n");
37 NVPtr pNv = NVPTR(pScrn);
38 const int limit = 165000;
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));
46 nv50_sor_dpms(xf86OutputPtr output, int mode)
48 ScrnInfoPtr pScrn = output->scrn;
49 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_sor_dpms is called with mode %d.\n", mode);
51 NVPtr pNv = NVPTR(pScrn);
54 while((NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800) & NV50_SOR_DPMS_CTRL_PENDING));
56 tmp = NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
57 tmp |= NV50_SOR_DPMS_CTRL_PENDING;
59 if(mode == DPMSModeOn)
60 tmp |= NV50_SOR_DPMS_CTRL_MODE_ON;
62 tmp &= ~NV50_SOR_DPMS_CTRL_MODE_ON;
64 NVWrite(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800, tmp);
65 while((NVRead(pNv, 0x0061c030 + NV50OrOffset(output) * 0x800) & 0x10000000));
69 nv50_tmds_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
71 NVOutputPrivatePtr nv_output = output->driver_private;
73 // Disable dual-link modes until I can find a way to make them work
75 if (mode->Clock > 165000)
76 return MODE_CLOCK_HIGH;
78 if (mode->HDisplay > nv_output->fpWidth || mode->VDisplay > nv_output->fpHeight)
81 return nv50_output_mode_valid(output, mode);
85 nv50_lvds_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
87 NVOutputPrivatePtr nv_output = output->driver_private;
88 DisplayModePtr native = nv_output->native_mode;
90 // Ignore modes larger than the native res.
91 if (mode->HDisplay > native->HDisplay || mode->VDisplay > native->VDisplay)
94 return nv50_output_mode_valid(output, mode);
98 nv50_sor_mode_set(xf86OutputPtr output, DisplayModePtr mode,
99 DisplayModePtr adjusted_mode)
101 ScrnInfoPtr pScrn = output->scrn;
102 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_sor_mode_set is called.\n");
104 NVOutputPrivatePtr nv_output = output->driver_private;
105 const int sorOff = 0x40 * NV50OrOffset(output);
106 uint32_t mode_ctl = NV50_SOR_MODE_CTRL_OFF;
108 if (!adjusted_mode) {
109 /* Disconnect the SOR */
110 NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
114 if (nv_output->type == OUTPUT_LVDS) {
115 mode_ctl |= NV50_SOR_MODE_CTRL_LVDS;
117 mode_ctl |= NV50_SOR_MODE_CTRL_TMDS;
118 if (adjusted_mode->Clock > 165000)
119 mode_ctl |= NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK;
123 NVCrtcPrivatePtr nv_crtc = output->crtc->driver_private;
124 if (nv_crtc->head == 1)
125 mode_ctl |= NV50_SOR_MODE_CTRL_CRTC1;
127 mode_ctl |= NV50_SOR_MODE_CTRL_CRTC0;
132 if (adjusted_mode->Flags & V_NHSYNC)
133 mode_ctl |= NV50_SOR_MODE_CTRL_NHSYNC;
135 if (adjusted_mode->Flags & V_NVSYNC)
136 mode_ctl |= NV50_SOR_MODE_CTRL_NVSYNC;
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);
143 NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
145 NV50CrtcSetScale(output->crtc, mode, adjusted_mode, nv_output->scaling_mode);
148 static xf86OutputStatus
149 nv50_sor_detect(xf86OutputPtr output)
151 ScrnInfoPtr pScrn = output->scrn;
152 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_sor_detect is called.\n");
154 NVOutputPrivatePtr nv_output = output->driver_private;
157 if (nv_output->pDDCBus == NULL)
158 return XF86OutputStatusDisconnected;
160 ddc_mon = NV50OutputGetEDID(output, nv_output->pDDCBus);
162 return XF86OutputStatusDisconnected;
164 if (!ddc_mon->features.input_type) /* Analog? */
165 return XF86OutputStatusDisconnected;
167 return XF86OutputStatusConnected;
170 static xf86OutputStatus
171 nv50_lvds_detect(xf86OutputPtr output)
173 ScrnInfoPtr pScrn = output->scrn;
174 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_lvds_detect is called.\n");
176 /* Assume LVDS is always connected */
177 return XF86OutputStatusConnected;
181 nv50_sor_destroy(xf86OutputPtr output)
183 NVOutputPrivatePtr nv_output = output->driver_private;
185 NV50OutputDestroy(output);
187 xf86DeleteMode(&nv_output->native_mode, nv_output->native_mode);
189 xfree(output->driver_private);
190 output->driver_private = NULL;
194 nv50_sor_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
195 DisplayModePtr adjusted_mode)
197 ScrnInfoPtr pScrn = output->scrn;
198 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_sor_mode_fixup is called.\n");
200 NVOutputPrivatePtr nv_output = output->driver_private;
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;
216 /* No INTERLACE_HALVE_V, because we manually correct. */
217 xf86SetModeCrtc(adjusted_mode, 0);
222 static DisplayModePtr
223 nv50_lvds_get_modes(xf86OutputPtr output)
225 NVOutputPrivatePtr nv_output = output->driver_private;
226 return xf86DuplicateMode(nv_output->native_mode);
229 static const xf86OutputFuncsRec NV50SorTMDSOutputFuncs = {
230 .dpms = nv50_sor_dpms,
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,
245 static const xf86OutputFuncsRec NV50SorLVDSOutputFuncs = {
246 .dpms = nv50_sor_dpms,
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,
261 const xf86OutputFuncsRec * nv50_get_tmds_output_funcs()
263 return &NV50SorTMDSOutputFuncs;
266 const xf86OutputFuncsRec * nv50_get_lvds_output_funcs()
268 return &NV50SorLVDSOutputFuncs;
271 static DisplayModePtr
272 ReadLVDSNativeMode(ScrnInfoPtr pScrn, const int off)
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;
280 mode->HDisplay = width;
281 mode->VDisplay = height;
282 mode->Clock = NVRead(pNv, 0x00610ad4 + off) & 0x3fffff;
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);*/
290 /* Recontruct our mode, so it can be handled normally. */
291 mode->HTotal = (unk1 & 0xFFFF);
292 mode->VTotal = (unk1 >> 16);
294 /* Assuming no interlacing. */
295 mode->HSyncStart = mode->HTotal - (unk3 & 0xFFFF) - 1;
296 mode->VSyncStart = mode->VTotal - (unk3 >> 16) - 1;
298 mode->HSyncEnd = mode->HSyncStart + (unk2 & 0xFFFF) + 1;
299 mode->VSyncEnd = mode->VSyncStart + (unk2 >> 16) + 1;
301 mode->next = mode->prev = NULL;
302 mode->status = MODE_OK;
303 mode->type = M_T_DRIVER | M_T_PREFERRED;
305 xf86SetModeDefaultName(mode);
311 GetLVDSNativeMode(ScrnInfoPtr pScrn)
313 NVPtr pNv = NVPTR(pScrn);
314 uint32_t val = NVRead(pNv, NV50_DISPLAY_UNK50_CTRL);
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);