Commit | Line | Data |
---|---|---|
6ee73861 BS |
1 | /* |
2 | * Copyright (C) 2008 Maarten Maathuis. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * Permission is hereby granted, free of charge, to any person obtaining | |
6 | * a copy of this software and associated documentation files (the | |
7 | * "Software"), to deal in the Software without restriction, including | |
8 | * without limitation the rights to use, copy, modify, merge, publish, | |
9 | * distribute, sublicense, and/or sell copies of the Software, and to | |
10 | * permit persons to whom the Software is furnished to do so, subject to | |
11 | * the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice (including the | |
14 | * next paragraph) shall be included in all copies or substantial | |
15 | * portions of the Software. | |
16 | * | |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | |
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
24 | * | |
25 | */ | |
26 | ||
27 | #include "drmP.h" | |
28 | #include "drm_crtc_helper.h" | |
29 | ||
30 | #define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) | |
31 | #include "nouveau_reg.h" | |
32 | #include "nouveau_drv.h" | |
33 | #include "nouveau_dma.h" | |
34 | #include "nouveau_encoder.h" | |
35 | #include "nouveau_connector.h" | |
36 | #include "nouveau_crtc.h" | |
37 | #include "nv50_display.h" | |
38 | ||
39 | static void | |
ec7fc4a1 | 40 | nv50_sor_disconnect(struct drm_encoder *encoder) |
6ee73861 | 41 | { |
ec7fc4a1 BS |
42 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); |
43 | struct drm_device *dev = encoder->dev; | |
6ee73861 BS |
44 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
45 | struct nouveau_channel *evo = dev_priv->evo; | |
46 | int ret; | |
47 | ||
ec7fc4a1 BS |
48 | if (!nv_encoder->crtc) |
49 | return; | |
835aadbe | 50 | nv50_crtc_blank(nouveau_crtc(nv_encoder->crtc), true); |
ec7fc4a1 | 51 | |
ef2bb506 | 52 | NV_DEBUG_KMS(dev, "Disconnecting SOR %d\n", nv_encoder->or); |
6ee73861 | 53 | |
835aadbe | 54 | ret = RING_SPACE(evo, 4); |
6ee73861 BS |
55 | if (ret) { |
56 | NV_ERROR(dev, "no space while disconnecting SOR\n"); | |
57 | return; | |
58 | } | |
59 | BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); | |
835aadbe BS |
60 | OUT_RING (evo, 0); |
61 | BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); | |
62 | OUT_RING (evo, 0); | |
ec7fc4a1 BS |
63 | |
64 | nv_encoder->crtc = NULL; | |
65 | nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; | |
6ee73861 BS |
66 | } |
67 | ||
6ee73861 BS |
68 | static void |
69 | nv50_sor_dpms(struct drm_encoder *encoder, int mode) | |
70 | { | |
71 | struct drm_device *dev = encoder->dev; | |
72 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
16226536 | 73 | struct drm_encoder *enc; |
6ee73861 BS |
74 | uint32_t val; |
75 | int or = nv_encoder->or; | |
76 | ||
ec7fc4a1 | 77 | NV_DEBUG_KMS(dev, "or %d type %d mode %d\n", or, nv_encoder->dcb->type, mode); |
6ee73861 | 78 | |
16226536 BS |
79 | nv_encoder->last_dpms = mode; |
80 | list_for_each_entry(enc, &dev->mode_config.encoder_list, head) { | |
81 | struct nouveau_encoder *nvenc = nouveau_encoder(enc); | |
82 | ||
83 | if (nvenc == nv_encoder || | |
ec7fc4a1 BS |
84 | (nvenc->dcb->type != OUTPUT_TMDS && |
85 | nvenc->dcb->type != OUTPUT_LVDS && | |
86 | nvenc->dcb->type != OUTPUT_DP) || | |
16226536 BS |
87 | nvenc->dcb->or != nv_encoder->dcb->or) |
88 | continue; | |
89 | ||
90 | if (nvenc->last_dpms == DRM_MODE_DPMS_ON) | |
91 | return; | |
92 | } | |
93 | ||
6ee73861 BS |
94 | /* wait for it to be done */ |
95 | if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_CTRL(or), | |
96 | NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) { | |
97 | NV_ERROR(dev, "timeout: SOR_DPMS_CTRL_PENDING(%d) == 0\n", or); | |
98 | NV_ERROR(dev, "SOR_DPMS_CTRL(%d) = 0x%08x\n", or, | |
99 | nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or))); | |
100 | } | |
101 | ||
102 | val = nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or)); | |
103 | ||
104 | if (mode == DRM_MODE_DPMS_ON) | |
105 | val |= NV50_PDISPLAY_SOR_DPMS_CTRL_ON; | |
106 | else | |
107 | val &= ~NV50_PDISPLAY_SOR_DPMS_CTRL_ON; | |
108 | ||
109 | nv_wr32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or), val | | |
110 | NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING); | |
111 | if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(or), | |
112 | NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) { | |
113 | NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", or); | |
114 | NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", or, | |
115 | nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(or))); | |
116 | } | |
117 | ||
1f403d9c BS |
118 | if (nv_encoder->dcb->type == OUTPUT_DP) { |
119 | struct nouveau_i2c_chan *auxch; | |
120 | ||
121 | auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); | |
122 | if (!auxch) | |
123 | return; | |
124 | ||
125 | if (mode == DRM_MODE_DPMS_ON) { | |
126 | u8 status = DP_SET_POWER_D0; | |
127 | nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); | |
128 | nouveau_dp_link_train(encoder); | |
129 | } else { | |
130 | u8 status = DP_SET_POWER_D3; | |
131 | nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); | |
132 | } | |
133 | } | |
6ee73861 BS |
134 | } |
135 | ||
136 | static void | |
137 | nv50_sor_save(struct drm_encoder *encoder) | |
138 | { | |
139 | NV_ERROR(encoder->dev, "!!\n"); | |
140 | } | |
141 | ||
142 | static void | |
143 | nv50_sor_restore(struct drm_encoder *encoder) | |
144 | { | |
145 | NV_ERROR(encoder->dev, "!!\n"); | |
146 | } | |
147 | ||
148 | static bool | |
149 | nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, | |
150 | struct drm_display_mode *adjusted_mode) | |
151 | { | |
152 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
153 | struct nouveau_connector *connector; | |
154 | ||
ef2bb506 | 155 | NV_DEBUG_KMS(encoder->dev, "or %d\n", nv_encoder->or); |
6ee73861 BS |
156 | |
157 | connector = nouveau_encoder_connector_get(nv_encoder); | |
158 | if (!connector) { | |
159 | NV_ERROR(encoder->dev, "Encoder has no connector\n"); | |
160 | return false; | |
161 | } | |
162 | ||
163 | if (connector->scaling_mode != DRM_MODE_SCALE_NONE && | |
164 | connector->native_mode) { | |
165 | int id = adjusted_mode->base.id; | |
166 | *adjusted_mode = *connector->native_mode; | |
167 | adjusted_mode->base.id = id; | |
168 | } | |
169 | ||
170 | return true; | |
171 | } | |
172 | ||
173 | static void | |
174 | nv50_sor_prepare(struct drm_encoder *encoder) | |
175 | { | |
176 | } | |
177 | ||
178 | static void | |
179 | nv50_sor_commit(struct drm_encoder *encoder) | |
180 | { | |
181 | } | |
182 | ||
183 | static void | |
184 | nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, | |
185 | struct drm_display_mode *adjusted_mode) | |
186 | { | |
187 | struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; | |
188 | struct nouveau_channel *evo = dev_priv->evo; | |
189 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
190 | struct drm_device *dev = encoder->dev; | |
191 | struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); | |
192 | uint32_t mode_ctl = 0; | |
193 | int ret; | |
194 | ||
04412921 BS |
195 | NV_DEBUG_KMS(dev, "or %d type %d -> crtc %d\n", |
196 | nv_encoder->or, nv_encoder->dcb->type, crtc->index); | |
6ee73861 BS |
197 | |
198 | nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); | |
199 | ||
200 | switch (nv_encoder->dcb->type) { | |
201 | case OUTPUT_TMDS: | |
202 | if (nv_encoder->dcb->sorconf.link & 1) { | |
203 | if (adjusted_mode->clock < 165000) | |
204 | mode_ctl = 0x0100; | |
205 | else | |
206 | mode_ctl = 0x0500; | |
207 | } else | |
208 | mode_ctl = 0x0200; | |
209 | break; | |
210 | case OUTPUT_DP: | |
6f335a7a | 211 | mode_ctl |= (nv_encoder->dp.mc_unknown << 16); |
6ee73861 BS |
212 | if (nv_encoder->dcb->sorconf.link & 1) |
213 | mode_ctl |= 0x00000800; | |
214 | else | |
215 | mode_ctl |= 0x00000900; | |
216 | break; | |
217 | default: | |
218 | break; | |
219 | } | |
220 | ||
221 | if (crtc->index == 1) | |
222 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC1; | |
223 | else | |
224 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0; | |
225 | ||
226 | if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) | |
227 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC; | |
228 | ||
229 | if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) | |
230 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC; | |
231 | ||
232 | ret = RING_SPACE(evo, 2); | |
233 | if (ret) { | |
234 | NV_ERROR(dev, "no space while connecting SOR\n"); | |
235 | return; | |
236 | } | |
237 | BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); | |
238 | OUT_RING(evo, mode_ctl); | |
ec7fc4a1 BS |
239 | |
240 | nv_encoder->crtc = encoder->crtc; | |
241 | } | |
242 | ||
243 | static struct drm_crtc * | |
244 | nv50_sor_crtc_get(struct drm_encoder *encoder) | |
245 | { | |
246 | return nouveau_encoder(encoder)->crtc; | |
6ee73861 BS |
247 | } |
248 | ||
249 | static const struct drm_encoder_helper_funcs nv50_sor_helper_funcs = { | |
250 | .dpms = nv50_sor_dpms, | |
251 | .save = nv50_sor_save, | |
252 | .restore = nv50_sor_restore, | |
253 | .mode_fixup = nv50_sor_mode_fixup, | |
254 | .prepare = nv50_sor_prepare, | |
255 | .commit = nv50_sor_commit, | |
256 | .mode_set = nv50_sor_mode_set, | |
ec7fc4a1 BS |
257 | .get_crtc = nv50_sor_crtc_get, |
258 | .detect = NULL, | |
259 | .disable = nv50_sor_disconnect | |
6ee73861 BS |
260 | }; |
261 | ||
262 | static void | |
263 | nv50_sor_destroy(struct drm_encoder *encoder) | |
264 | { | |
265 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
266 | ||
267 | if (!encoder) | |
268 | return; | |
269 | ||
ef2bb506 | 270 | NV_DEBUG_KMS(encoder->dev, "\n"); |
6ee73861 BS |
271 | |
272 | drm_encoder_cleanup(encoder); | |
273 | ||
274 | kfree(nv_encoder); | |
275 | } | |
276 | ||
277 | static const struct drm_encoder_funcs nv50_sor_encoder_funcs = { | |
278 | .destroy = nv50_sor_destroy, | |
279 | }; | |
280 | ||
281 | int | |
8f1a6086 | 282 | nv50_sor_create(struct drm_connector *connector, struct dcb_entry *entry) |
6ee73861 BS |
283 | { |
284 | struct nouveau_encoder *nv_encoder = NULL; | |
8f1a6086 | 285 | struct drm_device *dev = connector->dev; |
6ee73861 | 286 | struct drm_encoder *encoder; |
6ee73861 BS |
287 | int type; |
288 | ||
ef2bb506 | 289 | NV_DEBUG_KMS(dev, "\n"); |
6ee73861 BS |
290 | |
291 | switch (entry->type) { | |
292 | case OUTPUT_TMDS: | |
8f1a6086 | 293 | case OUTPUT_DP: |
6ee73861 BS |
294 | type = DRM_MODE_ENCODER_TMDS; |
295 | break; | |
296 | case OUTPUT_LVDS: | |
6ee73861 | 297 | type = DRM_MODE_ENCODER_LVDS; |
6ee73861 BS |
298 | break; |
299 | default: | |
300 | return -EINVAL; | |
301 | } | |
302 | ||
303 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | |
304 | if (!nv_encoder) | |
305 | return -ENOMEM; | |
306 | encoder = to_drm_encoder(nv_encoder); | |
307 | ||
308 | nv_encoder->dcb = entry; | |
309 | nv_encoder->or = ffs(entry->or) - 1; | |
ec7fc4a1 | 310 | nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; |
6ee73861 BS |
311 | |
312 | drm_encoder_init(dev, encoder, &nv50_sor_encoder_funcs, type); | |
313 | drm_encoder_helper_add(encoder, &nv50_sor_helper_funcs); | |
314 | ||
315 | encoder->possible_crtcs = entry->heads; | |
316 | encoder->possible_clones = 0; | |
317 | ||
6f335a7a | 318 | if (nv_encoder->dcb->type == OUTPUT_DP) { |
afa3b4c3 BS |
319 | int or = nv_encoder->or, link = !(entry->dpconf.sor.link & 1); |
320 | uint32_t tmp; | |
6f335a7a | 321 | |
becd2142 | 322 | tmp = nv_rd32(dev, 0x61c700 + (or * 0x800)); |
6f335a7a | 323 | |
afa3b4c3 | 324 | switch ((tmp & 0x00000f00) >> 8) { |
6f335a7a BS |
325 | case 8: |
326 | case 9: | |
afa3b4c3 BS |
327 | nv_encoder->dp.mc_unknown = (tmp & 0x000f0000) >> 16; |
328 | tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); | |
329 | nv_encoder->dp.unk0 = tmp & 0x000001fc; | |
330 | tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link)); | |
331 | nv_encoder->dp.unk1 = tmp & 0x010f7f3f; | |
6f335a7a BS |
332 | break; |
333 | default: | |
334 | break; | |
335 | } | |
e60a9df3 BS |
336 | |
337 | if (!nv_encoder->dp.mc_unknown) | |
338 | nv_encoder->dp.mc_unknown = 5; | |
6f335a7a BS |
339 | } |
340 | ||
8f1a6086 | 341 | drm_mode_connector_attach_encoder(connector, encoder); |
6ee73861 BS |
342 | return 0; |
343 | } |