randr12: separate encoder struct
[nouveau] / src / nv50_dac.c
1 /*
2  * Copyright 2007 NVIDIA, Corporation
3  * Copyright 2008 Maarten Maathuis
4  *
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:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
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
21  * SOFTWARE.
22  */
23
24 #include "nouveau_modeset.h"
25 #include "nouveau_crtc.h"
26 #include "nouveau_output.h"
27 #include "nouveau_connector.h"
28
29 static int
30 NV50DacModeValid(nouveauOutputPtr output, DisplayModePtr mode)
31 {
32         if (mode->Clock > 400000)
33                 return MODE_CLOCK_HIGH;
34
35         if (mode->Clock < 25000)
36                 return MODE_CLOCK_LOW;
37
38         return MODE_OK;
39 }
40
41 static void
42 NV50DacModeSet(nouveauOutputPtr output, DisplayModePtr mode)
43 {
44         ScrnInfoPtr pScrn = output->scrn;
45         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50DacModeSet is called.\n");
46
47         const int dacOff = 0x80 * NV50OrOffset(output);
48         uint32_t mode_ctl = NV50_DAC_MODE_CTRL_OFF;
49         uint32_t mode_ctl2 = 0;
50
51         if (!mode) {
52                 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disconnecting DAC.\n");
53                 NV50DisplayCommand(pScrn, NV50_DAC0_MODE_CTRL + dacOff, mode_ctl);
54                 return;
55         }
56
57         /* Anyone know a more appropriate name? */
58         DisplayModePtr desired_mode = output->crtc->use_native_mode ? output->crtc->native_mode : mode;
59
60         if (output->crtc) {
61                 if (output->crtc->index == 1)
62                         mode_ctl |= NV50_DAC_MODE_CTRL_CRTC1;
63                 else
64                         mode_ctl |= NV50_DAC_MODE_CTRL_CRTC0;
65         } else {
66                 xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Warning, output has no crtc.\n");
67                 return;
68         }
69
70         if (output->type == OUTPUT_ANALOG) {
71                 /* What is this? */
72                 mode_ctl |= 0x40;
73         } else if (output->type == OUTPUT_TV) {
74                 mode_ctl |= 0x100;
75         }
76
77         if (desired_mode->Flags & V_NHSYNC)
78                 mode_ctl2 |= NV50_DAC_MODE_CTRL2_NHSYNC;
79
80         if (desired_mode->Flags & V_NVSYNC)
81                 mode_ctl2 |= NV50_DAC_MODE_CTRL2_NVSYNC;
82
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);
87
88         NV50DisplayCommand(pScrn, NV50_DAC0_MODE_CTRL + dacOff, mode_ctl);
89
90         NV50DisplayCommand(pScrn, NV50_DAC0_MODE_CTRL2 + dacOff, mode_ctl2);
91
92         output->crtc->SetScaleMode(output->crtc, output->scale_mode);
93 }
94
95 static void
96 NV50DacSetClockMode(nouveauOutputPtr output, int clock)
97 {
98         ScrnInfoPtr pScrn = output->scrn;
99         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50DacSetClockMode is called.\n");
100
101         NVPtr pNv = NVPTR(pScrn);
102         NVWrite(pNv, NV50_DAC0_CLK_CTRL2 + NV50OrOffset(output) * 0x800, 0);
103 }
104
105 static int
106 NV50DacSense(nouveauOutputPtr output)
107 {
108         switch (output->type) {
109                 case OUTPUT_ANALOG:
110                 case OUTPUT_TV:
111                         return output->type;
112                 default:
113                         return OUTPUT_NONE;
114         }
115 }
116
117 static Bool
118 NV50DacDetect (nouveauOutputPtr output)
119 {
120         ScrnInfoPtr pScrn = output->scrn;
121         NVPtr pNv = NVPTR(pScrn);
122         const int scrnIndex = pScrn->scrnIndex;
123         int sigstate;
124         uint32_t load, tmp, tmp2;
125
126         xf86DrvMsg(scrnIndex, X_PROBED, "Trying load detection on VGA%i ... ",
127                 NV50OrOffset(output));
128
129         NVWrite(pNv, NV50_DAC0_CLK_CTRL1 + NV50OrOffset(output) * 0x800, 0x00000001);
130         tmp2 = NVRead(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
131
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();
139         usleep(45000);
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);
144
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");
148                 return TRUE;
149         }
150
151         xf86ErrorF("nothing.\n");
152         return FALSE;
153 }
154
155 static void
156 NV50DacSetPowerMode(nouveauOutputPtr output, int mode)
157 {
158         ScrnInfoPtr pScrn = output->scrn;
159         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50DacSetPowerMode is called with mode %d.\n", mode);
160
161         uint32_t tmp;
162         NVPtr pNv = NVPTR(pScrn);
163
164         /*
165          * DPMSModeOn       everything on
166          * DPMSModeStandby  hsync disabled, vsync enabled
167          * DPMSModeSuspend  hsync enabled, vsync disabled
168          * DPMSModeOff      sync disabled
169          */
170         while(NVRead(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800) & NV50_DAC_DPMS_CTRL_PENDING);
171
172         tmp = NVRead(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
173         tmp &= ~0x7f;
174         tmp |= NV50_DAC_DPMS_CTRL_PENDING;
175
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;
184
185         NVWrite(pNv, NV50_DAC0_DPMS_CTRL + NV50OrOffset(output) * 0x800, tmp);
186 }
187
188 void
189 NV50DacSetFunctionPointers(nouveauOutputPtr output)
190 {
191         output->ModeValid = NV50DacModeValid;
192         output->ModeSet = NV50DacModeSet;
193         output->SetClockMode = NV50DacSetClockMode;
194         output->Sense = NV50DacSense;
195         output->Detect = NV50DacDetect;
196         output->SetPowerMode = NV50DacSetPowerMode;
197 }