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