randr12: fix dpms, detect, destroy, save and restore for multiple encoders per connector
[nouveau] / src / nv50_sor.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 NV50SorModeValid(nouveauOutputPtr output, DisplayModePtr mode)
31 {
32         if (mode->Clock > 165000) /* no dual link until we figure it out completely */
33                 return MODE_CLOCK_HIGH;
34
35         if (mode->Clock < 25000)
36                 return MODE_CLOCK_LOW;
37
38         if (mode->Flags & V_DBLSCAN)
39                 return MODE_NO_DBLESCAN;
40
41         if (mode->HDisplay > output->native_mode->HDisplay || mode->VDisplay > output->native_mode->VDisplay)
42                 return MODE_PANEL;
43
44         return MODE_OK;
45 }
46
47 static void
48 NV50SorModeSet(nouveauOutputPtr output, DisplayModePtr mode)
49 {
50         ScrnInfoPtr pScrn = output->scrn;
51         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorModeSet is called.\n");
52
53         const int sorOff = 0x40 * NV50OrOffset(output);
54         uint32_t mode_ctl = NV50_SOR_MODE_CTRL_OFF;
55
56         if (!mode) {
57                 /* Disconnect the SOR */
58                 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disconnecting SOR.\n");
59                 NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
60                 return;
61         }
62
63         /* Anyone know a more appropriate name? */
64         DisplayModePtr desired_mode = output->crtc->use_native_mode ? output->crtc->native_mode : mode;
65
66         if (output->type == OUTPUT_LVDS) {
67                 mode_ctl |= NV50_SOR_MODE_CTRL_LVDS;
68         } else {
69                 mode_ctl |= NV50_SOR_MODE_CTRL_TMDS;
70                 if (desired_mode->Clock > 165000)
71                         mode_ctl |= NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK;
72         }
73
74         if (output->crtc) {
75                 if (output->crtc->index == 1)
76                         mode_ctl |= NV50_SOR_MODE_CTRL_CRTC1;
77                 else
78                         mode_ctl |= NV50_SOR_MODE_CTRL_CRTC0;
79         } else {
80                 xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Warning, output has no crtc.\n");
81                 return;
82         }
83
84         if (desired_mode->Flags & V_NHSYNC)
85                 mode_ctl |= NV50_SOR_MODE_CTRL_NHSYNC;
86
87         if (desired_mode->Flags & V_NVSYNC)
88                 mode_ctl |= NV50_SOR_MODE_CTRL_NVSYNC;
89
90         // This wouldn't be necessary, but the server is stupid and calls
91         // nv50_sor_dpms after the output is disconnected, even though the hardware
92         // turns it off automatically.
93         output->SetPowerMode(output, DPMSModeOn);
94
95         NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
96
97         output->crtc->SetScaleMode(output->crtc, output->scale_mode);
98 }
99
100 static void
101 NV50SorSetClockMode(nouveauOutputPtr output, int clock)
102 {
103         ScrnInfoPtr pScrn = output->scrn;
104         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorSetClockMode is called.\n");
105
106         NVPtr pNv = NVPTR(pScrn);
107         const int limit = 165000;
108
109         /* 0x70000 was a late addition to nv, mentioned as fixing tmds initialisation on certain gpu's. */
110         /* I presume it's some kind of clock setting, but what precisely i do not know. */
111         NVWrite(pNv, NV50_SOR0_CLK_CTRL2 + NV50OrOffset(output) * 0x800, 0x70000 | ((clock > limit) ? 0x101 : 0));
112 }
113
114 static void
115 NV50SorSetClockModeLVDS(nouveauOutputPtr output, int clock)
116 {
117 }
118
119 static int
120 NV50SorSense(nouveauOutputPtr output)
121 {
122         switch (output->type) {
123                 case OUTPUT_TMDS:
124                 case OUTPUT_LVDS:
125                         return output->type;
126                 default:
127                         return OUTPUT_NONE;
128         }
129 }
130
131 static void
132 NV50SorSetPowerMode(nouveauOutputPtr output, int mode)
133 {
134         ScrnInfoPtr pScrn = output->scrn;
135         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorSetPowerMode is called with mode %d.\n", mode);
136
137         NVPtr pNv = NVPTR(pScrn);
138         uint32_t tmp;
139
140         while ((NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800) & NV50_SOR_DPMS_CTRL_PENDING));
141
142         tmp = NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
143         tmp |= NV50_SOR_DPMS_CTRL_PENDING;
144
145         if (mode == DPMSModeOn)
146                 tmp |= NV50_SOR_DPMS_CTRL_MODE_ON;
147         else
148                 tmp &= ~NV50_SOR_DPMS_CTRL_MODE_ON;
149
150         NVWrite(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800, tmp);
151         while ((NVRead(pNv, NV50_SOR0_DPMS_STATE + NV50OrOffset(output) * 0x800) & NV50_SOR_DPMS_STATE_WAIT));
152 }
153
154 static Bool
155 NV50SorDetect(nouveauOutputPtr output)
156 {
157         if (output->type == OUTPUT_LVDS) /* assume connected */
158                 return TRUE;
159
160         return FALSE;
161 }
162
163 void
164 NV50SorSetFunctionPointers(nouveauOutputPtr output)
165 {
166         output->ModeValid = NV50SorModeValid;
167         output->ModeSet = NV50SorModeSet;
168         if (output->type == OUTPUT_TMDS)
169                 output->SetClockMode = NV50SorSetClockMode;
170         else
171                 output->SetClockMode = NV50SorSetClockModeLVDS;
172         output->Sense = NV50SorSense;
173         output->Detect = NV50SorDetect;
174         output->SetPowerMode = NV50SorSetPowerMode;
175 }
176
177 /*
178  * Some misc functions.
179  */
180
181 static DisplayModePtr
182 ReadLVDSNativeMode(ScrnInfoPtr pScrn, const int off)
183 {
184         NVPtr pNv = NVPTR(pScrn);
185         DisplayModePtr mode = xnfcalloc(1, sizeof(DisplayModeRec));
186         const CARD32 size = NVRead(pNv, 0x00610b4c + off);
187         const int width = size & 0x3fff;
188         const int height = (size >> 16) & 0x3fff;
189
190         mode->HDisplay = width;
191         mode->VDisplay = height;
192         mode->Clock = NVRead(pNv, 0x00610ad4 + off) & 0x3fffff;
193
194         /* We should investigate what else is found in these register ranges. */
195         uint32_t unk1 = NVRead(pNv, NV50_CRTC0_DISPLAY_TOTAL_VAL + off);
196         uint32_t unk2 = NVRead(pNv, NV50_CRTC0_SYNC_DURATION_VAL + off);
197         uint32_t unk3 = NVRead(pNv, NV50_CRTC0_SYNC_START_TO_BLANK_END_VAL + off);
198         /*uint32_t unk4 = NVRead(pNv, NV50_CRTC0_MODE_UNK1_VAL + off);*/
199
200         /* Recontruct our mode, so it can be handled normally. */
201         mode->HTotal = (unk1 & 0xFFFF);
202         mode->VTotal = (unk1 >> 16);
203
204         /* Assuming no interlacing. */
205         mode->HSyncStart = mode->HTotal - (unk3 & 0xFFFF) - 1;
206         mode->VSyncStart = mode->VTotal - (unk3 >> 16) - 1;
207
208         mode->HSyncEnd = mode->HSyncStart + (unk2 & 0xFFFF) + 1;
209         mode->VSyncEnd = mode->VSyncStart + (unk2 >> 16) + 1;
210
211         mode->next = mode->prev = NULL;
212         mode->status = MODE_OK;
213         mode->type = M_T_DRIVER | M_T_PREFERRED;
214
215         xf86SetModeDefaultName(mode);
216
217         return mode;
218 }
219
220 DisplayModePtr
221 GetLVDSNativeMode(ScrnInfoPtr pScrn)
222 {
223         NVPtr pNv = NVPTR(pScrn);
224         uint32_t val = NVRead(pNv, NV50_DISPLAY_UNK50_CTRL);
225
226         /* This is rather crude imo, i wonder if it always works. */
227         if ((val & NV50_DISPLAY_UNK50_CTRL_CRTC0_MASK) == NV50_DISPLAY_UNK50_CTRL_CRTC0_ACTIVE) {
228                 return ReadLVDSNativeMode(pScrn, 0);
229         } else if ((val & NV50_DISPLAY_UNK50_CTRL_CRTC1_MASK) == NV50_DISPLAY_UNK50_CTRL_CRTC1_ACTIVE) {
230                 return ReadLVDSNativeMode(pScrn, 0x540);
231         }
232
233         return NULL;
234 }