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)
34 if (output->type == OUTPUT_LVDS)
37 high_limit = 165000; /* no dual link dvi until we figure it out completely */
39 if (mode->Clock > high_limit)
40 return MODE_CLOCK_HIGH;
42 if (mode->Clock < 25000)
43 return MODE_CLOCK_LOW;
45 if (mode->Flags & V_DBLSCAN)
46 return MODE_NO_DBLESCAN;
48 if (mode->HDisplay > output->native_mode->HDisplay || mode->VDisplay > output->native_mode->VDisplay)
55 NV50SorModeSet(nouveauOutputPtr output, DisplayModePtr mode)
57 ScrnInfoPtr pScrn = output->scrn;
58 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorModeSet is called.\n");
60 const int sorOff = 0x40 * NV50OrOffset(output);
61 uint32_t mode_ctl = NV50_SOR_MODE_CTRL_OFF;
64 /* Disconnect the SOR */
65 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disconnecting SOR.\n");
66 NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
70 /* Anyone know a more appropriate name? */
71 DisplayModePtr desired_mode = output->crtc->use_native_mode ? output->crtc->native_mode : mode;
73 if (output->type == OUTPUT_LVDS) {
74 mode_ctl |= NV50_SOR_MODE_CTRL_LVDS;
76 mode_ctl |= NV50_SOR_MODE_CTRL_TMDS;
77 if (desired_mode->Clock > 165000)
78 mode_ctl |= NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK;
82 if (output->crtc->index == 1)
83 mode_ctl |= NV50_SOR_MODE_CTRL_CRTC1;
85 mode_ctl |= NV50_SOR_MODE_CTRL_CRTC0;
87 xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Warning, output has no crtc.\n");
91 if (desired_mode->Flags & V_NHSYNC)
92 mode_ctl |= NV50_SOR_MODE_CTRL_NHSYNC;
94 if (desired_mode->Flags & V_NVSYNC)
95 mode_ctl |= NV50_SOR_MODE_CTRL_NVSYNC;
97 // This wouldn't be necessary, but the server is stupid and calls
98 // nv50_sor_dpms after the output is disconnected, even though the hardware
99 // turns it off automatically.
100 output->SetPowerMode(output, DPMSModeOn);
102 NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
104 output->crtc->SetScaleMode(output->crtc, output->scale_mode);
108 NV50SorSetClockMode(nouveauOutputPtr output, int clock)
110 ScrnInfoPtr pScrn = output->scrn;
111 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorSetClockMode is called.\n");
113 NVPtr pNv = NVPTR(pScrn);
114 const int limit = 165000;
116 /* 0x70000 was a late addition to nv, mentioned as fixing tmds initialisation on certain gpu's. */
117 /* I presume it's some kind of clock setting, but what precisely i do not know. */
118 NVWrite(pNv, NV50_SOR0_CLK_CTRL2 + NV50OrOffset(output) * 0x800, 0x70000 | ((clock > limit) ? 0x101 : 0));
122 NV50SorSetClockModeLVDS(nouveauOutputPtr output, int clock)
127 NV50SorSense(nouveauOutputPtr output)
129 switch (output->type) {
139 NV50SorSetPowerMode(nouveauOutputPtr output, int mode)
141 ScrnInfoPtr pScrn = output->scrn;
142 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorSetPowerMode is called with mode %d.\n", mode);
144 NVPtr pNv = NVPTR(pScrn);
147 while ((NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800) & NV50_SOR_DPMS_CTRL_PENDING));
149 tmp = NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
150 tmp |= NV50_SOR_DPMS_CTRL_PENDING;
152 if (mode == DPMSModeOn)
153 tmp |= NV50_SOR_DPMS_CTRL_MODE_ON;
155 tmp &= ~NV50_SOR_DPMS_CTRL_MODE_ON;
157 NVWrite(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800, tmp);
158 while ((NVRead(pNv, NV50_SOR0_DPMS_STATE + NV50OrOffset(output) * 0x800) & NV50_SOR_DPMS_STATE_WAIT));
162 NV50SorDetect(nouveauOutputPtr output)
164 if (output->type == OUTPUT_LVDS) /* assume connected */
171 NV50SorSetFunctionPointers(nouveauOutputPtr output)
173 output->ModeValid = NV50SorModeValid;
174 output->ModeSet = NV50SorModeSet;
175 if (output->type == OUTPUT_TMDS)
176 output->SetClockMode = NV50SorSetClockMode;
178 output->SetClockMode = NV50SorSetClockModeLVDS;
179 output->Sense = NV50SorSense;
180 output->Detect = NV50SorDetect;
181 output->SetPowerMode = NV50SorSetPowerMode;
185 * Some misc functions.
188 static DisplayModePtr
189 ReadLVDSNativeMode(ScrnInfoPtr pScrn, const int off)
191 NVPtr pNv = NVPTR(pScrn);
192 DisplayModePtr mode = xnfcalloc(1, sizeof(DisplayModeRec));
193 const CARD32 size = NVRead(pNv, 0x00610b4c + off);
194 const int width = size & 0x3fff;
195 const int height = (size >> 16) & 0x3fff;
197 mode->HDisplay = width;
198 mode->VDisplay = height;
199 mode->Clock = NVRead(pNv, 0x00610ad4 + off) & 0x3fffff;
201 /* We should investigate what else is found in these register ranges. */
202 uint32_t unk1 = NVRead(pNv, NV50_CRTC0_DISPLAY_TOTAL_VAL + off);
203 uint32_t unk2 = NVRead(pNv, NV50_CRTC0_SYNC_DURATION_VAL + off);
204 uint32_t unk3 = NVRead(pNv, NV50_CRTC0_SYNC_START_TO_BLANK_END_VAL + off);
205 /*uint32_t unk4 = NVRead(pNv, NV50_CRTC0_MODE_UNK1_VAL + off);*/
207 /* Recontruct our mode, so it can be handled normally. */
208 mode->HTotal = (unk1 & 0xFFFF);
209 mode->VTotal = (unk1 >> 16);
211 /* Assuming no interlacing. */
212 mode->HSyncStart = mode->HTotal - (unk3 & 0xFFFF) - 1;
213 mode->VSyncStart = mode->VTotal - (unk3 >> 16) - 1;
215 mode->HSyncEnd = mode->HSyncStart + (unk2 & 0xFFFF) + 1;
216 mode->VSyncEnd = mode->VSyncStart + (unk2 >> 16) + 1;
218 mode->next = mode->prev = NULL;
219 mode->status = MODE_OK;
220 mode->type = M_T_DRIVER | M_T_PREFERRED;
222 xf86SetModeDefaultName(mode);
228 GetLVDSNativeMode(ScrnInfoPtr pScrn)
230 NVPtr pNv = NVPTR(pScrn);
231 uint32_t val = NVRead(pNv, NV50_DISPLAY_UNK50_CTRL);
233 /* This is rather crude imo, i wonder if it always works. */
234 if ((val & NV50_DISPLAY_UNK50_CTRL_CRTC0_MASK) == NV50_DISPLAY_UNK50_CTRL_CRTC0_ACTIVE) {
235 return ReadLVDSNativeMode(pScrn, 0);
236 } else if ((val & NV50_DISPLAY_UNK50_CTRL_CRTC1_MASK) == NV50_DISPLAY_UNK50_CTRL_CRTC1_ACTIVE) {
237 return ReadLVDSNativeMode(pScrn, 0x540);