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 | ||
2a44e499 BS |
267 | void |
268 | nv50_display_fini(struct drm_device *dev) | |
6ee73861 BS |
269 | { |
270 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
ef8389a8 | 271 | struct nv50_display *disp = nv50_display(dev); |
59c0f578 | 272 | struct nouveau_channel *evo = disp->master; |
6ee73861 BS |
273 | struct drm_crtc *drm_crtc; |
274 | int ret, i; | |
275 | ||
ef2bb506 | 276 | NV_DEBUG_KMS(dev, "\n"); |
6ee73861 BS |
277 | |
278 | list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { | |
279 | struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc); | |
280 | ||
281 | nv50_crtc_blank(crtc, true); | |
282 | } | |
283 | ||
ef8389a8 | 284 | ret = RING_SPACE(evo, 2); |
6ee73861 | 285 | if (ret == 0) { |
ef8389a8 BS |
286 | BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); |
287 | OUT_RING(evo, 0); | |
6ee73861 | 288 | } |
ef8389a8 | 289 | FIRE_RING(evo); |
6ee73861 BS |
290 | |
291 | /* Almost like ack'ing a vblank interrupt, maybe in the spirit of | |
292 | * cleaning up? | |
293 | */ | |
294 | list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { | |
295 | struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc); | |
296 | uint32_t mask = NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(crtc->index); | |
297 | ||
298 | if (!crtc->base.enabled) | |
299 | continue; | |
300 | ||
301 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, mask); | |
4b5c152a | 302 | if (!nv_wait(dev, NV50_PDISPLAY_INTR_1, mask, mask)) { |
6ee73861 BS |
303 | NV_ERROR(dev, "timeout: (0x610024 & 0x%08x) == " |
304 | "0x%08x\n", mask, mask); | |
305 | NV_ERROR(dev, "0x610024 = 0x%08x\n", | |
306 | nv_rd32(dev, NV50_PDISPLAY_INTR_1)); | |
307 | } | |
308 | } | |
309 | ||
048a8859 BS |
310 | for (i = 0; i < 2; i++) { |
311 | nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0); | |
312 | if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), | |
313 | NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) { | |
314 | NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n"); | |
315 | NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n", | |
316 | nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i))); | |
317 | } | |
318 | } | |
319 | ||
b7bc613a | 320 | nv50_evo_fini(dev); |
6ee73861 BS |
321 | |
322 | for (i = 0; i < 3; i++) { | |
4b5c152a | 323 | if (!nv_wait(dev, NV50_PDISPLAY_SOR_DPMS_STATE(i), |
6ee73861 BS |
324 | NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) { |
325 | NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", i); | |
326 | NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", i, | |
327 | nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(i))); | |
328 | } | |
329 | } | |
330 | ||
331 | /* disable interrupts. */ | |
97e2000f | 332 | nv_wr32(dev, NV50_PDISPLAY_INTR_EN_1, 0x00000000); |
6ee73861 BS |
333 | |
334 | /* disable hotplug interrupts */ | |
335 | nv_wr32(dev, 0xe054, 0xffffffff); | |
336 | nv_wr32(dev, 0xe050, 0x00000000); | |
337 | if (dev_priv->chipset >= 0x90) { | |
338 | nv_wr32(dev, 0xe074, 0xffffffff); | |
339 | nv_wr32(dev, 0xe070, 0x00000000); | |
340 | } | |
6ee73861 BS |
341 | } |
342 | ||
2a44e499 BS |
343 | int |
344 | nv50_display_create(struct drm_device *dev) | |
6ee73861 BS |
345 | { |
346 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
04a39c57 | 347 | struct dcb_table *dcb = &dev_priv->vbios.dcb; |
8f1a6086 | 348 | struct drm_connector *connector, *ct; |
ef8389a8 | 349 | struct nv50_display *priv; |
1772fcc6 | 350 | int ret, i; |
6ee73861 | 351 | |
ef2bb506 | 352 | NV_DEBUG_KMS(dev, "\n"); |
6ee73861 | 353 | |
ef8389a8 BS |
354 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
355 | if (!priv) | |
356 | return -ENOMEM; | |
357 | dev_priv->engine.display.priv = priv; | |
358 | ||
6ee73861 BS |
359 | /* Create CRTC objects */ |
360 | for (i = 0; i < 2; i++) | |
361 | nv50_crtc_create(dev, i); | |
362 | ||
363 | /* We setup the encoders from the BIOS table */ | |
364 | for (i = 0 ; i < dcb->entries; i++) { | |
365 | struct dcb_entry *entry = &dcb->entry[i]; | |
366 | ||
367 | if (entry->location != DCB_LOC_ON_CHIP) { | |
368 | NV_WARN(dev, "Off-chip encoder %d/%d unsupported\n", | |
369 | entry->type, ffs(entry->or) - 1); | |
370 | continue; | |
371 | } | |
372 | ||
8f1a6086 BS |
373 | connector = nouveau_connector_create(dev, entry->connector); |
374 | if (IS_ERR(connector)) | |
375 | continue; | |
376 | ||
6ee73861 BS |
377 | switch (entry->type) { |
378 | case OUTPUT_TMDS: | |
379 | case OUTPUT_LVDS: | |
380 | case OUTPUT_DP: | |
8f1a6086 | 381 | nv50_sor_create(connector, entry); |
6ee73861 BS |
382 | break; |
383 | case OUTPUT_ANALOG: | |
8f1a6086 | 384 | nv50_dac_create(connector, entry); |
6ee73861 BS |
385 | break; |
386 | default: | |
387 | NV_WARN(dev, "DCB encoder %d unknown\n", entry->type); | |
388 | continue; | |
389 | } | |
6ee73861 BS |
390 | } |
391 | ||
8f1a6086 BS |
392 | list_for_each_entry_safe(connector, ct, |
393 | &dev->mode_config.connector_list, head) { | |
394 | if (!connector->encoder_ids[0]) { | |
395 | NV_WARN(dev, "%s has no encoders, removing\n", | |
396 | drm_get_connector_name(connector)); | |
397 | connector->funcs->destroy(connector); | |
398 | } | |
6ee73861 BS |
399 | } |
400 | ||
f13e435c | 401 | tasklet_init(&priv->tasklet, nv50_display_bh, (unsigned long)dev); |
19b7fc7b | 402 | nouveau_irq_register(dev, 26, nv50_display_isr); |
1772fcc6 BS |
403 | |
404 | ret = nv50_evo_create(dev); | |
405 | if (ret) { | |
406 | nv50_display_destroy(dev); | |
407 | return ret; | |
408 | } | |
409 | ||
6ee73861 BS |
410 | return 0; |
411 | } | |
412 | ||
c88c2e06 FJ |
413 | void |
414 | nv50_display_destroy(struct drm_device *dev) | |
6ee73861 | 415 | { |
ef8389a8 | 416 | struct nv50_display *disp = nv50_display(dev); |
d82f8e6c | 417 | |
ef2bb506 | 418 | NV_DEBUG_KMS(dev, "\n"); |
6ee73861 | 419 | |
1772fcc6 | 420 | nv50_evo_destroy(dev); |
19b7fc7b | 421 | nouveau_irq_unregister(dev, 26); |
ef8389a8 | 422 | kfree(disp); |
6ee73861 BS |
423 | } |
424 | ||
cdccc70e BS |
425 | void |
426 | nv50_display_flip_stop(struct drm_crtc *crtc) | |
427 | { | |
428 | struct nv50_display *disp = nv50_display(crtc->dev); | |
429 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | |
430 | struct nv50_display_crtc *dispc = &disp->crtc[nv_crtc->index]; | |
431 | struct nouveau_channel *evo = dispc->sync; | |
432 | int ret; | |
433 | ||
434 | ret = RING_SPACE(evo, 8); | |
435 | if (ret) { | |
436 | WARN_ON(1); | |
437 | return; | |
438 | } | |
439 | ||
440 | BEGIN_RING(evo, 0, 0x0084, 1); | |
441 | OUT_RING (evo, 0x00000000); | |
442 | BEGIN_RING(evo, 0, 0x0094, 1); | |
443 | OUT_RING (evo, 0x00000000); | |
444 | BEGIN_RING(evo, 0, 0x00c0, 1); | |
445 | OUT_RING (evo, 0x00000000); | |
446 | BEGIN_RING(evo, 0, 0x0080, 1); | |
447 | OUT_RING (evo, 0x00000000); | |
448 | FIRE_RING (evo); | |
449 | } | |
450 | ||
451 | int | |
452 | nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, | |
453 | struct nouveau_channel *chan) | |
454 | { | |
455 | struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; | |
456 | struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); | |
457 | struct nv50_display *disp = nv50_display(crtc->dev); | |
458 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | |
459 | struct nv50_display_crtc *dispc = &disp->crtc[nv_crtc->index]; | |
460 | struct nouveau_channel *evo = dispc->sync; | |
461 | int ret; | |
462 | ||
f66b3d55 | 463 | ret = RING_SPACE(evo, chan ? 25 : 27); |
cdccc70e BS |
464 | if (unlikely(ret)) |
465 | return ret; | |
466 | ||
467 | /* synchronise with the rendering channel, if necessary */ | |
468 | if (likely(chan)) { | |
cdccc70e BS |
469 | ret = RING_SPACE(chan, 10); |
470 | if (ret) { | |
471 | WIND_RING(evo); | |
472 | return ret; | |
473 | } | |
474 | ||
475 | if (dev_priv->chipset < 0xc0) { | |
476 | BEGIN_RING(chan, NvSubSw, 0x0060, 2); | |
477 | OUT_RING (chan, NvEvoSema0 + nv_crtc->index); | |
478 | OUT_RING (chan, dispc->sem.offset); | |
479 | BEGIN_RING(chan, NvSubSw, 0x006c, 1); | |
480 | OUT_RING (chan, 0xf00d0000 | dispc->sem.value); | |
481 | BEGIN_RING(chan, NvSubSw, 0x0064, 2); | |
482 | OUT_RING (chan, dispc->sem.offset ^ 0x10); | |
483 | OUT_RING (chan, 0x74b1e000); | |
484 | BEGIN_RING(chan, NvSubSw, 0x0060, 1); | |
485 | if (dev_priv->chipset < 0x84) | |
486 | OUT_RING (chan, NvSema); | |
487 | else | |
488 | OUT_RING (chan, chan->vram_handle); | |
489 | } else { | |
3d483d57 BS |
490 | u64 offset = chan->dispc_vma[nv_crtc->index].offset; |
491 | offset += dispc->sem.offset; | |
cdccc70e BS |
492 | BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4); |
493 | OUT_RING (chan, upper_32_bits(offset)); | |
494 | OUT_RING (chan, lower_32_bits(offset)); | |
495 | OUT_RING (chan, 0xf00d0000 | dispc->sem.value); | |
496 | OUT_RING (chan, 0x1002); | |
497 | BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4); | |
498 | OUT_RING (chan, upper_32_bits(offset)); | |
499 | OUT_RING (chan, lower_32_bits(offset ^ 0x10)); | |
500 | OUT_RING (chan, 0x74b1e000); | |
501 | OUT_RING (chan, 0x1001); | |
502 | } | |
503 | FIRE_RING (chan); | |
504 | } else { | |
505 | nouveau_bo_wr32(dispc->sem.bo, dispc->sem.offset / 4, | |
506 | 0xf00d0000 | dispc->sem.value); | |
507 | } | |
508 | ||
509 | /* queue the flip on the crtc's "display sync" channel */ | |
510 | BEGIN_RING(evo, 0, 0x0100, 1); | |
511 | OUT_RING (evo, 0xfffe0000); | |
f66b3d55 BS |
512 | if (chan) { |
513 | BEGIN_RING(evo, 0, 0x0084, 1); | |
514 | OUT_RING (evo, 0x00000100); | |
515 | } else { | |
516 | BEGIN_RING(evo, 0, 0x0084, 1); | |
517 | OUT_RING (evo, 0x00000010); | |
518 | /* allows gamma somehow, PDISP will bitch at you if | |
519 | * you don't wait for vblank before changing this.. | |
520 | */ | |
521 | BEGIN_RING(evo, 0, 0x00e0, 1); | |
522 | OUT_RING (evo, 0x40000000); | |
523 | } | |
524 | BEGIN_RING(evo, 0, 0x0088, 4); | |
cdccc70e BS |
525 | OUT_RING (evo, dispc->sem.offset); |
526 | OUT_RING (evo, 0xf00d0000 | dispc->sem.value); | |
527 | OUT_RING (evo, 0x74b1e000); | |
528 | OUT_RING (evo, NvEvoSync); | |
529 | BEGIN_RING(evo, 0, 0x00a0, 2); | |
530 | OUT_RING (evo, 0x00000000); | |
531 | OUT_RING (evo, 0x00000000); | |
532 | BEGIN_RING(evo, 0, 0x00c0, 1); | |
533 | OUT_RING (evo, nv_fb->r_dma); | |
534 | BEGIN_RING(evo, 0, 0x0110, 2); | |
535 | OUT_RING (evo, 0x00000000); | |
536 | OUT_RING (evo, 0x00000000); | |
537 | BEGIN_RING(evo, 0, 0x0800, 5); | |
180cc306 | 538 | OUT_RING (evo, nv_fb->nvbo->bo.offset >> 8); |
cdccc70e BS |
539 | OUT_RING (evo, 0); |
540 | OUT_RING (evo, (fb->height << 16) | fb->width); | |
541 | OUT_RING (evo, nv_fb->r_pitch); | |
542 | OUT_RING (evo, nv_fb->r_format); | |
543 | BEGIN_RING(evo, 0, 0x0080, 1); | |
544 | OUT_RING (evo, 0x00000000); | |
545 | FIRE_RING (evo); | |
546 | ||
547 | dispc->sem.offset ^= 0x10; | |
548 | dispc->sem.value++; | |
549 | return 0; | |
550 | } | |
551 | ||
87c0e0e5 BS |
552 | static u16 |
553 | nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcb, | |
554 | u32 mc, int pxclk) | |
6ee73861 BS |
555 | { |
556 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
75c722d7 BS |
557 | struct nouveau_connector *nv_connector = NULL; |
558 | struct drm_encoder *encoder; | |
04a39c57 | 559 | struct nvbios *bios = &dev_priv->vbios; |
87c0e0e5 | 560 | u32 script = 0, or; |
6ee73861 | 561 | |
75c722d7 BS |
562 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
563 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
564 | ||
87c0e0e5 | 565 | if (nv_encoder->dcb != dcb) |
75c722d7 BS |
566 | continue; |
567 | ||
568 | nv_connector = nouveau_encoder_connector_get(nv_encoder); | |
569 | break; | |
570 | } | |
571 | ||
87c0e0e5 BS |
572 | or = ffs(dcb->or) - 1; |
573 | switch (dcb->type) { | |
6ee73861 BS |
574 | case OUTPUT_LVDS: |
575 | script = (mc >> 8) & 0xf; | |
04a39c57 | 576 | if (bios->fp_no_ddc) { |
6ee73861 BS |
577 | if (bios->fp.dual_link) |
578 | script |= 0x0100; | |
579 | if (bios->fp.if_is_24bit) | |
580 | script |= 0x0200; | |
581 | } else { | |
b23b9e71 BS |
582 | /* determine number of lvds links */ |
583 | if (nv_connector && nv_connector->edid && | |
584 | nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) { | |
585 | /* http://www.spwg.org */ | |
586 | if (((u8 *)nv_connector->edid)[121] == 2) | |
587 | script |= 0x0100; | |
588 | } else | |
6ee73861 BS |
589 | if (pxclk >= bios->fp.duallink_transition_clk) { |
590 | script |= 0x0100; | |
b23b9e71 BS |
591 | } |
592 | ||
593 | /* determine panel depth */ | |
594 | if (script & 0x0100) { | |
6ee73861 BS |
595 | if (bios->fp.strapless_is_24bit & 2) |
596 | script |= 0x0200; | |
b23b9e71 BS |
597 | } else { |
598 | if (bios->fp.strapless_is_24bit & 1) | |
599 | script |= 0x0200; | |
600 | } | |
75c722d7 BS |
601 | |
602 | if (nv_connector && nv_connector->edid && | |
603 | (nv_connector->edid->revision >= 4) && | |
604 | (nv_connector->edid->input & 0x70) >= 0x20) | |
605 | script |= 0x0200; | |
6ee73861 BS |
606 | } |
607 | ||
608 | if (nouveau_uscript_lvds >= 0) { | |
609 | NV_INFO(dev, "override script 0x%04x with 0x%04x " | |
610 | "for output LVDS-%d\n", script, | |
611 | nouveau_uscript_lvds, or); | |
612 | script = nouveau_uscript_lvds; | |
613 | } | |
614 | break; | |
615 | case OUTPUT_TMDS: | |
616 | script = (mc >> 8) & 0xf; | |
617 | if (pxclk >= 165000) | |
618 | script |= 0x0100; | |
619 | ||
620 | if (nouveau_uscript_tmds >= 0) { | |
621 | NV_INFO(dev, "override script 0x%04x with 0x%04x " | |
622 | "for output TMDS-%d\n", script, | |
623 | nouveau_uscript_tmds, or); | |
624 | script = nouveau_uscript_tmds; | |
625 | } | |
626 | break; | |
627 | case OUTPUT_DP: | |
628 | script = (mc >> 8) & 0xf; | |
629 | break; | |
630 | case OUTPUT_ANALOG: | |
631 | script = 0xff; | |
632 | break; | |
633 | default: | |
634 | NV_ERROR(dev, "modeset on unsupported output type!\n"); | |
635 | break; | |
636 | } | |
637 | ||
638 | return script; | |
639 | } | |
640 | ||
641 | static void | |
642 | nv50_display_vblank_crtc_handler(struct drm_device *dev, int crtc) | |
643 | { | |
644 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
042206c0 | 645 | struct nouveau_channel *chan, *tmp; |
6ee73861 | 646 | |
042206c0 FJ |
647 | list_for_each_entry_safe(chan, tmp, &dev_priv->vbl_waiting, |
648 | nvsw.vbl_wait) { | |
1f6d2de2 FJ |
649 | if (chan->nvsw.vblsem_head != crtc) |
650 | continue; | |
651 | ||
6ee73861 BS |
652 | nouveau_bo_wr32(chan->notifier_bo, chan->nvsw.vblsem_offset, |
653 | chan->nvsw.vblsem_rval); | |
654 | list_del(&chan->nvsw.vbl_wait); | |
042206c0 | 655 | drm_vblank_put(dev, crtc); |
6ee73861 | 656 | } |
042206c0 FJ |
657 | |
658 | drm_handle_vblank(dev, crtc); | |
6ee73861 BS |
659 | } |
660 | ||
661 | static void | |
662 | nv50_display_vblank_handler(struct drm_device *dev, uint32_t intr) | |
663 | { | |
6ee73861 BS |
664 | if (intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_0) |
665 | nv50_display_vblank_crtc_handler(dev, 0); | |
666 | ||
667 | if (intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_1) | |
668 | nv50_display_vblank_crtc_handler(dev, 1); | |
669 | ||
042206c0 | 670 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_VBLANK_CRTC); |
6ee73861 BS |
671 | } |
672 | ||
673 | static void | |
674 | nv50_display_unk10_handler(struct drm_device *dev) | |
675 | { | |
87c0e0e5 | 676 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
ef8389a8 | 677 | struct nv50_display *disp = nv50_display(dev); |
87c0e0e5 | 678 | u32 unk30 = nv_rd32(dev, 0x610030), mc; |
a55b68e0 | 679 | int i, crtc, or = 0, type = OUTPUT_ANY; |
6ee73861 | 680 | |
87c0e0e5 | 681 | NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); |
ef8389a8 | 682 | disp->irq.dcb = NULL; |
6ee73861 BS |
683 | |
684 | nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) & ~8); | |
685 | ||
87c0e0e5 BS |
686 | /* Determine which CRTC we're dealing with, only 1 ever will be |
687 | * signalled at the same time with the current nouveau code. | |
688 | */ | |
689 | crtc = ffs((unk30 & 0x00000060) >> 5) - 1; | |
690 | if (crtc < 0) | |
691 | goto ack; | |
692 | ||
693 | /* Nothing needs to be done for the encoder */ | |
694 | crtc = ffs((unk30 & 0x00000180) >> 7) - 1; | |
695 | if (crtc < 0) | |
696 | goto ack; | |
6ee73861 | 697 | |
87c0e0e5 BS |
698 | /* Find which encoder was connected to the CRTC */ |
699 | for (i = 0; type == OUTPUT_ANY && i < 3; i++) { | |
700 | mc = nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_C(i)); | |
701 | NV_DEBUG_KMS(dev, "DAC-%d mc: 0x%08x\n", i, mc); | |
702 | if (!(mc & (1 << crtc))) | |
703 | continue; | |
704 | ||
705 | switch ((mc & 0x00000f00) >> 8) { | |
706 | case 0: type = OUTPUT_ANALOG; break; | |
707 | case 1: type = OUTPUT_TV; break; | |
708 | default: | |
709 | NV_ERROR(dev, "invalid mc, DAC-%d: 0x%08x\n", i, mc); | |
710 | goto ack; | |
711 | } | |
712 | ||
713 | or = i; | |
714 | } | |
715 | ||
8597a1ba | 716 | for (i = 0; type == OUTPUT_ANY && i < nv50_sor_nr(dev); i++) { |
87c0e0e5 BS |
717 | if (dev_priv->chipset < 0x90 || |
718 | dev_priv->chipset == 0x92 || | |
719 | dev_priv->chipset == 0xa0) | |
720 | mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_C(i)); | |
721 | else | |
722 | mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_C(i)); | |
723 | ||
724 | NV_DEBUG_KMS(dev, "SOR-%d mc: 0x%08x\n", i, mc); | |
725 | if (!(mc & (1 << crtc))) | |
726 | continue; | |
727 | ||
728 | switch ((mc & 0x00000f00) >> 8) { | |
729 | case 0: type = OUTPUT_LVDS; break; | |
730 | case 1: type = OUTPUT_TMDS; break; | |
731 | case 2: type = OUTPUT_TMDS; break; | |
732 | case 5: type = OUTPUT_TMDS; break; | |
733 | case 8: type = OUTPUT_DP; break; | |
734 | case 9: type = OUTPUT_DP; break; | |
735 | default: | |
736 | NV_ERROR(dev, "invalid mc, SOR-%d: 0x%08x\n", i, mc); | |
737 | goto ack; | |
738 | } | |
739 | ||
740 | or = i; | |
741 | } | |
742 | ||
743 | /* There was no encoder to disable */ | |
744 | if (type == OUTPUT_ANY) | |
745 | goto ack; | |
746 | ||
747 | /* Disable the encoder */ | |
748 | for (i = 0; i < dev_priv->vbios.dcb.entries; i++) { | |
749 | struct dcb_entry *dcb = &dev_priv->vbios.dcb.entry[i]; | |
750 | ||
751 | if (dcb->type == type && (dcb->or & (1 << or))) { | |
02e4f587 | 752 | nouveau_bios_run_display_table(dev, 0, -1, dcb, -1); |
ef8389a8 | 753 | disp->irq.dcb = dcb; |
87c0e0e5 BS |
754 | goto ack; |
755 | } | |
756 | } | |
757 | ||
758 | NV_ERROR(dev, "no dcb for %d %d 0x%08x\n", or, type, mc); | |
6ee73861 BS |
759 | ack: |
760 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK10); | |
761 | nv_wr32(dev, 0x610030, 0x80000000); | |
762 | } | |
763 | ||
764 | static void | |
765 | nv50_display_unk20_handler(struct drm_device *dev) | |
766 | { | |
87c0e0e5 | 767 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
ef8389a8 | 768 | struct nv50_display *disp = nv50_display(dev); |
ea5f2786 | 769 | u32 unk30 = nv_rd32(dev, 0x610030), tmp, pclk, script, mc = 0; |
87c0e0e5 | 770 | struct dcb_entry *dcb; |
a55b68e0 | 771 | int i, crtc, or = 0, type = OUTPUT_ANY; |
6ee73861 | 772 | |
87c0e0e5 | 773 | NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); |
ef8389a8 | 774 | dcb = disp->irq.dcb; |
87c0e0e5 | 775 | if (dcb) { |
02e4f587 | 776 | nouveau_bios_run_display_table(dev, 0, -2, dcb, -1); |
ef8389a8 | 777 | disp->irq.dcb = NULL; |
87c0e0e5 BS |
778 | } |
779 | ||
780 | /* CRTC clock change requested? */ | |
781 | crtc = ffs((unk30 & 0x00000600) >> 9) - 1; | |
782 | if (crtc >= 0) { | |
783 | pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(crtc, CLOCK)); | |
784 | pclk &= 0x003fffff; | |
b98e3f5c BS |
785 | if (pclk) |
786 | nv50_crtc_set_clock(dev, crtc, pclk); | |
87c0e0e5 BS |
787 | |
788 | tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc)); | |
789 | tmp &= ~0x000000f; | |
790 | nv_wr32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc), tmp); | |
791 | } | |
792 | ||
793 | /* Nothing needs to be done for the encoder */ | |
794 | crtc = ffs((unk30 & 0x00000180) >> 7) - 1; | |
795 | if (crtc < 0) | |
6ee73861 | 796 | goto ack; |
87c0e0e5 | 797 | pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(crtc, CLOCK)) & 0x003fffff; |
6ee73861 | 798 | |
87c0e0e5 BS |
799 | /* Find which encoder is connected to the CRTC */ |
800 | for (i = 0; type == OUTPUT_ANY && i < 3; i++) { | |
801 | mc = nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_P(i)); | |
802 | NV_DEBUG_KMS(dev, "DAC-%d mc: 0x%08x\n", i, mc); | |
803 | if (!(mc & (1 << crtc))) | |
804 | continue; | |
6ee73861 | 805 | |
87c0e0e5 BS |
806 | switch ((mc & 0x00000f00) >> 8) { |
807 | case 0: type = OUTPUT_ANALOG; break; | |
808 | case 1: type = OUTPUT_TV; break; | |
809 | default: | |
810 | NV_ERROR(dev, "invalid mc, DAC-%d: 0x%08x\n", i, mc); | |
811 | goto ack; | |
812 | } | |
813 | ||
814 | or = i; | |
815 | } | |
816 | ||
8597a1ba | 817 | for (i = 0; type == OUTPUT_ANY && i < nv50_sor_nr(dev); i++) { |
87c0e0e5 BS |
818 | if (dev_priv->chipset < 0x90 || |
819 | dev_priv->chipset == 0x92 || | |
820 | dev_priv->chipset == 0xa0) | |
821 | mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_P(i)); | |
822 | else | |
823 | mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_P(i)); | |
824 | ||
825 | NV_DEBUG_KMS(dev, "SOR-%d mc: 0x%08x\n", i, mc); | |
826 | if (!(mc & (1 << crtc))) | |
827 | continue; | |
828 | ||
829 | switch ((mc & 0x00000f00) >> 8) { | |
830 | case 0: type = OUTPUT_LVDS; break; | |
831 | case 1: type = OUTPUT_TMDS; break; | |
832 | case 2: type = OUTPUT_TMDS; break; | |
833 | case 5: type = OUTPUT_TMDS; break; | |
834 | case 8: type = OUTPUT_DP; break; | |
835 | case 9: type = OUTPUT_DP; break; | |
836 | default: | |
837 | NV_ERROR(dev, "invalid mc, SOR-%d: 0x%08x\n", i, mc); | |
838 | goto ack; | |
839 | } | |
6ee73861 | 840 | |
87c0e0e5 BS |
841 | or = i; |
842 | } | |
6ee73861 | 843 | |
87c0e0e5 BS |
844 | if (type == OUTPUT_ANY) |
845 | goto ack; | |
6ee73861 | 846 | |
87c0e0e5 BS |
847 | /* Enable the encoder */ |
848 | for (i = 0; i < dev_priv->vbios.dcb.entries; i++) { | |
849 | dcb = &dev_priv->vbios.dcb.entry[i]; | |
850 | if (dcb->type == type && (dcb->or & (1 << or))) | |
851 | break; | |
852 | } | |
afa3b4c3 | 853 | |
87c0e0e5 BS |
854 | if (i == dev_priv->vbios.dcb.entries) { |
855 | NV_ERROR(dev, "no dcb for %d %d 0x%08x\n", or, type, mc); | |
856 | goto ack; | |
857 | } | |
858 | ||
859 | script = nv50_display_script_select(dev, dcb, mc, pclk); | |
02e4f587 | 860 | nouveau_bios_run_display_table(dev, script, pclk, dcb, -1); |
6ee73861 | 861 | |
46959b77 BS |
862 | if (type == OUTPUT_DP) { |
863 | int link = !(dcb->dpconf.sor.link & 1); | |
864 | if ((mc & 0x000f0000) == 0x00020000) | |
865 | nouveau_dp_tu_update(dev, or, link, pclk, 18); | |
866 | else | |
867 | nouveau_dp_tu_update(dev, or, link, pclk, 24); | |
868 | } | |
87c0e0e5 BS |
869 | |
870 | if (dcb->type != OUTPUT_ANALOG) { | |
6ee73861 BS |
871 | tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or)); |
872 | tmp &= ~0x00000f0f; | |
873 | if (script & 0x0100) | |
874 | tmp |= 0x00000101; | |
875 | nv_wr32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or), tmp); | |
876 | } else { | |
877 | nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL2(or), 0); | |
878 | } | |
879 | ||
ef8389a8 BS |
880 | disp->irq.dcb = dcb; |
881 | disp->irq.pclk = pclk; | |
882 | disp->irq.script = script; | |
87c0e0e5 | 883 | |
6ee73861 BS |
884 | ack: |
885 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK20); | |
886 | nv_wr32(dev, 0x610030, 0x80000000); | |
887 | } | |
888 | ||
271f29e7 BS |
889 | /* If programming a TMDS output on a SOR that can also be configured for |
890 | * DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off. | |
891 | * | |
892 | * It looks like the VBIOS TMDS scripts make an attempt at this, however, | |
893 | * the VBIOS scripts on at least one board I have only switch it off on | |
894 | * link 0, causing a blank display if the output has previously been | |
895 | * programmed for DisplayPort. | |
896 | */ | |
897 | static void | |
898 | nv50_display_unk40_dp_set_tmds(struct drm_device *dev, struct dcb_entry *dcb) | |
899 | { | |
900 | int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1); | |
901 | struct drm_encoder *encoder; | |
902 | u32 tmp; | |
903 | ||
904 | if (dcb->type != OUTPUT_TMDS) | |
905 | return; | |
906 | ||
907 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | |
908 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
909 | ||
910 | if (nv_encoder->dcb->type == OUTPUT_DP && | |
911 | nv_encoder->dcb->or & (1 << or)) { | |
912 | tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); | |
913 | tmp &= ~NV50_SOR_DP_CTRL_ENABLED; | |
914 | nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); | |
915 | break; | |
916 | } | |
917 | } | |
918 | } | |
919 | ||
6ee73861 BS |
920 | static void |
921 | nv50_display_unk40_handler(struct drm_device *dev) | |
922 | { | |
ef8389a8 BS |
923 | struct nv50_display *disp = nv50_display(dev); |
924 | struct dcb_entry *dcb = disp->irq.dcb; | |
925 | u16 script = disp->irq.script; | |
926 | u32 unk30 = nv_rd32(dev, 0x610030), pclk = disp->irq.pclk; | |
6ee73861 | 927 | |
87c0e0e5 | 928 | NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); |
ef8389a8 | 929 | disp->irq.dcb = NULL; |
87c0e0e5 | 930 | if (!dcb) |
6ee73861 | 931 | goto ack; |
6ee73861 | 932 | |
02e4f587 | 933 | nouveau_bios_run_display_table(dev, script, -pclk, dcb, -1); |
271f29e7 BS |
934 | nv50_display_unk40_dp_set_tmds(dev, dcb); |
935 | ||
6ee73861 BS |
936 | ack: |
937 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK40); | |
938 | nv_wr32(dev, 0x610030, 0x80000000); | |
939 | nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) | 8); | |
940 | } | |
941 | ||
f13e435c BS |
942 | static void |
943 | nv50_display_bh(unsigned long data) | |
6ee73861 | 944 | { |
f13e435c | 945 | struct drm_device *dev = (struct drm_device *)data; |
6ee73861 BS |
946 | |
947 | for (;;) { | |
948 | uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0); | |
949 | uint32_t intr1 = nv_rd32(dev, NV50_PDISPLAY_INTR_1); | |
950 | ||
ef2bb506 | 951 | NV_DEBUG_KMS(dev, "PDISPLAY_INTR_BH 0x%08x 0x%08x\n", intr0, intr1); |
6ee73861 BS |
952 | |
953 | if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK10) | |
954 | nv50_display_unk10_handler(dev); | |
955 | else | |
956 | if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK20) | |
957 | nv50_display_unk20_handler(dev); | |
958 | else | |
959 | if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK40) | |
960 | nv50_display_unk40_handler(dev); | |
961 | else | |
962 | break; | |
963 | } | |
964 | ||
965 | nv_wr32(dev, NV03_PMC_INTR_EN_0, 1); | |
966 | } | |
967 | ||
968 | static void | |
969 | nv50_display_error_handler(struct drm_device *dev) | |
970 | { | |
97e2000f BS |
971 | u32 channels = (nv_rd32(dev, NV50_PDISPLAY_INTR_0) & 0x001f0000) >> 16; |
972 | u32 addr, data; | |
973 | int chid; | |
6ee73861 | 974 | |
97e2000f BS |
975 | for (chid = 0; chid < 5; chid++) { |
976 | if (!(channels & (1 << chid))) | |
977 | continue; | |
6ee73861 | 978 | |
97e2000f BS |
979 | nv_wr32(dev, NV50_PDISPLAY_INTR_0, 0x00010000 << chid); |
980 | addr = nv_rd32(dev, NV50_PDISPLAY_TRAPPED_ADDR(chid)); | |
981 | data = nv_rd32(dev, NV50_PDISPLAY_TRAPPED_DATA(chid)); | |
982 | NV_ERROR(dev, "EvoCh %d Mthd 0x%04x Data 0x%08x " | |
983 | "(0x%04x 0x%02x)\n", chid, | |
984 | addr & 0xffc, data, addr >> 16, (addr >> 12) & 0xf); | |
6ee73861 | 985 | |
97e2000f BS |
986 | nv_wr32(dev, NV50_PDISPLAY_TRAPPED_ADDR(chid), 0x90000000); |
987 | } | |
6ee73861 BS |
988 | } |
989 | ||
19b7fc7b BS |
990 | static void |
991 | nv50_display_isr(struct drm_device *dev) | |
6ee73861 | 992 | { |
f13e435c | 993 | struct nv50_display *disp = nv50_display(dev); |
6ee73861 BS |
994 | uint32_t delayed = 0; |
995 | ||
6ee73861 BS |
996 | while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) { |
997 | uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0); | |
998 | uint32_t intr1 = nv_rd32(dev, NV50_PDISPLAY_INTR_1); | |
999 | uint32_t clock; | |
1000 | ||
ef2bb506 | 1001 | NV_DEBUG_KMS(dev, "PDISPLAY_INTR 0x%08x 0x%08x\n", intr0, intr1); |
6ee73861 BS |
1002 | |
1003 | if (!intr0 && !(intr1 & ~delayed)) | |
1004 | break; | |
1005 | ||
97e2000f | 1006 | if (intr0 & 0x001f0000) { |
6ee73861 | 1007 | nv50_display_error_handler(dev); |
97e2000f | 1008 | intr0 &= ~0x001f0000; |
6ee73861 BS |
1009 | } |
1010 | ||
1011 | if (intr1 & NV50_PDISPLAY_INTR_1_VBLANK_CRTC) { | |
1012 | nv50_display_vblank_handler(dev, intr1); | |
1013 | intr1 &= ~NV50_PDISPLAY_INTR_1_VBLANK_CRTC; | |
1014 | } | |
1015 | ||
1016 | clock = (intr1 & (NV50_PDISPLAY_INTR_1_CLK_UNK10 | | |
1017 | NV50_PDISPLAY_INTR_1_CLK_UNK20 | | |
1018 | NV50_PDISPLAY_INTR_1_CLK_UNK40)); | |
1019 | if (clock) { | |
1020 | nv_wr32(dev, NV03_PMC_INTR_EN_0, 0); | |
f13e435c | 1021 | tasklet_schedule(&disp->tasklet); |
6ee73861 BS |
1022 | delayed |= clock; |
1023 | intr1 &= ~clock; | |
1024 | } | |
1025 | ||
1026 | if (intr0) { | |
1027 | NV_ERROR(dev, "unknown PDISPLAY_INTR_0: 0x%08x\n", intr0); | |
1028 | nv_wr32(dev, NV50_PDISPLAY_INTR_0, intr0); | |
1029 | } | |
1030 | ||
1031 | if (intr1) { | |
1032 | NV_ERROR(dev, | |
1033 | "unknown PDISPLAY_INTR_1: 0x%08x\n", intr1); | |
1034 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, intr1); | |
1035 | } | |
1036 | } | |
1037 | } |