2 * Copyright (c) 2007 NVIDIA, Corporation
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:
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
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.
25 #include <X11/extensions/dpms.h>
26 #include <X11/Xatom.h>
28 #include "nv_include.h"
29 #include "nv50_type.h"
30 #include "nv50_display.h"
31 #include "nv50_output.h"
34 NV50SorSetPClk(xf86OutputPtr output, int pclk)
36 ScrnInfoPtr pScrn = output->scrn;
37 NVPtr pNv = NVPTR(pScrn);
38 const int limit = 165000;
40 NVWrite(pNv, 0x00614300 + NV50OrOffset(output) * 0x800, (pclk > limit) ? 0x101 : 0);
44 NV50SorDPMSSet(xf86OutputPtr output, int mode)
46 ScrnInfoPtr pScrn = output->scrn;
47 NVPtr pNv = NVPTR(pScrn);
50 while((NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800) & NV50_SOR_DPMS_CTRL_PENDING));
52 tmp = NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
53 tmp |= NV50_SOR_DPMS_CTRL_PENDING;
55 if(mode == DPMSModeOn)
56 tmp |= NV50_SOR_DPMS_CTRL_MODE_ON;
58 tmp &= ~NV50_SOR_DPMS_CTRL_MODE_ON;
60 NVWrite(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800, tmp);
61 while((NVRead(pNv, 0x0061c030 + NV50OrOffset(output) * 0x800) & 0x10000000));
65 NV50TMDSModeValid(xf86OutputPtr output, DisplayModePtr mode)
67 // Disable dual-link modes until I can find a way to make them work
69 if (mode->Clock > 165000)
70 return MODE_CLOCK_HIGH;
72 return NV50OutputModeValid(output, mode);
76 NV50LVDSModeValid(xf86OutputPtr output, DisplayModePtr mode)
78 NVOutputPrivatePtr nv_output = output->driver_private;
79 DisplayModePtr native = nv_output->native_mode;
81 // Ignore modes larger than the native res.
82 if (mode->HDisplay > native->HDisplay || mode->VDisplay > native->VDisplay)
85 return NV50OutputModeValid(output, mode);
89 NV50SorModeSet(xf86OutputPtr output, DisplayModePtr mode,
90 DisplayModePtr adjusted_mode)
92 ScrnInfoPtr pScrn = output->scrn;
93 NVOutputPrivatePtr nv_output = output->driver_private;
94 const int sorOff = 0x40 * NV50OrOffset(output);
95 uint32_t mode_ctl = NV50_SOR_MODE_CTRL_OFF;
98 /* Disconnect the SOR */
99 NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
103 if (nv_output->type == OUTPUT_LVDS) {
104 mode_ctl |= NV50_SOR_MODE_CTRL_LVDS;
106 mode_ctl |= NV50_SOR_MODE_CTRL_TMDS;
107 if (adjusted_mode->Clock > 165000)
108 mode_ctl |= NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK;
112 NVCrtcPrivatePtr nv_crtc = output->crtc->driver_private;
113 if (nv_crtc->head == 1)
114 mode_ctl |= NV50_SOR_MODE_CTRL_CRTC1;
116 mode_ctl |= NV50_SOR_MODE_CTRL_CRTC0;
121 if (adjusted_mode->Flags & V_NHSYNC)
122 mode_ctl |= NV50_SOR_MODE_CTRL_NHSYNC;
124 if (adjusted_mode->Flags & V_NVSYNC)
125 mode_ctl |= NV50_SOR_MODE_CTRL_NVSYNC;
127 // This wouldn't be necessary, but the server is stupid and calls
128 // NV50SorDPMSSet after the output is disconnected, even though the hardware
129 // turns it off automatically.
130 NV50SorDPMSSet(output, DPMSModeOn);
132 NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
134 NV50CrtcSetScale(output->crtc, mode, adjusted_mode, nv_output->scaling_mode);
137 static xf86OutputStatus
138 NV50SorDetect(xf86OutputPtr output)
140 NVOutputPrivatePtr nv_output = output->driver_private;
143 if (nv_output->pDDCBus == NULL)
144 return XF86OutputStatusDisconnected;
146 ddc_mon = NV50OutputGetEDID(output, nv_output->pDDCBus);
148 return XF86OutputStatusDisconnected;
150 if (!ddc_mon->features.input_type) /* Analog? */
151 return XF86OutputStatusDisconnected;
154 xf86OutputSetEDID(output, ddc_mon);
156 return XF86OutputStatusConnected;
159 static xf86OutputStatus
160 NV50SorLVDSDetect(xf86OutputPtr output)
162 /* Assume LVDS is always connected */
163 return XF86OutputStatusConnected;
167 NV50SorDestroy(xf86OutputPtr output)
169 NVOutputPrivatePtr nv_output = output->driver_private;
171 NV50OutputDestroy(output);
173 xf86DeleteMode(&nv_output->native_mode, nv_output->native_mode);
175 xfree(output->driver_private);
176 output->driver_private = NULL;
180 NV50SorModeFixup(xf86OutputPtr output, DisplayModePtr mode,
181 DisplayModePtr adjusted_mode)
183 NVOutputPrivatePtr nv_output = output->driver_private;
185 if (nv_output->native_mode && nv_output->scaling_mode != SCALE_PANEL) {
186 adjusted_mode->HDisplay = nv_output->native_mode->HDisplay;
187 adjusted_mode->HSkew = nv_output->native_mode->HSkew;
188 adjusted_mode->HSyncStart = nv_output->native_mode->HSyncStart;
189 adjusted_mode->HSyncEnd = nv_output->native_mode->HSyncEnd;
190 adjusted_mode->HTotal = nv_output->native_mode->HTotal;
191 adjusted_mode->VDisplay = nv_output->native_mode->VDisplay;
192 adjusted_mode->VScan = nv_output->native_mode->VScan;
193 adjusted_mode->VSyncStart = nv_output->native_mode->VSyncStart;
194 adjusted_mode->VSyncEnd = nv_output->native_mode->VSyncEnd;
195 adjusted_mode->VTotal = nv_output->native_mode->VTotal;
196 adjusted_mode->Clock = nv_output->native_mode->Clock;
197 adjusted_mode->Flags = nv_output->native_mode->Flags;
199 /* No INTERLACE_HALVE_V, because we manually correct. */
200 xf86SetModeCrtc(adjusted_mode, 0);
206 NV50SorTMDSModeFixup(xf86OutputPtr output, DisplayModePtr mode,
207 DisplayModePtr adjusted_mode)
209 int scrnIndex = output->scrn->scrnIndex;
210 NVOutputPrivatePtr nv_output = output->driver_private;
211 DisplayModePtr modes = output->probed_modes;
213 xf86DeleteMode(&nv_output->native_mode, nv_output->native_mode);
216 // Find the preferred mode and use that as the "native" mode.
217 // If no preferred mode is available, use the first one.
220 // Find the preferred mode.
221 for(mode = modes; mode; mode = mode->next) {
222 if(mode->type & M_T_PREFERRED) {
223 xf86DrvMsgVerb(scrnIndex, X_INFO, 5,
224 "%s: preferred mode is %s\n",
225 output->name, mode->name);
230 // XXX: May not want to allow scaling if no preferred mode is found.
233 xf86DrvMsgVerb(scrnIndex, X_INFO, 5,
234 "%s: no preferred mode found, using %s\n",
235 output->name, mode->name);
238 nv_output->native_mode = xf86DuplicateMode(mode);
241 return NV50SorModeFixup(output, mode, adjusted_mode);
244 static DisplayModePtr
245 NV50SorGetLVDSModes(xf86OutputPtr output)
247 NVOutputPrivatePtr nv_output = output->driver_private;
248 return xf86DuplicateMode(nv_output->native_mode);
251 static const xf86OutputFuncsRec NV50SorTMDSOutputFuncs = {
252 .dpms = NV50SorDPMSSet,
255 .mode_valid = NV50TMDSModeValid,
256 .mode_fixup = NV50SorTMDSModeFixup,
257 .prepare = NV50OutputPrepare,
258 .commit = NV50OutputCommit,
259 .mode_set = NV50SorModeSet,
260 .detect = NV50SorDetect,
261 .get_modes = NV50OutputGetDDCModes,
262 .create_resources = nv_digital_output_create_resources,
263 .set_property = nv_digital_output_set_property,
264 .destroy = NV50SorDestroy,
267 static const xf86OutputFuncsRec NV50SorLVDSOutputFuncs = {
268 .dpms = NV50SorDPMSSet,
271 .mode_valid = NV50LVDSModeValid,
272 .mode_fixup = NV50SorModeFixup,
273 .prepare = NV50OutputPrepare,
274 .commit = NV50OutputCommit,
275 .mode_set = NV50SorModeSet,
276 .detect = NV50SorLVDSDetect,
277 .get_modes = NV50SorGetLVDSModes,
278 .create_resources = nv_digital_output_create_resources,
279 .set_property = nv_digital_output_set_property,
280 .destroy = NV50SorDestroy,
283 const xf86OutputFuncsRec * nv50_get_tmds_output_funcs()
285 return &NV50SorTMDSOutputFuncs;
288 const xf86OutputFuncsRec * nv50_get_lvds_output_funcs()
290 return &NV50SorLVDSOutputFuncs;
293 static DisplayModePtr
294 ReadLVDSNativeMode(ScrnInfoPtr pScrn, const int off)
296 NVPtr pNv = NVPTR(pScrn);
297 DisplayModePtr mode = xnfcalloc(1, sizeof(DisplayModeRec));
298 const CARD32 size = NVRead(pNv, 0x00610b4c + off);
299 const int width = size & 0x3fff;
300 const int height = (size >> 16) & 0x3fff;
302 mode->HDisplay = width;
303 mode->VDisplay = height;
304 mode->Clock = NVRead(pNv, 0x00610ad4 + off) & 0x3fffff;
306 /* We should investigate what else is found in these register ranges. */
307 uint32_t unk1 = NVRead(pNv, 0x00610afc + off);
308 uint32_t unk2 = NVRead(pNv, 0x00610b04 + off);
309 uint32_t unk3 = NVRead(pNv, 0x00610ae8 + off);
310 /*uint32_t unk4 = NVRead(pNv, 0x00610af4 + off);*/
312 /* Recontruct our mode, so it can be handled normally. */
313 mode->HTotal = (unk1 & 0xFFFF);
314 mode->VTotal = (unk1 >> 16);
316 /* Assuming no interlacing. */
317 mode->HSyncStart = mode->HTotal - (unk3 & 0xFFFF) - 1;
318 mode->VSyncStart = mode->VTotal - (unk3 >> 16) - 1;
320 mode->HSyncEnd = mode->HSyncStart + (unk2 & 0xFFFF) + 1;
321 mode->VSyncEnd = mode->VSyncStart + (unk2 >> 16) + 1;
323 mode->next = mode->prev = NULL;
324 mode->status = MODE_OK;
325 mode->type = M_T_DRIVER | M_T_PREFERRED;
327 xf86SetModeDefaultName(mode);
333 GetLVDSNativeMode(ScrnInfoPtr pScrn)
335 NVPtr pNv = NVPTR(pScrn);
336 CARD32 val = NVRead(pNv, 0x00610050);
338 if ((val & 0x3) == 0x2) {
339 return ReadLVDSNativeMode(pScrn, 0);
340 } else if ((val & 0x300) == 0x200) {
341 return ReadLVDSNativeMode(pScrn, 0x540);