randr12 + NV50: Misc changes.
[nouveau] / src / nv50_dac.c
1 /*
2  * Copyright (c) 2007 NVIDIA, Corporation
3  *
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:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
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.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #ifdef ENABLE_RANDR12
29
30 #include <unistd.h>
31
32 #define DPMS_SERVER
33 #include <X11/extensions/dpms.h>
34
35 #include "nv_include.h"
36 #include "nv50_display.h"
37 #include "nv50_output.h"
38
39 static void
40 NV50DacSetPClk(xf86OutputPtr output, int pclk)
41 {
42     NVPtr pNv = NVPTR(output->scrn);
43     NV50OutputPrivPtr pPriv = output->driver_private;
44     const int orOff = 0x800 * pPriv->or;
45
46     pNv->REGS[(0x00614280+orOff)/4] = 0;
47 }
48
49 static void
50 NV50DacDPMSSet(xf86OutputPtr output, int mode)
51 {
52     NVPtr pNv = NVPTR(output->scrn);
53     NV50OutputPrivPtr pPriv = output->driver_private;
54     const int off = 0x800 * pPriv->or;
55     CARD32 tmp;
56
57     /*
58      * DPMSModeOn       everything on
59      * DPMSModeStandby  hsync disabled, vsync enabled
60      * DPMSModeSuspend  hsync enabled, vsync disabled
61      * DPMSModeOff      sync disabled
62      */
63     while(pNv->REGS[(0x0061A004+off)/4] & 0x80000000);
64
65     tmp = pNv->REGS[(0x0061A004+off)/4];
66     tmp &= ~0x7f;
67     tmp |= 0x80000000;
68
69     if(mode == DPMSModeStandby || mode == DPMSModeOff)
70         tmp |= 1;
71     if(mode == DPMSModeSuspend || mode == DPMSModeOff)
72         tmp |= 4;
73     if(mode != DPMSModeOn)
74         tmp |= 0x10;
75     if(mode == DPMSModeOff)
76         tmp |= 0x40;
77
78     pNv->REGS[(0x0061A004+off)/4] = tmp;
79 }
80
81 Bool
82 NV50DacModeFixup(xf86OutputPtr output, DisplayModePtr mode,
83                  DisplayModePtr adjusted_mode)
84 {
85         return TRUE;
86 }
87
88 static void
89 NV50DacModeSet(xf86OutputPtr output, DisplayModePtr mode,
90               DisplayModePtr adjusted_mode)
91 {
92     ScrnInfoPtr pScrn = output->scrn;
93     NV50OutputPrivPtr pPriv = output->driver_private;
94     const int dacOff = 0x80 * pPriv->or;
95
96     if(!adjusted_mode) {
97         NV50DisplayCommand(pScrn, 0x400 + dacOff, 0);
98         return;
99     }
100
101     // This wouldn't be necessary, but the server is stupid and calls
102     // NV50DacDPMSSet after the output is disconnected, even though the hardware
103     // turns it off automatically.
104     NV50DacDPMSSet(output, DPMSModeOn);
105
106         NV50DisplayCommand(pScrn, 0x400 + dacOff,
107                 (NV50CrtcGetHead(output->crtc) == HEAD0 ? 1 : 2) | 0x40);
108
109         NV50DisplayCommand(pScrn, 0x404 + dacOff,
110                 (adjusted_mode->Flags & V_NHSYNC) ? 1 : 0 |
111                 (adjusted_mode->Flags & V_NVSYNC) ? 2 : 0);
112
113     NV50CrtcSetScale(output->crtc, adjusted_mode, NV50_SCALE_OFF);
114 }
115
116 /*
117  * Perform DAC load detection to determine if there is a connected display.
118  */
119 static xf86OutputStatus
120 NV50DacDetect(xf86OutputPtr output)
121 {
122     NV50OutputPrivPtr pPriv = output->driver_private;
123
124     /* Assume physical status isn't going to change before the BlockHandler */
125     if(pPriv->cached_status != XF86OutputStatusUnknown)
126         return pPriv->cached_status;
127
128     NV50OutputPartnersDetect(output, pPriv->partner, pPriv->i2c);
129     return pPriv->cached_status;
130 }
131
132 Bool
133 NV50DacLoadDetect(xf86OutputPtr output)
134 {
135     ScrnInfoPtr pScrn = output->scrn;
136     NVPtr pNv = NVPTR(pScrn);
137     NV50OutputPrivPtr pPriv = output->driver_private;
138     const int scrnIndex = pScrn->scrnIndex;
139     const int dacOff = 2048 * pPriv->or;
140     CARD32 load, tmp, tmp2;
141
142     xf86DrvMsg(scrnIndex, X_PROBED, "Trying load detection on VGA%i ... ",
143             pPriv->or);
144
145     pNv->REGS[(0x0061A010+dacOff)/4] = 0x00000001;
146     tmp2 = pNv->REGS[(0x0061A004+dacOff)/4];
147     pNv->REGS[(0x0061A004+dacOff)/4] = 0x80150000;
148     while(pNv->REGS[(0x0061A004+dacOff)/4] & 0x80000000);
149     tmp = pNv->NVArch == 0x50 ? 420 : 340;
150     pNv->REGS[(0x0061A00C+dacOff)/4] = tmp | 0x100000;
151     usleep(4500);
152     load = pNv->REGS[(0x0061A00C+dacOff)/4];
153     pNv->REGS[(0x0061A00C+dacOff)/4] = 0;
154     pNv->REGS[(0x0061A004+dacOff)/4] = 0x80000000 | tmp2;
155
156     // Use this DAC if all three channels show load.
157     if((load & 0x38000000) == 0x38000000) {
158         xf86ErrorF("found one!\n");
159         return TRUE;
160     }
161
162     xf86ErrorF("nothing.\n");
163     return FALSE;
164 }
165
166 static void
167 NV50DacDestroy(xf86OutputPtr output)
168 {
169     NV50OutputDestroy(output);
170
171     xfree(output->driver_private);
172     output->driver_private = NULL;
173 }
174
175 static const xf86OutputFuncsRec NV50DacOutputFuncs = {
176     .dpms = NV50DacDPMSSet,
177     .save = NULL,
178     .restore = NULL,
179     .mode_valid = NV50OutputModeValid,
180     .mode_fixup = NV50DacModeFixup,
181     .prepare = NV50OutputPrepare,
182     .commit = NV50OutputCommit,
183     .mode_set = NV50DacModeSet,
184     .detect = NV50DacDetect,
185     .get_modes = NV50OutputGetDDCModes,
186     .destroy = NV50DacDestroy,
187 };
188
189 xf86OutputPtr
190 NV50CreateDac(ScrnInfoPtr pScrn, ORNum or)
191 {
192     NV50OutputPrivPtr pPriv = xnfcalloc(sizeof(*pPriv), 1);
193     xf86OutputPtr output;
194     char orName[5];
195
196     if(!pPriv)
197         return FALSE;
198
199     snprintf(orName, 5, "VGA%i", or);
200     output = xf86OutputCreate(pScrn, &NV50DacOutputFuncs, orName);
201
202     pPriv->type = DAC;
203     pPriv->or = or;
204     pPriv->cached_status = XF86OutputStatusUnknown;
205     pPriv->set_pclk = NV50DacSetPClk;
206     output->driver_private = pPriv;
207     output->interlaceAllowed = TRUE;
208     output->doubleScanAllowed = TRUE;
209
210     return output;
211 }
212
213 #endif /* ENABLE_RANDR12 */