NV50: Forgot some more stuff.
[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 #include <unistd.h>
25
26 #define DPMS_SERVER
27 #include <X11/extensions/dpms.h>
28
29 #include "nv_include.h"
30 #include "nv50_display.h"
31 #include "nv50_output.h"
32
33 void
34 NV50DacSetPClk(xf86OutputPtr output, int pclk)
35 {
36         ScrnInfoPtr pScrn = output->scrn;
37         NVPtr pNv = NVPTR(pScrn);
38         NVWrite(pNv, 0x00614280 + NV50OrOffset(output) * 0x800, 0);
39 }
40
41 static void
42 NV50DacDPMSSet(xf86OutputPtr output, int mode)
43 {
44         CARD32 tmp;
45         ScrnInfoPtr pScrn = output->scrn;
46         NVPtr pNv = NVPTR(pScrn);
47
48         /*
49          * DPMSModeOn       everything on
50          * DPMSModeStandby  hsync disabled, vsync enabled
51          * DPMSModeSuspend  hsync enabled, vsync disabled
52          * DPMSModeOff      sync disabled
53          */
54         while(NVRead(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800) & NV50_DAC_DPMS_CTRL_PENDING);
55
56         tmp = NVRead(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
57         tmp &= ~0x7f;
58         tmp |= NV50_DAC_DPMS_CTRL_PENDING;
59
60         if(mode == DPMSModeStandby || mode == DPMSModeOff)
61                 tmp |= NV50_DAC_DPMS_CTRL_HSYNC_OFF;
62         if(mode == DPMSModeSuspend || mode == DPMSModeOff)
63                 tmp |= NV50_DAC_DPMS_CTRL_VSYNC_OFF;
64         if(mode != DPMSModeOn)
65                 tmp |= NV50_DAC_DPMS_CTRL_BLANK;
66         if(mode == DPMSModeOff)
67                 tmp |= NV50_DAC_DPMS_CTRL_OFF;
68
69         NVWrite(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800, tmp);
70 }
71
72 Bool
73 NV50DacModeFixup(xf86OutputPtr output, DisplayModePtr mode,
74                  DisplayModePtr adjusted_mode)
75 {
76         return TRUE;
77 }
78
79 static void
80 NV50DacModeSet(xf86OutputPtr output, DisplayModePtr mode,
81                 DisplayModePtr adjusted_mode)
82 {
83         ScrnInfoPtr pScrn = output->scrn;
84         const int dacOff = 0x80 * NV50OrOffset(output);
85         uint32_t mode_ctl = NV50_DAC_MODE_CTRL_OFF;
86         uint32_t mode_ctl2 = 0;
87
88         if (!adjusted_mode) {
89                 NV50DisplayCommand(pScrn, NV50_DAC0_MODE_CTRL + dacOff, mode_ctl);
90                 return;
91         }
92
93         if (output->crtc) {
94                 NVCrtcPrivatePtr nv_crtc = output->crtc->driver_private;
95                 if (nv_crtc->head == 1)
96                         mode_ctl |= NV50_DAC_MODE_CTRL_CRTC1;
97                 else
98                         mode_ctl |= NV50_DAC_MODE_CTRL_CRTC0;
99         } else {
100                 return;
101         }
102
103         /* What is this? */
104         mode_ctl |= 0x40;
105
106         if (adjusted_mode->Flags & V_NHSYNC)
107                 mode_ctl2 |= NV50_DAC_MODE_CTRL2_NHSYNC;
108
109         if (adjusted_mode->Flags & V_NVSYNC)
110                 mode_ctl2 |= NV50_DAC_MODE_CTRL2_NVSYNC;
111
112         // This wouldn't be necessary, but the server is stupid and calls
113         // NV50DacDPMSSet after the output is disconnected, even though the hardware
114         // turns it off automatically.
115         NV50DacDPMSSet(output, DPMSModeOn);
116
117         NV50DisplayCommand(pScrn, NV50_DAC0_MODE_CTRL + dacOff, mode_ctl);
118
119         NV50DisplayCommand(pScrn, NV50_DAC0_MODE_CTRL2 + dacOff, mode_ctl2);
120
121         NV50CrtcSetScale(output->crtc, adjusted_mode, SCALE_PANEL);
122 }
123
124 /*
125  * Perform DAC load detection to determine if there is a connected display.
126  */
127 static xf86OutputStatus
128 NV50DacDetect(xf86OutputPtr output)
129 {
130         NVOutputPrivatePtr nv_output = output->driver_private;
131         xf86MonPtr ddc_mon;
132
133         if (nv_output->pDDCBus == NULL)
134                 return XF86OutputStatusDisconnected;
135
136         ddc_mon = NV50OutputGetEDID(output, nv_output->pDDCBus);
137         if (!ddc_mon && !NV50DacLoadDetect(output))
138                 return XF86OutputStatusDisconnected;
139
140         if (ddc_mon && ddc_mon->features.input_type) /* DVI? */
141                 return XF86OutputStatusDisconnected;
142
143         if (ddc_mon)
144                 xf86OutputSetEDID(output, ddc_mon);
145
146         return XF86OutputStatusConnected;
147 }
148
149 Bool
150 NV50DacLoadDetect(xf86OutputPtr output)
151 {
152         ScrnInfoPtr pScrn = output->scrn;
153         NVPtr pNv = NVPTR(pScrn);
154         const int scrnIndex = pScrn->scrnIndex;
155         int sigstate;
156         CARD32 load, tmp, tmp2;
157
158         xf86DrvMsg(scrnIndex, X_PROBED, "Trying load detection on VGA%i ... ",
159                 NV50OrOffset(output));
160
161         NVWrite(pNv, 0x0061a010 + NV50OrOffset(output) * 0x800, 0x00000001);
162         tmp2 = NVRead(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
163
164         NVWrite(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800, 0x00150000 | NV50_DAC_DPMS_CTRL_PENDING);
165         while(NVRead(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800) & NV50_DAC_DPMS_CTRL_PENDING);
166         tmp = (pNv->NVArch == 0x50) ? 420 : 340;
167         NVWrite(pNv, 0x0061a00c + NV50OrOffset(output) * 0x800, tmp | 0x100000);
168         sigstate = xf86BlockSIGIO();
169         usleep(45000);
170         xf86UnblockSIGIO(sigstate);
171         load = NVRead(pNv, 0x0061a00c + NV50OrOffset(output) * 0x800);
172         NVWrite(pNv, 0x0061a00c + NV50OrOffset(output) * 0x800, 0);
173         NVWrite(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800, NV50_DAC_DPMS_CTRL_PENDING | tmp2);
174
175         // Use this DAC if all three channels show load.
176         if((load & 0x38000000) == 0x38000000) {
177                 xf86ErrorF("found one!\n");
178                 return TRUE;
179         }
180
181         xf86ErrorF("nothing.\n");
182         return FALSE;
183 }
184
185 static void
186 NV50DacDestroy(xf86OutputPtr output)
187 {
188         NV50OutputDestroy(output);
189
190         xfree(output->driver_private);
191         output->driver_private = NULL;
192 }
193
194 static const xf86OutputFuncsRec NV50DacOutputFuncs = {
195         .dpms = NV50DacDPMSSet,
196         .save = NULL,
197         .restore = NULL,
198         .mode_valid = NV50OutputModeValid,
199         .mode_fixup = NV50DacModeFixup,
200         .prepare = NV50OutputPrepare,
201         .commit = NV50OutputCommit,
202         .mode_set = NV50DacModeSet,
203         .detect = NV50DacDetect,
204         .get_modes = NV50OutputGetDDCModes,
205         .destroy = NV50DacDestroy,
206 };
207
208 const xf86OutputFuncsRec * nv50_get_analog_output_funcs()
209 {
210         return &NV50DacOutputFuncs;
211 }
212