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