2 * Copyright 2007 NVIDIA, Corporation
3 * Copyright 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 "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
20 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 #include "nouveau_modeset.h"
25 #include "nouveau_crtc.h"
26 #include "nouveau_output.h"
27 #include "nouveau_connector.h"
30 NV50SorModeValid(nouveauOutputPtr output, DisplayModePtr mode)
32 if (mode->Clock > 165000) /* no dual link until we figure it out completely */
33 return MODE_CLOCK_HIGH;
35 if (mode->Clock < 25000)
36 return MODE_CLOCK_LOW;
38 if (mode->Flags & V_DBLSCAN)
39 return MODE_NO_DBLESCAN;
41 if (mode->HDisplay > output->native_mode->HDisplay || mode->VDisplay > output->native_mode->VDisplay)
48 NV50SorModeSet(nouveauOutputPtr output, DisplayModePtr mode)
50 ScrnInfoPtr pScrn = output->scrn;
51 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorModeSet is called.\n");
53 const int sorOff = 0x40 * NV50OrOffset(output);
54 uint32_t mode_ctl = NV50_SOR_MODE_CTRL_OFF;
57 /* Disconnect the SOR */
58 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disconnecting SOR.\n");
59 NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
63 /* Anyone know a more appropriate name? */
64 DisplayModePtr desired_mode = output->crtc->use_native_mode ? output->crtc->native_mode : mode;
66 if (output->type == OUTPUT_LVDS) {
67 mode_ctl |= NV50_SOR_MODE_CTRL_LVDS;
69 mode_ctl |= NV50_SOR_MODE_CTRL_TMDS;
70 if (desired_mode->Clock > 165000)
71 mode_ctl |= NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK;
75 if (output->crtc->index == 1)
76 mode_ctl |= NV50_SOR_MODE_CTRL_CRTC1;
78 mode_ctl |= NV50_SOR_MODE_CTRL_CRTC0;
80 xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Warning, output has no crtc.\n");
84 if (desired_mode->Flags & V_NHSYNC)
85 mode_ctl |= NV50_SOR_MODE_CTRL_NHSYNC;
87 if (desired_mode->Flags & V_NVSYNC)
88 mode_ctl |= NV50_SOR_MODE_CTRL_NVSYNC;
90 // This wouldn't be necessary, but the server is stupid and calls
91 // nv50_sor_dpms after the output is disconnected, even though the hardware
92 // turns it off automatically.
93 output->SetPowerMode(output, DPMSModeOn);
95 NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
97 output->crtc->SetScaleMode(output->crtc, output->scale_mode);
101 NV50SorSetClockMode(nouveauOutputPtr output, int clock)
103 ScrnInfoPtr pScrn = output->scrn;
104 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorSetClockMode is called.\n");
106 NVPtr pNv = NVPTR(pScrn);
107 const int limit = 165000;
109 /* 0x70000 was a late addition to nv, mentioned as fixing tmds initialisation on certain gpu's. */
110 /* I presume it's some kind of clock setting, but what precisely i do not know. */
111 NVWrite(pNv, NV50_SOR0_CLK_CTRL2 + NV50OrOffset(output) * 0x800, 0x70000 | ((clock > limit) ? 0x101 : 0));
115 NV50SorSetClockModeLVDS(nouveauOutputPtr output, int clock)
120 NV50SorSense(nouveauOutputPtr output)
122 switch (output->type) {
132 NV50SorSetPowerMode(nouveauOutputPtr output, int mode)
134 ScrnInfoPtr pScrn = output->scrn;
135 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorSetPowerMode is called with mode %d.\n", mode);
137 NVPtr pNv = NVPTR(pScrn);
140 while ((NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800) & NV50_SOR_DPMS_CTRL_PENDING));
142 tmp = NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
143 tmp |= NV50_SOR_DPMS_CTRL_PENDING;
145 if (mode == DPMSModeOn)
146 tmp |= NV50_SOR_DPMS_CTRL_MODE_ON;
148 tmp &= ~NV50_SOR_DPMS_CTRL_MODE_ON;
150 NVWrite(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800, tmp);
151 while ((NVRead(pNv, NV50_SOR0_DPMS_STATE + NV50OrOffset(output) * 0x800) & NV50_SOR_DPMS_STATE_WAIT));
155 NV50SorDetect(nouveauOutputPtr output)
157 if (output->type == OUTPUT_LVDS) /* assume connected */
164 NV50SorSetFunctionPointers(nouveauOutputPtr output)
166 output->ModeValid = NV50SorModeValid;
167 output->ModeSet = NV50SorModeSet;
168 if (output->type == OUTPUT_TMDS)
169 output->SetClockMode = NV50SorSetClockMode;
171 output->SetClockMode = NV50SorSetClockModeLVDS;
172 output->Sense = NV50SorSense;
173 output->Detect = NV50SorDetect;
174 output->SetPowerMode = NV50SorSetPowerMode;
178 * Some misc functions.
181 static DisplayModePtr
182 ReadLVDSNativeMode(ScrnInfoPtr pScrn, const int off)
184 NVPtr pNv = NVPTR(pScrn);
185 DisplayModePtr mode = xnfcalloc(1, sizeof(DisplayModeRec));
186 const CARD32 size = NVRead(pNv, 0x00610b4c + off);
187 const int width = size & 0x3fff;
188 const int height = (size >> 16) & 0x3fff;
190 mode->HDisplay = width;
191 mode->VDisplay = height;
192 mode->Clock = NVRead(pNv, 0x00610ad4 + off) & 0x3fffff;
194 /* We should investigate what else is found in these register ranges. */
195 uint32_t unk1 = NVRead(pNv, NV50_CRTC0_DISPLAY_TOTAL_VAL + off);
196 uint32_t unk2 = NVRead(pNv, NV50_CRTC0_SYNC_DURATION_VAL + off);
197 uint32_t unk3 = NVRead(pNv, NV50_CRTC0_SYNC_START_TO_BLANK_END_VAL + off);
198 /*uint32_t unk4 = NVRead(pNv, NV50_CRTC0_MODE_UNK1_VAL + off);*/
200 /* Recontruct our mode, so it can be handled normally. */
201 mode->HTotal = (unk1 & 0xFFFF);
202 mode->VTotal = (unk1 >> 16);
204 /* Assuming no interlacing. */
205 mode->HSyncStart = mode->HTotal - (unk3 & 0xFFFF) - 1;
206 mode->VSyncStart = mode->VTotal - (unk3 >> 16) - 1;
208 mode->HSyncEnd = mode->HSyncStart + (unk2 & 0xFFFF) + 1;
209 mode->VSyncEnd = mode->VSyncStart + (unk2 >> 16) + 1;
211 mode->next = mode->prev = NULL;
212 mode->status = MODE_OK;
213 mode->type = M_T_DRIVER | M_T_PREFERRED;
215 xf86SetModeDefaultName(mode);
221 GetLVDSNativeMode(ScrnInfoPtr pScrn)
223 NVPtr pNv = NVPTR(pScrn);
224 uint32_t val = NVRead(pNv, NV50_DISPLAY_UNK50_CTRL);
226 /* This is rather crude imo, i wonder if it always works. */
227 if ((val & NV50_DISPLAY_UNK50_CTRL_CRTC0_MASK) == NV50_DISPLAY_UNK50_CTRL_CRTC0_ACTIVE) {
228 return ReadLVDSNativeMode(pScrn, 0);
229 } else if ((val & NV50_DISPLAY_UNK50_CTRL_CRTC1_MASK) == NV50_DISPLAY_UNK50_CTRL_CRTC1_ACTIVE) {
230 return ReadLVDSNativeMode(pScrn, 0x540);