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 NV50DacModeValid(nouveauOutputPtr output, DisplayModePtr mode)
32 if (mode->Clock > 400000)
33 return MODE_CLOCK_HIGH;
35 if (mode->Clock < 25000)
36 return MODE_CLOCK_LOW;
42 NV50DacModeSet(nouveauOutputPtr output, DisplayModePtr mode)
44 ScrnInfoPtr pScrn = output->scrn;
45 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50DacModeSet is called.\n");
47 const int dacOff = 0x80 * NV50OrOffset(output);
48 uint32_t mode_ctl = NV50_DAC_MODE_CTRL_OFF;
49 uint32_t mode_ctl2 = 0;
52 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disconnecting DAC.\n");
53 NV50DisplayCommand(pScrn, NV50_DAC0_MODE_CTRL + dacOff, mode_ctl);
57 /* Anyone know a more appropriate name? */
58 DisplayModePtr desired_mode = output->crtc->use_native_mode ? output->crtc->native_mode : mode;
61 if (output->crtc->index == 1)
62 mode_ctl |= NV50_DAC_MODE_CTRL_CRTC1;
64 mode_ctl |= NV50_DAC_MODE_CTRL_CRTC0;
66 xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Warning, output has no crtc.\n");
70 if (output->type == OUTPUT_ANALOG) {
73 } else if (output->type == OUTPUT_TV) {
77 if (desired_mode->Flags & V_NHSYNC)
78 mode_ctl2 |= NV50_DAC_MODE_CTRL2_NHSYNC;
80 if (desired_mode->Flags & V_NVSYNC)
81 mode_ctl2 |= NV50_DAC_MODE_CTRL2_NVSYNC;
83 // This wouldn't be necessary, but the server is stupid and calls
84 // nv50_dac_dpms after the output is disconnected, even though the hardware
85 // turns it off automatically.
86 output->SetPowerMode(output, DPMSModeOn);
88 NV50DisplayCommand(pScrn, NV50_DAC0_MODE_CTRL + dacOff, mode_ctl);
90 NV50DisplayCommand(pScrn, NV50_DAC0_MODE_CTRL2 + dacOff, mode_ctl2);
92 output->crtc->SetScaleMode(output->crtc, output->scale_mode);
96 NV50DacSetClockMode(nouveauOutputPtr output, int clock)
98 ScrnInfoPtr pScrn = output->scrn;
99 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50DacSetClockMode is called.\n");
101 NVPtr pNv = NVPTR(pScrn);
102 NVWrite(pNv, NV50_DAC0_CLK_CTRL2 + NV50OrOffset(output) * 0x800, 0);
106 NV50DacSense(nouveauOutputPtr output)
108 switch (output->type) {
118 NV50DacDetect (nouveauOutputPtr output)
120 ScrnInfoPtr pScrn = output->scrn;
121 NVPtr pNv = NVPTR(pScrn);
122 const int scrnIndex = pScrn->scrnIndex;
124 uint32_t load, tmp, tmp2;
126 xf86DrvMsg(scrnIndex, X_PROBED, "Trying load detection on VGA%i ... ",
127 NV50OrOffset(output));
129 NVWrite(pNv, NV50_DAC0_CLK_CTRL1 + NV50OrOffset(output) * 0x800, 0x00000001);
130 tmp2 = NVRead(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
132 NVWrite(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800, NV50_DAC_DPMS_CTRL_DEFAULT_STATE | NV50_DAC_DPMS_CTRL_PENDING);
133 while (NVRead(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800) & NV50_DAC_DPMS_CTRL_PENDING);
134 /* The blob seems to try various patterns after each other. */
135 tmp = (pNv->NVArch == 0x50) ? 420 : 340;
136 NVWrite(pNv, NV50_DAC0_LOAD_CTRL + NV50OrOffset(output) * 0x800, tmp | NV50_DAC_LOAD_CTRL_ACTIVE);
137 /* Why is this needed, load detect is almost instantanious and seemingly reliable for me. */
138 sigstate = xf86BlockSIGIO();
140 xf86UnblockSIGIO(sigstate);
141 load = NVRead(pNv, NV50_DAC0_LOAD_CTRL + NV50OrOffset(output) * 0x800);
142 NVWrite(pNv, NV50_DAC0_LOAD_CTRL + NV50OrOffset(output) * 0x800, 0);
143 NVWrite(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800, NV50_DAC_DPMS_CTRL_PENDING | tmp2);
145 // Use this DAC if all three channels show load.
146 if ((load & NV50_DAC_LOAD_CTRL_PRESENT) == NV50_DAC_LOAD_CTRL_PRESENT) {
147 xf86ErrorF("found one!\n");
151 xf86ErrorF("nothing.\n");
156 NV50DacSetPowerMode(nouveauOutputPtr output, int mode)
158 ScrnInfoPtr pScrn = output->scrn;
159 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50DacSetPowerMode is called with mode %d.\n", mode);
162 NVPtr pNv = NVPTR(pScrn);
165 * DPMSModeOn everything on
166 * DPMSModeStandby hsync disabled, vsync enabled
167 * DPMSModeSuspend hsync enabled, vsync disabled
168 * DPMSModeOff sync disabled
170 while(NVRead(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800) & NV50_DAC_DPMS_CTRL_PENDING);
172 tmp = NVRead(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
174 tmp |= NV50_DAC_DPMS_CTRL_PENDING;
176 if (mode == DPMSModeStandby || mode == DPMSModeOff)
177 tmp |= NV50_DAC_DPMS_CTRL_HSYNC_OFF;
178 if (mode == DPMSModeSuspend || mode == DPMSModeOff)
179 tmp |= NV50_DAC_DPMS_CTRL_VSYNC_OFF;
180 if (mode != DPMSModeOn)
181 tmp |= NV50_DAC_DPMS_CTRL_BLANKED;
182 if (mode == DPMSModeOff)
183 tmp |= NV50_DAC_DPMS_CTRL_OFF;
185 NVWrite(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800, tmp);
189 NV50DacSetFunctionPointers(nouveauOutputPtr output)
191 output->ModeValid = NV50DacModeValid;
192 output->ModeSet = NV50DacModeSet;
193 output->SetClockMode = NV50DacSetClockMode;
194 output->Sense = NV50DacSense;
195 output->Detect = NV50DacDetect;
196 output->SetPowerMode = NV50DacSetPowerMode;