]>
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 | ||
8348f36d | 27 | #define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) |
6ee73861 BS |
28 | #include "nv50_display.h" |
29 | #include "nouveau_crtc.h" | |
30 | #include "nouveau_encoder.h" | |
31 | #include "nouveau_connector.h" | |
32 | #include "nouveau_fb.h" | |
4abe3520 | 33 | #include "nouveau_fbcon.h" |
a8eaebc6 | 34 | #include "nouveau_ramht.h" |
35bcf5d5 | 35 | #include "nouveau_software.h" |
760285e7 | 36 | #include <drm/drm_crtc_helper.h> |
6ee73861 | 37 | |
19b7fc7b | 38 | static void nv50_display_isr(struct drm_device *); |
f13e435c | 39 | static void nv50_display_bh(unsigned long); |
19b7fc7b | 40 | |
8597a1ba BS |
41 | static inline int |
42 | nv50_sor_nr(struct drm_device *dev) | |
43 | { | |
44 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
45 | ||
46 | if (dev_priv->chipset < 0x90 || | |
47 | dev_priv->chipset == 0x92 || | |
48 | dev_priv->chipset == 0xa0) | |
49 | return 2; | |
50 | ||
51 | return 4; | |
52 | } | |
53 | ||
e495d0d7 BS |
54 | u32 |
55 | nv50_display_active_crtcs(struct drm_device *dev) | |
56 | { | |
57 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
58 | u32 mask = 0; | |
59 | int i; | |
60 | ||
61 | if (dev_priv->chipset < 0x90 || | |
62 | dev_priv->chipset == 0x92 || | |
63 | dev_priv->chipset == 0xa0) { | |
64 | for (i = 0; i < 2; i++) | |
65 | mask |= nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_C(i)); | |
66 | } else { | |
67 | for (i = 0; i < 4; i++) | |
68 | mask |= nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_C(i)); | |
69 | } | |
70 | ||
71 | for (i = 0; i < 3; i++) | |
72 | mask |= nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_C(i)); | |
73 | ||
74 | return mask & 3; | |
75 | } | |
76 | ||
0f6ea564 BS |
77 | static int |
78 | evo_icmd(struct drm_device *dev, int ch, u32 mthd, u32 data) | |
79 | { | |
80 | int ret = 0; | |
0f6ea564 BS |
81 | nv_mask(dev, 0x610300 + (ch * 0x08), 0x00000001, 0x00000001); |
82 | nv_wr32(dev, 0x610304 + (ch * 0x08), data); | |
83 | nv_wr32(dev, 0x610300 + (ch * 0x08), 0x80000001 | mthd); | |
84 | if (!nv_wait(dev, 0x610300 + (ch * 0x08), 0x80000000, 0x00000000)) | |
85 | ret = -EBUSY; | |
020c6bf3 BS |
86 | if (ret || (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)) |
87 | NV_INFO(dev, "EvoPIO: %d 0x%04x 0x%08x\n", ch, mthd, data); | |
0f6ea564 BS |
88 | nv_mask(dev, 0x610300 + (ch * 0x08), 0x00000001, 0x00000000); |
89 | return ret; | |
90 | } | |
91 | ||
c88c2e06 FJ |
92 | int |
93 | nv50_display_early_init(struct drm_device *dev) | |
94 | { | |
020c6bf3 | 95 | u32 ctrl = nv_rd32(dev, 0x610200); |
0f6ea564 | 96 | int i; |
020c6bf3 | 97 | |
0f6ea564 BS |
98 | /* check if master evo channel is already active, a good a sign as any |
99 | * that the display engine is in a weird state (hibernate/kexec), if | |
100 | * it is, do our best to reset the display engine... | |
101 | */ | |
020c6bf3 BS |
102 | if ((ctrl & 0x00000003) == 0x00000003) { |
103 | NV_INFO(dev, "PDISP: EVO(0) 0x%08x, resetting...\n", ctrl); | |
0f6ea564 BS |
104 | |
105 | /* deactivate both heads first, PDISP will disappear forever | |
106 | * (well, until you power cycle) on some boards as soon as | |
107 | * PMC_ENABLE is hit unless they are.. | |
108 | */ | |
109 | for (i = 0; i < 2; i++) { | |
110 | evo_icmd(dev, 0, 0x0880 + (i * 0x400), 0x05000000); | |
111 | evo_icmd(dev, 0, 0x089c + (i * 0x400), 0); | |
112 | evo_icmd(dev, 0, 0x0840 + (i * 0x400), 0); | |
113 | evo_icmd(dev, 0, 0x0844 + (i * 0x400), 0); | |
114 | evo_icmd(dev, 0, 0x085c + (i * 0x400), 0); | |
115 | evo_icmd(dev, 0, 0x0874 + (i * 0x400), 0); | |
116 | } | |
117 | evo_icmd(dev, 0, 0x0080, 0); | |
118 | ||
119 | /* reset PDISP */ | |
120 | nv_mask(dev, 0x000200, 0x40000000, 0x00000000); | |
121 | nv_mask(dev, 0x000200, 0x40000000, 0x40000000); | |
122 | } | |
020c6bf3 | 123 | |
c88c2e06 FJ |
124 | return 0; |
125 | } | |
126 | ||
127 | void | |
128 | nv50_display_late_takedown(struct drm_device *dev) | |
129 | { | |
130 | } | |
131 | ||
e6e039d1 BS |
132 | int |
133 | nv50_display_sync(struct drm_device *dev) | |
134 | { | |
135 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
136 | struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; | |
137 | struct nv50_display *disp = nv50_display(dev); | |
138 | struct nouveau_channel *evo = disp->master; | |
139 | u64 start; | |
140 | int ret; | |
141 | ||
142 | ret = RING_SPACE(evo, 6); | |
143 | if (ret == 0) { | |
6d597027 | 144 | BEGIN_NV04(evo, 0, 0x0084, 1); |
e6e039d1 | 145 | OUT_RING (evo, 0x80000000); |
6d597027 | 146 | BEGIN_NV04(evo, 0, 0x0080, 1); |
e6e039d1 | 147 | OUT_RING (evo, 0); |
6d597027 | 148 | BEGIN_NV04(evo, 0, 0x0084, 1); |
e6e039d1 BS |
149 | OUT_RING (evo, 0x00000000); |
150 | ||
151 | nv_wo32(disp->ntfy, 0x000, 0x00000000); | |
152 | FIRE_RING (evo); | |
153 | ||
154 | start = ptimer->read(dev); | |
155 | do { | |
156 | if (nv_ro32(disp->ntfy, 0x000)) | |
157 | return 0; | |
158 | } while (ptimer->read(dev) - start < 2000000000ULL); | |
159 | } | |
160 | ||
161 | return -EBUSY; | |
162 | } | |
163 | ||
6ee73861 BS |
164 | int |
165 | nv50_display_init(struct drm_device *dev) | |
166 | { | |
b7bc613a | 167 | struct nouveau_channel *evo; |
6ee73861 | 168 | int ret, i; |
cbb4b608 | 169 | u32 val; |
6ee73861 | 170 | |
ef2bb506 | 171 | NV_DEBUG_KMS(dev, "\n"); |
6ee73861 BS |
172 | |
173 | nv_wr32(dev, 0x00610184, nv_rd32(dev, 0x00614004)); | |
106ddad5 | 174 | |
6ee73861 BS |
175 | /* |
176 | * I think the 0x006101XX range is some kind of main control area | |
177 | * that enables things. | |
178 | */ | |
179 | /* CRTC? */ | |
180 | for (i = 0; i < 2; i++) { | |
181 | val = nv_rd32(dev, 0x00616100 + (i * 0x800)); | |
182 | nv_wr32(dev, 0x00610190 + (i * 0x10), val); | |
183 | val = nv_rd32(dev, 0x00616104 + (i * 0x800)); | |
184 | nv_wr32(dev, 0x00610194 + (i * 0x10), val); | |
185 | val = nv_rd32(dev, 0x00616108 + (i * 0x800)); | |
186 | nv_wr32(dev, 0x00610198 + (i * 0x10), val); | |
187 | val = nv_rd32(dev, 0x0061610c + (i * 0x800)); | |
188 | nv_wr32(dev, 0x0061019c + (i * 0x10), val); | |
189 | } | |
106ddad5 | 190 | |
6ee73861 BS |
191 | /* DAC */ |
192 | for (i = 0; i < 3; i++) { | |
193 | val = nv_rd32(dev, 0x0061a000 + (i * 0x800)); | |
194 | nv_wr32(dev, 0x006101d0 + (i * 0x04), val); | |
195 | } | |
106ddad5 | 196 | |
6ee73861 | 197 | /* SOR */ |
8597a1ba | 198 | for (i = 0; i < nv50_sor_nr(dev); i++) { |
6ee73861 BS |
199 | val = nv_rd32(dev, 0x0061c000 + (i * 0x800)); |
200 | nv_wr32(dev, 0x006101e0 + (i * 0x04), val); | |
201 | } | |
106ddad5 | 202 | |
8597a1ba | 203 | /* EXT */ |
6ee73861 BS |
204 | for (i = 0; i < 3; i++) { |
205 | val = nv_rd32(dev, 0x0061e000 + (i * 0x800)); | |
206 | nv_wr32(dev, 0x006101f0 + (i * 0x04), val); | |
207 | } | |
208 | ||
209 | for (i = 0; i < 3; i++) { | |
210 | nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(i), 0x00550000 | | |
211 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING); | |
212 | nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(i), 0x00000001); | |
213 | } | |
214 | ||
6ee73861 BS |
215 | /* The precise purpose is unknown, i suspect it has something to do |
216 | * with text mode. | |
217 | */ | |
218 | if (nv_rd32(dev, NV50_PDISPLAY_INTR_1) & 0x100) { | |
219 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, 0x100); | |
220 | nv_wr32(dev, 0x006194e8, nv_rd32(dev, 0x006194e8) & ~1); | |
4b5c152a | 221 | if (!nv_wait(dev, 0x006194e8, 2, 0)) { |
6ee73861 BS |
222 | NV_ERROR(dev, "timeout: (0x6194e8 & 2) != 0\n"); |
223 | NV_ERROR(dev, "0x6194e8 = 0x%08x\n", | |
224 | nv_rd32(dev, 0x6194e8)); | |
225 | return -EBUSY; | |
226 | } | |
227 | } | |
228 | ||
106ddad5 BS |
229 | for (i = 0; i < 2; i++) { |
230 | nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0x2000); | |
231 | if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), | |
232 | NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) { | |
233 | NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n"); | |
234 | NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n", | |
235 | nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i))); | |
236 | return -EBUSY; | |
237 | } | |
238 | ||
239 | nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), | |
240 | NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON); | |
241 | if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), | |
242 | NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, | |
243 | NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE)) { | |
244 | NV_ERROR(dev, "timeout: " | |
245 | "CURSOR_CTRL2_STATUS_ACTIVE(%d)\n", i); | |
246 | NV_ERROR(dev, "CURSOR_CTRL2(%d) = 0x%08x\n", i, | |
247 | nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i))); | |
248 | return -EBUSY; | |
249 | } | |
250 | } | |
251 | ||
106ddad5 | 252 | nv_wr32(dev, NV50_PDISPLAY_PIO_CTRL, 0x00000000); |
106ddad5 | 253 | nv_mask(dev, NV50_PDISPLAY_INTR_0, 0x00000000, 0x00000000); |
97e2000f | 254 | nv_wr32(dev, NV50_PDISPLAY_INTR_EN_0, 0x00000000); |
106ddad5 | 255 | nv_mask(dev, NV50_PDISPLAY_INTR_1, 0x00000000, 0x00000000); |
97e2000f BS |
256 | nv_wr32(dev, NV50_PDISPLAY_INTR_EN_1, |
257 | NV50_PDISPLAY_INTR_EN_1_CLK_UNK10 | | |
258 | NV50_PDISPLAY_INTR_EN_1_CLK_UNK20 | | |
259 | NV50_PDISPLAY_INTR_EN_1_CLK_UNK40); | |
106ddad5 | 260 | |
b7bc613a | 261 | ret = nv50_evo_init(dev); |
6ee73861 BS |
262 | if (ret) |
263 | return ret; | |
59c0f578 | 264 | evo = nv50_display(dev)->master; |
6ee73861 | 265 | |
b7bc613a | 266 | nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->vinst >> 8) | 9); |
6ee73861 | 267 | |
b98e3f5c | 268 | ret = RING_SPACE(evo, 3); |
6ee73861 BS |
269 | if (ret) |
270 | return ret; | |
6d597027 | 271 | BEGIN_NV04(evo, 0, NV50_EVO_UNK84, 2); |
b98e3f5c BS |
272 | OUT_RING (evo, NV50_EVO_UNK84_NOTIFY_DISABLED); |
273 | OUT_RING (evo, NvEvoSync); | |
6ee73861 | 274 | |
b98e3f5c | 275 | return nv50_display_sync(dev); |
6ee73861 BS |
276 | } |
277 | ||
2a44e499 BS |
278 | void |
279 | nv50_display_fini(struct drm_device *dev) | |
6ee73861 | 280 | { |
ef8389a8 | 281 | struct nv50_display *disp = nv50_display(dev); |
59c0f578 | 282 | struct nouveau_channel *evo = disp->master; |
6ee73861 BS |
283 | struct drm_crtc *drm_crtc; |
284 | int ret, i; | |
285 | ||
ef2bb506 | 286 | NV_DEBUG_KMS(dev, "\n"); |
6ee73861 BS |
287 | |
288 | list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { | |
289 | struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc); | |
290 | ||
291 | nv50_crtc_blank(crtc, true); | |
292 | } | |
293 | ||
ef8389a8 | 294 | ret = RING_SPACE(evo, 2); |
6ee73861 | 295 | if (ret == 0) { |
6d597027 | 296 | BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); |
ef8389a8 | 297 | OUT_RING(evo, 0); |
6ee73861 | 298 | } |
ef8389a8 | 299 | FIRE_RING(evo); |
6ee73861 BS |
300 | |
301 | /* Almost like ack'ing a vblank interrupt, maybe in the spirit of | |
302 | * cleaning up? | |
303 | */ | |
304 | list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { | |
305 | struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc); | |
306 | uint32_t mask = NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(crtc->index); | |
307 | ||
308 | if (!crtc->base.enabled) | |
309 | continue; | |
310 | ||
311 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, mask); | |
4b5c152a | 312 | if (!nv_wait(dev, NV50_PDISPLAY_INTR_1, mask, mask)) { |
6ee73861 BS |
313 | NV_ERROR(dev, "timeout: (0x610024 & 0x%08x) == " |
314 | "0x%08x\n", mask, mask); | |
315 | NV_ERROR(dev, "0x610024 = 0x%08x\n", | |
316 | nv_rd32(dev, NV50_PDISPLAY_INTR_1)); | |
317 | } | |
318 | } | |
319 | ||
048a8859 BS |
320 | for (i = 0; i < 2; i++) { |
321 | nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0); | |
322 | if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), | |
323 | NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) { | |
324 | NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n"); | |
325 | NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n", | |
326 | nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i))); | |
327 | } | |
328 | } | |
329 | ||
b7bc613a | 330 | nv50_evo_fini(dev); |
6ee73861 BS |
331 | |
332 | for (i = 0; i < 3; i++) { | |
4b5c152a | 333 | if (!nv_wait(dev, NV50_PDISPLAY_SOR_DPMS_STATE(i), |
6ee73861 BS |
334 | NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) { |
335 | NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", i); | |
336 | NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", i, | |
337 | nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(i))); | |
338 | } | |
339 | } | |
340 | ||
341 | /* disable interrupts. */ | |
97e2000f | 342 | nv_wr32(dev, NV50_PDISPLAY_INTR_EN_1, 0x00000000); |
6ee73861 BS |
343 | } |
344 | ||
2a44e499 BS |
345 | int |
346 | nv50_display_create(struct drm_device *dev) | |
6ee73861 BS |
347 | { |
348 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
04a39c57 | 349 | struct dcb_table *dcb = &dev_priv->vbios.dcb; |
8f1a6086 | 350 | struct drm_connector *connector, *ct; |
ef8389a8 | 351 | struct nv50_display *priv; |
1772fcc6 | 352 | int ret, i; |
6ee73861 | 353 | |
ef2bb506 | 354 | NV_DEBUG_KMS(dev, "\n"); |
6ee73861 | 355 | |
ef8389a8 BS |
356 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
357 | if (!priv) | |
358 | return -ENOMEM; | |
359 | dev_priv->engine.display.priv = priv; | |
360 | ||
6ee73861 | 361 | /* Create CRTC objects */ |
a8f81837 BS |
362 | for (i = 0; i < 2; i++) { |
363 | ret = nv50_crtc_create(dev, i); | |
364 | if (ret) | |
365 | return ret; | |
366 | } | |
6ee73861 BS |
367 | |
368 | /* We setup the encoders from the BIOS table */ | |
369 | for (i = 0 ; i < dcb->entries; i++) { | |
370 | struct dcb_entry *entry = &dcb->entry[i]; | |
371 | ||
372 | if (entry->location != DCB_LOC_ON_CHIP) { | |
373 | NV_WARN(dev, "Off-chip encoder %d/%d unsupported\n", | |
374 | entry->type, ffs(entry->or) - 1); | |
375 | continue; | |
376 | } | |
377 | ||
8f1a6086 BS |
378 | connector = nouveau_connector_create(dev, entry->connector); |
379 | if (IS_ERR(connector)) | |
380 | continue; | |
381 | ||
6ee73861 BS |
382 | switch (entry->type) { |
383 | case OUTPUT_TMDS: | |
384 | case OUTPUT_LVDS: | |
385 | case OUTPUT_DP: | |
8f1a6086 | 386 | nv50_sor_create(connector, entry); |
6ee73861 BS |
387 | break; |
388 | case OUTPUT_ANALOG: | |
8f1a6086 | 389 | nv50_dac_create(connector, entry); |
6ee73861 BS |
390 | break; |
391 | default: | |
392 | NV_WARN(dev, "DCB encoder %d unknown\n", entry->type); | |
393 | continue; | |
394 | } | |
6ee73861 BS |
395 | } |
396 | ||
8f1a6086 BS |
397 | list_for_each_entry_safe(connector, ct, |
398 | &dev->mode_config.connector_list, head) { | |
399 | if (!connector->encoder_ids[0]) { | |
400 | NV_WARN(dev, "%s has no encoders, removing\n", | |
401 | drm_get_connector_name(connector)); | |
402 | connector->funcs->destroy(connector); | |
403 | } | |
6ee73861 BS |
404 | } |
405 | ||
f13e435c | 406 | tasklet_init(&priv->tasklet, nv50_display_bh, (unsigned long)dev); |
19b7fc7b | 407 | nouveau_irq_register(dev, 26, nv50_display_isr); |
1772fcc6 BS |
408 | |
409 | ret = nv50_evo_create(dev); | |
410 | if (ret) { | |
411 | nv50_display_destroy(dev); | |
412 | return ret; | |
413 | } | |
414 | ||
6ee73861 BS |
415 | return 0; |
416 | } | |
417 | ||
c88c2e06 FJ |
418 | void |
419 | nv50_display_destroy(struct drm_device *dev) | |
6ee73861 | 420 | { |
ef8389a8 | 421 | struct nv50_display *disp = nv50_display(dev); |
d82f8e6c | 422 | |
ef2bb506 | 423 | NV_DEBUG_KMS(dev, "\n"); |
6ee73861 | 424 | |
1772fcc6 | 425 | nv50_evo_destroy(dev); |
19b7fc7b | 426 | nouveau_irq_unregister(dev, 26); |
ef8389a8 | 427 | kfree(disp); |
6ee73861 BS |
428 | } |
429 | ||
cdccc70e BS |
430 | void |
431 | nv50_display_flip_stop(struct drm_crtc *crtc) | |
432 | { | |
433 | struct nv50_display *disp = nv50_display(crtc->dev); | |
434 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | |
435 | struct nv50_display_crtc *dispc = &disp->crtc[nv_crtc->index]; | |
436 | struct nouveau_channel *evo = dispc->sync; | |
437 | int ret; | |
438 | ||
439 | ret = RING_SPACE(evo, 8); | |
440 | if (ret) { | |
441 | WARN_ON(1); | |
442 | return; | |
443 | } | |
444 | ||
6d597027 | 445 | BEGIN_NV04(evo, 0, 0x0084, 1); |
cdccc70e | 446 | OUT_RING (evo, 0x00000000); |
6d597027 | 447 | BEGIN_NV04(evo, 0, 0x0094, 1); |
cdccc70e | 448 | OUT_RING (evo, 0x00000000); |
6d597027 | 449 | BEGIN_NV04(evo, 0, 0x00c0, 1); |
cdccc70e | 450 | OUT_RING (evo, 0x00000000); |
6d597027 | 451 | BEGIN_NV04(evo, 0, 0x0080, 1); |
cdccc70e BS |
452 | OUT_RING (evo, 0x00000000); |
453 | FIRE_RING (evo); | |
454 | } | |
455 | ||
456 | int | |
457 | nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, | |
458 | struct nouveau_channel *chan) | |
459 | { | |
460 | struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; | |
461 | struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); | |
462 | struct nv50_display *disp = nv50_display(crtc->dev); | |
463 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | |
464 | struct nv50_display_crtc *dispc = &disp->crtc[nv_crtc->index]; | |
465 | struct nouveau_channel *evo = dispc->sync; | |
466 | int ret; | |
467 | ||
f66b3d55 | 468 | ret = RING_SPACE(evo, chan ? 25 : 27); |
cdccc70e BS |
469 | if (unlikely(ret)) |
470 | return ret; | |
471 | ||
472 | /* synchronise with the rendering channel, if necessary */ | |
473 | if (likely(chan)) { | |
cdccc70e BS |
474 | ret = RING_SPACE(chan, 10); |
475 | if (ret) { | |
476 | WIND_RING(evo); | |
477 | return ret; | |
478 | } | |
479 | ||
480 | if (dev_priv->chipset < 0xc0) { | |
6d597027 | 481 | BEGIN_NV04(chan, 0, 0x0060, 2); |
cdccc70e BS |
482 | OUT_RING (chan, NvEvoSema0 + nv_crtc->index); |
483 | OUT_RING (chan, dispc->sem.offset); | |
6d597027 | 484 | BEGIN_NV04(chan, 0, 0x006c, 1); |
cdccc70e | 485 | OUT_RING (chan, 0xf00d0000 | dispc->sem.value); |
6d597027 | 486 | BEGIN_NV04(chan, 0, 0x0064, 2); |
cdccc70e BS |
487 | OUT_RING (chan, dispc->sem.offset ^ 0x10); |
488 | OUT_RING (chan, 0x74b1e000); | |
6d597027 | 489 | BEGIN_NV04(chan, 0, 0x0060, 1); |
cdccc70e BS |
490 | if (dev_priv->chipset < 0x84) |
491 | OUT_RING (chan, NvSema); | |
492 | else | |
493 | OUT_RING (chan, chan->vram_handle); | |
494 | } else { | |
35bcf5d5 | 495 | u64 offset = nvc0_software_crtc(chan, nv_crtc->index); |
3d483d57 | 496 | offset += dispc->sem.offset; |
6d597027 | 497 | BEGIN_NVC0(chan, 0, 0x0010, 4); |
cdccc70e BS |
498 | OUT_RING (chan, upper_32_bits(offset)); |
499 | OUT_RING (chan, lower_32_bits(offset)); | |
500 | OUT_RING (chan, 0xf00d0000 | dispc->sem.value); | |
501 | OUT_RING (chan, 0x1002); | |
6d597027 | 502 | BEGIN_NVC0(chan, 0, 0x0010, 4); |
cdccc70e BS |
503 | OUT_RING (chan, upper_32_bits(offset)); |
504 | OUT_RING (chan, lower_32_bits(offset ^ 0x10)); | |
505 | OUT_RING (chan, 0x74b1e000); | |
506 | OUT_RING (chan, 0x1001); | |
507 | } | |
508 | FIRE_RING (chan); | |
509 | } else { | |
510 | nouveau_bo_wr32(dispc->sem.bo, dispc->sem.offset / 4, | |
511 | 0xf00d0000 | dispc->sem.value); | |
512 | } | |
513 | ||
514 | /* queue the flip on the crtc's "display sync" channel */ | |
6d597027 | 515 | BEGIN_NV04(evo, 0, 0x0100, 1); |
cdccc70e | 516 | OUT_RING (evo, 0xfffe0000); |
f66b3d55 | 517 | if (chan) { |
6d597027 | 518 | BEGIN_NV04(evo, 0, 0x0084, 1); |
f66b3d55 BS |
519 | OUT_RING (evo, 0x00000100); |
520 | } else { | |
6d597027 | 521 | BEGIN_NV04(evo, 0, 0x0084, 1); |
f66b3d55 BS |
522 | OUT_RING (evo, 0x00000010); |
523 | /* allows gamma somehow, PDISP will bitch at you if | |
524 | * you don't wait for vblank before changing this.. | |
525 | */ | |
6d597027 | 526 | BEGIN_NV04(evo, 0, 0x00e0, 1); |
f66b3d55 BS |
527 | OUT_RING (evo, 0x40000000); |
528 | } | |
6d597027 | 529 | BEGIN_NV04(evo, 0, 0x0088, 4); |
cdccc70e BS |
530 | OUT_RING (evo, dispc->sem.offset); |
531 | OUT_RING (evo, 0xf00d0000 | dispc->sem.value); | |
532 | OUT_RING (evo, 0x74b1e000); | |
533 | OUT_RING (evo, NvEvoSync); | |
6d597027 | 534 | BEGIN_NV04(evo, 0, 0x00a0, 2); |
cdccc70e BS |
535 | OUT_RING (evo, 0x00000000); |
536 | OUT_RING (evo, 0x00000000); | |
6d597027 | 537 | BEGIN_NV04(evo, 0, 0x00c0, 1); |
cdccc70e | 538 | OUT_RING (evo, nv_fb->r_dma); |
6d597027 | 539 | BEGIN_NV04(evo, 0, 0x0110, 2); |
cdccc70e BS |
540 | OUT_RING (evo, 0x00000000); |
541 | OUT_RING (evo, 0x00000000); | |
6d597027 | 542 | BEGIN_NV04(evo, 0, 0x0800, 5); |
180cc306 | 543 | OUT_RING (evo, nv_fb->nvbo->bo.offset >> 8); |
cdccc70e BS |
544 | OUT_RING (evo, 0); |
545 | OUT_RING (evo, (fb->height << 16) | fb->width); | |
546 | OUT_RING (evo, nv_fb->r_pitch); | |
547 | OUT_RING (evo, nv_fb->r_format); | |
6d597027 | 548 | BEGIN_NV04(evo, 0, 0x0080, 1); |
cdccc70e BS |
549 | OUT_RING (evo, 0x00000000); |
550 | FIRE_RING (evo); | |
551 | ||
552 | dispc->sem.offset ^= 0x10; | |
553 | dispc->sem.value++; | |
554 | return 0; | |
555 | } | |
556 | ||
87c0e0e5 BS |
557 | static u16 |
558 | nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcb, | |
559 | u32 mc, int pxclk) | |
6ee73861 BS |
560 | { |
561 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
75c722d7 BS |
562 | struct nouveau_connector *nv_connector = NULL; |
563 | struct drm_encoder *encoder; | |
04a39c57 | 564 | struct nvbios *bios = &dev_priv->vbios; |
87c0e0e5 | 565 | u32 script = 0, or; |
6ee73861 | 566 | |
75c722d7 BS |
567 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
568 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
569 | ||
87c0e0e5 | 570 | if (nv_encoder->dcb != dcb) |
75c722d7 BS |
571 | continue; |
572 | ||
573 | nv_connector = nouveau_encoder_connector_get(nv_encoder); | |
574 | break; | |
575 | } | |
576 | ||
87c0e0e5 BS |
577 | or = ffs(dcb->or) - 1; |
578 | switch (dcb->type) { | |
6ee73861 BS |
579 | case OUTPUT_LVDS: |
580 | script = (mc >> 8) & 0xf; | |
04a39c57 | 581 | if (bios->fp_no_ddc) { |
6ee73861 BS |
582 | if (bios->fp.dual_link) |
583 | script |= 0x0100; | |
584 | if (bios->fp.if_is_24bit) | |
585 | script |= 0x0200; | |
586 | } else { | |
b23b9e71 BS |
587 | /* determine number of lvds links */ |
588 | if (nv_connector && nv_connector->edid && | |
befb51e9 | 589 | nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { |
b23b9e71 BS |
590 | /* http://www.spwg.org */ |
591 | if (((u8 *)nv_connector->edid)[121] == 2) | |
592 | script |= 0x0100; | |
593 | } else | |
6ee73861 BS |
594 | if (pxclk >= bios->fp.duallink_transition_clk) { |
595 | script |= 0x0100; | |
b23b9e71 BS |
596 | } |
597 | ||
598 | /* determine panel depth */ | |
599 | if (script & 0x0100) { | |
6ee73861 BS |
600 | if (bios->fp.strapless_is_24bit & 2) |
601 | script |= 0x0200; | |
b23b9e71 BS |
602 | } else { |
603 | if (bios->fp.strapless_is_24bit & 1) | |
604 | script |= 0x0200; | |
605 | } | |
75c722d7 BS |
606 | |
607 | if (nv_connector && nv_connector->edid && | |
608 | (nv_connector->edid->revision >= 4) && | |
609 | (nv_connector->edid->input & 0x70) >= 0x20) | |
610 | script |= 0x0200; | |
6ee73861 BS |
611 | } |
612 | ||
613 | if (nouveau_uscript_lvds >= 0) { | |
614 | NV_INFO(dev, "override script 0x%04x with 0x%04x " | |
615 | "for output LVDS-%d\n", script, | |
616 | nouveau_uscript_lvds, or); | |
617 | script = nouveau_uscript_lvds; | |
618 | } | |
619 | break; | |
620 | case OUTPUT_TMDS: | |
621 | script = (mc >> 8) & 0xf; | |
622 | if (pxclk >= 165000) | |
623 | script |= 0x0100; | |
624 | ||
625 | if (nouveau_uscript_tmds >= 0) { | |
626 | NV_INFO(dev, "override script 0x%04x with 0x%04x " | |
627 | "for output TMDS-%d\n", script, | |
628 | nouveau_uscript_tmds, or); | |
629 | script = nouveau_uscript_tmds; | |
630 | } | |
631 | break; | |
632 | case OUTPUT_DP: | |
633 | script = (mc >> 8) & 0xf; | |
634 | break; | |
635 | case OUTPUT_ANALOG: | |
636 | script = 0xff; | |
637 | break; | |
638 | default: | |
639 | NV_ERROR(dev, "modeset on unsupported output type!\n"); | |
640 | break; | |
641 | } | |
642 | ||
643 | return script; | |
644 | } | |
645 | ||
646 | static void | |
647 | nv50_display_vblank_crtc_handler(struct drm_device *dev, int crtc) | |
648 | { | |
0ade74b6 BS |
649 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
650 | struct nouveau_software_priv *psw = nv_engine(dev, NVOBJ_ENGINE_SW); | |
651 | struct nouveau_software_chan *pch, *tmp; | |
652 | ||
653 | list_for_each_entry_safe(pch, tmp, &psw->vblank, vblank.list) { | |
654 | if (pch->vblank.head != crtc) | |
655 | continue; | |
656 | ||
657 | spin_lock(&psw->peephole_lock); | |
658 | nv_wr32(dev, 0x001704, pch->vblank.channel); | |
659 | nv_wr32(dev, 0x001710, 0x80000000 | pch->vblank.ctxdma); | |
660 | if (dev_priv->chipset == 0x50) { | |
661 | nv_wr32(dev, 0x001570, pch->vblank.offset); | |
662 | nv_wr32(dev, 0x001574, pch->vblank.value); | |
663 | } else { | |
664 | nv_wr32(dev, 0x060010, pch->vblank.offset); | |
665 | nv_wr32(dev, 0x060014, pch->vblank.value); | |
666 | } | |
667 | spin_unlock(&psw->peephole_lock); | |
668 | ||
669 | list_del(&pch->vblank.list); | |
670 | drm_vblank_put(dev, crtc); | |
671 | } | |
672 | ||
042206c0 | 673 | drm_handle_vblank(dev, crtc); |
6ee73861 BS |
674 | } |
675 | ||
676 | static void | |
677 | nv50_display_vblank_handler(struct drm_device *dev, uint32_t intr) | |
678 | { | |
6ee73861 BS |
679 | if (intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_0) |
680 | nv50_display_vblank_crtc_handler(dev, 0); | |
681 | ||
682 | if (intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_1) | |
683 | nv50_display_vblank_crtc_handler(dev, 1); | |
684 | ||
042206c0 | 685 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_VBLANK_CRTC); |
6ee73861 BS |
686 | } |
687 | ||
688 | static void | |
689 | nv50_display_unk10_handler(struct drm_device *dev) | |
690 | { | |
87c0e0e5 | 691 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
ef8389a8 | 692 | struct nv50_display *disp = nv50_display(dev); |
87c0e0e5 | 693 | u32 unk30 = nv_rd32(dev, 0x610030), mc; |
a55b68e0 | 694 | int i, crtc, or = 0, type = OUTPUT_ANY; |
6ee73861 | 695 | |
87c0e0e5 | 696 | NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); |
ef8389a8 | 697 | disp->irq.dcb = NULL; |
6ee73861 BS |
698 | |
699 | nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) & ~8); | |
700 | ||
87c0e0e5 BS |
701 | /* Determine which CRTC we're dealing with, only 1 ever will be |
702 | * signalled at the same time with the current nouveau code. | |
703 | */ | |
704 | crtc = ffs((unk30 & 0x00000060) >> 5) - 1; | |
705 | if (crtc < 0) | |
706 | goto ack; | |
707 | ||
708 | /* Nothing needs to be done for the encoder */ | |
709 | crtc = ffs((unk30 & 0x00000180) >> 7) - 1; | |
710 | if (crtc < 0) | |
711 | goto ack; | |
6ee73861 | 712 | |
87c0e0e5 BS |
713 | /* Find which encoder was connected to the CRTC */ |
714 | for (i = 0; type == OUTPUT_ANY && i < 3; i++) { | |
715 | mc = nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_C(i)); | |
716 | NV_DEBUG_KMS(dev, "DAC-%d mc: 0x%08x\n", i, mc); | |
717 | if (!(mc & (1 << crtc))) | |
718 | continue; | |
719 | ||
720 | switch ((mc & 0x00000f00) >> 8) { | |
721 | case 0: type = OUTPUT_ANALOG; break; | |
722 | case 1: type = OUTPUT_TV; break; | |
723 | default: | |
724 | NV_ERROR(dev, "invalid mc, DAC-%d: 0x%08x\n", i, mc); | |
725 | goto ack; | |
726 | } | |
727 | ||
728 | or = i; | |
729 | } | |
730 | ||
8597a1ba | 731 | for (i = 0; type == OUTPUT_ANY && i < nv50_sor_nr(dev); i++) { |
87c0e0e5 BS |
732 | if (dev_priv->chipset < 0x90 || |
733 | dev_priv->chipset == 0x92 || | |
734 | dev_priv->chipset == 0xa0) | |
735 | mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_C(i)); | |
736 | else | |
737 | mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_C(i)); | |
738 | ||
739 | NV_DEBUG_KMS(dev, "SOR-%d mc: 0x%08x\n", i, mc); | |
740 | if (!(mc & (1 << crtc))) | |
741 | continue; | |
742 | ||
743 | switch ((mc & 0x00000f00) >> 8) { | |
744 | case 0: type = OUTPUT_LVDS; break; | |
745 | case 1: type = OUTPUT_TMDS; break; | |
746 | case 2: type = OUTPUT_TMDS; break; | |
747 | case 5: type = OUTPUT_TMDS; break; | |
748 | case 8: type = OUTPUT_DP; break; | |
749 | case 9: type = OUTPUT_DP; break; | |
750 | default: | |
751 | NV_ERROR(dev, "invalid mc, SOR-%d: 0x%08x\n", i, mc); | |
752 | goto ack; | |
753 | } | |
754 | ||
755 | or = i; | |
756 | } | |
757 | ||
758 | /* There was no encoder to disable */ | |
759 | if (type == OUTPUT_ANY) | |
760 | goto ack; | |
761 | ||
762 | /* Disable the encoder */ | |
763 | for (i = 0; i < dev_priv->vbios.dcb.entries; i++) { | |
764 | struct dcb_entry *dcb = &dev_priv->vbios.dcb.entry[i]; | |
765 | ||
766 | if (dcb->type == type && (dcb->or & (1 << or))) { | |
02e4f587 | 767 | nouveau_bios_run_display_table(dev, 0, -1, dcb, -1); |
ef8389a8 | 768 | disp->irq.dcb = dcb; |
87c0e0e5 BS |
769 | goto ack; |
770 | } | |
771 | } | |
772 | ||
773 | NV_ERROR(dev, "no dcb for %d %d 0x%08x\n", or, type, mc); | |
6ee73861 BS |
774 | ack: |
775 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK10); | |
776 | nv_wr32(dev, 0x610030, 0x80000000); | |
777 | } | |
778 | ||
779 | static void | |
780 | nv50_display_unk20_handler(struct drm_device *dev) | |
781 | { | |
87c0e0e5 | 782 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
ef8389a8 | 783 | struct nv50_display *disp = nv50_display(dev); |
ea5f2786 | 784 | u32 unk30 = nv_rd32(dev, 0x610030), tmp, pclk, script, mc = 0; |
87c0e0e5 | 785 | struct dcb_entry *dcb; |
a55b68e0 | 786 | int i, crtc, or = 0, type = OUTPUT_ANY; |
6ee73861 | 787 | |
87c0e0e5 | 788 | NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); |
ef8389a8 | 789 | dcb = disp->irq.dcb; |
87c0e0e5 | 790 | if (dcb) { |
02e4f587 | 791 | nouveau_bios_run_display_table(dev, 0, -2, dcb, -1); |
ef8389a8 | 792 | disp->irq.dcb = NULL; |
87c0e0e5 BS |
793 | } |
794 | ||
795 | /* CRTC clock change requested? */ | |
796 | crtc = ffs((unk30 & 0x00000600) >> 9) - 1; | |
797 | if (crtc >= 0) { | |
798 | pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(crtc, CLOCK)); | |
799 | pclk &= 0x003fffff; | |
b98e3f5c BS |
800 | if (pclk) |
801 | nv50_crtc_set_clock(dev, crtc, pclk); | |
87c0e0e5 BS |
802 | |
803 | tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc)); | |
804 | tmp &= ~0x000000f; | |
805 | nv_wr32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc), tmp); | |
806 | } | |
807 | ||
808 | /* Nothing needs to be done for the encoder */ | |
809 | crtc = ffs((unk30 & 0x00000180) >> 7) - 1; | |
810 | if (crtc < 0) | |
6ee73861 | 811 | goto ack; |
87c0e0e5 | 812 | pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(crtc, CLOCK)) & 0x003fffff; |
6ee73861 | 813 | |
87c0e0e5 BS |
814 | /* Find which encoder is connected to the CRTC */ |
815 | for (i = 0; type == OUTPUT_ANY && i < 3; i++) { | |
816 | mc = nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_P(i)); | |
817 | NV_DEBUG_KMS(dev, "DAC-%d mc: 0x%08x\n", i, mc); | |
818 | if (!(mc & (1 << crtc))) | |
819 | continue; | |
6ee73861 | 820 | |
87c0e0e5 BS |
821 | switch ((mc & 0x00000f00) >> 8) { |
822 | case 0: type = OUTPUT_ANALOG; break; | |
823 | case 1: type = OUTPUT_TV; break; | |
824 | default: | |
825 | NV_ERROR(dev, "invalid mc, DAC-%d: 0x%08x\n", i, mc); | |
826 | goto ack; | |
827 | } | |
828 | ||
829 | or = i; | |
830 | } | |
831 | ||
8597a1ba | 832 | for (i = 0; type == OUTPUT_ANY && i < nv50_sor_nr(dev); i++) { |
87c0e0e5 BS |
833 | if (dev_priv->chipset < 0x90 || |
834 | dev_priv->chipset == 0x92 || | |
835 | dev_priv->chipset == 0xa0) | |
836 | mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_P(i)); | |
837 | else | |
838 | mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_P(i)); | |
839 | ||
840 | NV_DEBUG_KMS(dev, "SOR-%d mc: 0x%08x\n", i, mc); | |
841 | if (!(mc & (1 << crtc))) | |
842 | continue; | |
843 | ||
844 | switch ((mc & 0x00000f00) >> 8) { | |
845 | case 0: type = OUTPUT_LVDS; break; | |
846 | case 1: type = OUTPUT_TMDS; break; | |
847 | case 2: type = OUTPUT_TMDS; break; | |
848 | case 5: type = OUTPUT_TMDS; break; | |
849 | case 8: type = OUTPUT_DP; break; | |
850 | case 9: type = OUTPUT_DP; break; | |
851 | default: | |
852 | NV_ERROR(dev, "invalid mc, SOR-%d: 0x%08x\n", i, mc); | |
853 | goto ack; | |
854 | } | |
6ee73861 | 855 | |
87c0e0e5 BS |
856 | or = i; |
857 | } | |
6ee73861 | 858 | |
87c0e0e5 BS |
859 | if (type == OUTPUT_ANY) |
860 | goto ack; | |
6ee73861 | 861 | |
87c0e0e5 BS |
862 | /* Enable the encoder */ |
863 | for (i = 0; i < dev_priv->vbios.dcb.entries; i++) { | |
864 | dcb = &dev_priv->vbios.dcb.entry[i]; | |
865 | if (dcb->type == type && (dcb->or & (1 << or))) | |
866 | break; | |
867 | } | |
afa3b4c3 | 868 | |
87c0e0e5 BS |
869 | if (i == dev_priv->vbios.dcb.entries) { |
870 | NV_ERROR(dev, "no dcb for %d %d 0x%08x\n", or, type, mc); | |
871 | goto ack; | |
872 | } | |
873 | ||
874 | script = nv50_display_script_select(dev, dcb, mc, pclk); | |
02e4f587 | 875 | nouveau_bios_run_display_table(dev, script, pclk, dcb, -1); |
6ee73861 | 876 | |
46959b77 BS |
877 | if (type == OUTPUT_DP) { |
878 | int link = !(dcb->dpconf.sor.link & 1); | |
879 | if ((mc & 0x000f0000) == 0x00020000) | |
8663bc7c | 880 | nv50_sor_dp_calc_tu(dev, or, link, pclk, 18); |
46959b77 | 881 | else |
8663bc7c | 882 | nv50_sor_dp_calc_tu(dev, or, link, pclk, 24); |
46959b77 | 883 | } |
87c0e0e5 BS |
884 | |
885 | if (dcb->type != OUTPUT_ANALOG) { | |
6ee73861 BS |
886 | tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or)); |
887 | tmp &= ~0x00000f0f; | |
888 | if (script & 0x0100) | |
889 | tmp |= 0x00000101; | |
890 | nv_wr32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or), tmp); | |
891 | } else { | |
892 | nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL2(or), 0); | |
893 | } | |
894 | ||
ef8389a8 BS |
895 | disp->irq.dcb = dcb; |
896 | disp->irq.pclk = pclk; | |
897 | disp->irq.script = script; | |
87c0e0e5 | 898 | |
6ee73861 BS |
899 | ack: |
900 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK20); | |
901 | nv_wr32(dev, 0x610030, 0x80000000); | |
902 | } | |
903 | ||
271f29e7 BS |
904 | /* If programming a TMDS output on a SOR that can also be configured for |
905 | * DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off. | |
906 | * | |
907 | * It looks like the VBIOS TMDS scripts make an attempt at this, however, | |
908 | * the VBIOS scripts on at least one board I have only switch it off on | |
909 | * link 0, causing a blank display if the output has previously been | |
910 | * programmed for DisplayPort. | |
911 | */ | |
912 | static void | |
913 | nv50_display_unk40_dp_set_tmds(struct drm_device *dev, struct dcb_entry *dcb) | |
914 | { | |
915 | int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1); | |
916 | struct drm_encoder *encoder; | |
917 | u32 tmp; | |
918 | ||
919 | if (dcb->type != OUTPUT_TMDS) | |
920 | return; | |
921 | ||
922 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | |
923 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
924 | ||
925 | if (nv_encoder->dcb->type == OUTPUT_DP && | |
926 | nv_encoder->dcb->or & (1 << or)) { | |
927 | tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); | |
928 | tmp &= ~NV50_SOR_DP_CTRL_ENABLED; | |
929 | nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); | |
930 | break; | |
931 | } | |
932 | } | |
933 | } | |
934 | ||
6ee73861 BS |
935 | static void |
936 | nv50_display_unk40_handler(struct drm_device *dev) | |
937 | { | |
ef8389a8 BS |
938 | struct nv50_display *disp = nv50_display(dev); |
939 | struct dcb_entry *dcb = disp->irq.dcb; | |
940 | u16 script = disp->irq.script; | |
941 | u32 unk30 = nv_rd32(dev, 0x610030), pclk = disp->irq.pclk; | |
6ee73861 | 942 | |
87c0e0e5 | 943 | NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); |
ef8389a8 | 944 | disp->irq.dcb = NULL; |
87c0e0e5 | 945 | if (!dcb) |
6ee73861 | 946 | goto ack; |
6ee73861 | 947 | |
02e4f587 | 948 | nouveau_bios_run_display_table(dev, script, -pclk, dcb, -1); |
271f29e7 BS |
949 | nv50_display_unk40_dp_set_tmds(dev, dcb); |
950 | ||
6ee73861 BS |
951 | ack: |
952 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK40); | |
953 | nv_wr32(dev, 0x610030, 0x80000000); | |
954 | nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) | 8); | |
955 | } | |
956 | ||
f13e435c BS |
957 | static void |
958 | nv50_display_bh(unsigned long data) | |
6ee73861 | 959 | { |
f13e435c | 960 | struct drm_device *dev = (struct drm_device *)data; |
6ee73861 BS |
961 | |
962 | for (;;) { | |
963 | uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0); | |
964 | uint32_t intr1 = nv_rd32(dev, NV50_PDISPLAY_INTR_1); | |
965 | ||
ef2bb506 | 966 | NV_DEBUG_KMS(dev, "PDISPLAY_INTR_BH 0x%08x 0x%08x\n", intr0, intr1); |
6ee73861 BS |
967 | |
968 | if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK10) | |
969 | nv50_display_unk10_handler(dev); | |
970 | else | |
971 | if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK20) | |
972 | nv50_display_unk20_handler(dev); | |
973 | else | |
974 | if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK40) | |
975 | nv50_display_unk40_handler(dev); | |
976 | else | |
977 | break; | |
978 | } | |
979 | ||
980 | nv_wr32(dev, NV03_PMC_INTR_EN_0, 1); | |
981 | } | |
982 | ||
983 | static void | |
984 | nv50_display_error_handler(struct drm_device *dev) | |
985 | { | |
97e2000f BS |
986 | u32 channels = (nv_rd32(dev, NV50_PDISPLAY_INTR_0) & 0x001f0000) >> 16; |
987 | u32 addr, data; | |
988 | int chid; | |
6ee73861 | 989 | |
97e2000f BS |
990 | for (chid = 0; chid < 5; chid++) { |
991 | if (!(channels & (1 << chid))) | |
992 | continue; | |
6ee73861 | 993 | |
97e2000f BS |
994 | nv_wr32(dev, NV50_PDISPLAY_INTR_0, 0x00010000 << chid); |
995 | addr = nv_rd32(dev, NV50_PDISPLAY_TRAPPED_ADDR(chid)); | |
996 | data = nv_rd32(dev, NV50_PDISPLAY_TRAPPED_DATA(chid)); | |
997 | NV_ERROR(dev, "EvoCh %d Mthd 0x%04x Data 0x%08x " | |
998 | "(0x%04x 0x%02x)\n", chid, | |
999 | addr & 0xffc, data, addr >> 16, (addr >> 12) & 0xf); | |
6ee73861 | 1000 | |
97e2000f BS |
1001 | nv_wr32(dev, NV50_PDISPLAY_TRAPPED_ADDR(chid), 0x90000000); |
1002 | } | |
6ee73861 BS |
1003 | } |
1004 | ||
19b7fc7b BS |
1005 | static void |
1006 | nv50_display_isr(struct drm_device *dev) | |
6ee73861 | 1007 | { |
f13e435c | 1008 | struct nv50_display *disp = nv50_display(dev); |
6ee73861 BS |
1009 | uint32_t delayed = 0; |
1010 | ||
6ee73861 BS |
1011 | while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) { |
1012 | uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0); | |
1013 | uint32_t intr1 = nv_rd32(dev, NV50_PDISPLAY_INTR_1); | |
1014 | uint32_t clock; | |
1015 | ||
ef2bb506 | 1016 | NV_DEBUG_KMS(dev, "PDISPLAY_INTR 0x%08x 0x%08x\n", intr0, intr1); |
6ee73861 BS |
1017 | |
1018 | if (!intr0 && !(intr1 & ~delayed)) | |
1019 | break; | |
1020 | ||
97e2000f | 1021 | if (intr0 & 0x001f0000) { |
6ee73861 | 1022 | nv50_display_error_handler(dev); |
97e2000f | 1023 | intr0 &= ~0x001f0000; |
6ee73861 BS |
1024 | } |
1025 | ||
1026 | if (intr1 & NV50_PDISPLAY_INTR_1_VBLANK_CRTC) { | |
1027 | nv50_display_vblank_handler(dev, intr1); | |
1028 | intr1 &= ~NV50_PDISPLAY_INTR_1_VBLANK_CRTC; | |
1029 | } | |
1030 | ||
1031 | clock = (intr1 & (NV50_PDISPLAY_INTR_1_CLK_UNK10 | | |
1032 | NV50_PDISPLAY_INTR_1_CLK_UNK20 | | |
1033 | NV50_PDISPLAY_INTR_1_CLK_UNK40)); | |
1034 | if (clock) { | |
1035 | nv_wr32(dev, NV03_PMC_INTR_EN_0, 0); | |
f13e435c | 1036 | tasklet_schedule(&disp->tasklet); |
6ee73861 BS |
1037 | delayed |= clock; |
1038 | intr1 &= ~clock; | |
1039 | } | |
1040 | ||
1041 | if (intr0) { | |
1042 | NV_ERROR(dev, "unknown PDISPLAY_INTR_0: 0x%08x\n", intr0); | |
1043 | nv_wr32(dev, NV50_PDISPLAY_INTR_0, intr0); | |
1044 | } | |
1045 | ||
1046 | if (intr1) { | |
1047 | NV_ERROR(dev, | |
1048 | "unknown PDISPLAY_INTR_1: 0x%08x\n", intr1); | |
1049 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, intr1); | |
1050 | } | |
1051 | } | |
1052 | } |