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