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