diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvc0_fifo.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nvc0_fifo.c | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c index 890c2b95fbc1..e6f92c541dba 100644 --- a/drivers/gpu/drm/nouveau/nvc0_fifo.c +++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c | |||
@@ -25,6 +25,49 @@ | |||
25 | #include "drmP.h" | 25 | #include "drmP.h" |
26 | 26 | ||
27 | #include "nouveau_drv.h" | 27 | #include "nouveau_drv.h" |
28 | #include "nouveau_mm.h" | ||
29 | |||
30 | static void nvc0_fifo_isr(struct drm_device *); | ||
31 | |||
32 | struct nvc0_fifo_priv { | ||
33 | struct nouveau_gpuobj *playlist[2]; | ||
34 | int cur_playlist; | ||
35 | struct nouveau_vma user_vma; | ||
36 | int spoon_nr; | ||
37 | }; | ||
38 | |||
39 | struct nvc0_fifo_chan { | ||
40 | struct nouveau_bo *user; | ||
41 | struct nouveau_gpuobj *ramfc; | ||
42 | }; | ||
43 | |||
44 | static void | ||
45 | nvc0_fifo_playlist_update(struct drm_device *dev) | ||
46 | { | ||
47 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
48 | struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; | ||
49 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
50 | struct nvc0_fifo_priv *priv = pfifo->priv; | ||
51 | struct nouveau_gpuobj *cur; | ||
52 | int i, p; | ||
53 | |||
54 | cur = priv->playlist[priv->cur_playlist]; | ||
55 | priv->cur_playlist = !priv->cur_playlist; | ||
56 | |||
57 | for (i = 0, p = 0; i < 128; i++) { | ||
58 | if (!(nv_rd32(dev, 0x3004 + (i * 8)) & 1)) | ||
59 | continue; | ||
60 | nv_wo32(cur, p + 0, i); | ||
61 | nv_wo32(cur, p + 4, 0x00000004); | ||
62 | p += 8; | ||
63 | } | ||
64 | pinstmem->flush(dev); | ||
65 | |||
66 | nv_wr32(dev, 0x002270, cur->vinst >> 12); | ||
67 | nv_wr32(dev, 0x002274, 0x01f00000 | (p >> 3)); | ||
68 | if (!nv_wait(dev, 0x00227c, 0x00100000, 0x00000000)) | ||
69 | NV_ERROR(dev, "PFIFO - playlist update failed\n"); | ||
70 | } | ||
28 | 71 | ||
29 | void | 72 | void |
30 | nvc0_fifo_disable(struct drm_device *dev) | 73 | nvc0_fifo_disable(struct drm_device *dev) |
@@ -57,12 +100,135 @@ nvc0_fifo_channel_id(struct drm_device *dev) | |||
57 | int | 100 | int |
58 | nvc0_fifo_create_context(struct nouveau_channel *chan) | 101 | nvc0_fifo_create_context(struct nouveau_channel *chan) |
59 | { | 102 | { |
103 | struct drm_device *dev = chan->dev; | ||
104 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
105 | struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; | ||
106 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
107 | struct nvc0_fifo_priv *priv = pfifo->priv; | ||
108 | struct nvc0_fifo_chan *fifoch; | ||
109 | u64 ib_virt, user_vinst; | ||
110 | int ret; | ||
111 | |||
112 | chan->fifo_priv = kzalloc(sizeof(*fifoch), GFP_KERNEL); | ||
113 | if (!chan->fifo_priv) | ||
114 | return -ENOMEM; | ||
115 | fifoch = chan->fifo_priv; | ||
116 | |||
117 | /* allocate vram for control regs, map into polling area */ | ||
118 | ret = nouveau_bo_new(dev, NULL, 0x1000, 0, TTM_PL_FLAG_VRAM, | ||
119 | 0, 0, true, true, &fifoch->user); | ||
120 | if (ret) | ||
121 | goto error; | ||
122 | |||
123 | ret = nouveau_bo_pin(fifoch->user, TTM_PL_FLAG_VRAM); | ||
124 | if (ret) { | ||
125 | nouveau_bo_ref(NULL, &fifoch->user); | ||
126 | goto error; | ||
127 | } | ||
128 | |||
129 | user_vinst = fifoch->user->bo.mem.start << PAGE_SHIFT; | ||
130 | |||
131 | ret = nouveau_bo_map(fifoch->user); | ||
132 | if (ret) { | ||
133 | nouveau_bo_unpin(fifoch->user); | ||
134 | nouveau_bo_ref(NULL, &fifoch->user); | ||
135 | goto error; | ||
136 | } | ||
137 | |||
138 | nouveau_vm_map_at(&priv->user_vma, chan->id * 0x1000, | ||
139 | fifoch->user->bo.mem.mm_node); | ||
140 | |||
141 | chan->user = ioremap_wc(pci_resource_start(dev->pdev, 1) + | ||
142 | priv->user_vma.offset + (chan->id * 0x1000), | ||
143 | PAGE_SIZE); | ||
144 | if (!chan->user) { | ||
145 | ret = -ENOMEM; | ||
146 | goto error; | ||
147 | } | ||
148 | |||
149 | ib_virt = chan->pushbuf_base + chan->dma.ib_base * 4; | ||
150 | |||
151 | /* zero channel regs */ | ||
152 | nouveau_bo_wr32(fifoch->user, 0x0040/4, 0); | ||
153 | nouveau_bo_wr32(fifoch->user, 0x0044/4, 0); | ||
154 | nouveau_bo_wr32(fifoch->user, 0x0048/4, 0); | ||
155 | nouveau_bo_wr32(fifoch->user, 0x004c/4, 0); | ||
156 | nouveau_bo_wr32(fifoch->user, 0x0050/4, 0); | ||
157 | nouveau_bo_wr32(fifoch->user, 0x0058/4, 0); | ||
158 | nouveau_bo_wr32(fifoch->user, 0x005c/4, 0); | ||
159 | nouveau_bo_wr32(fifoch->user, 0x0060/4, 0); | ||
160 | nouveau_bo_wr32(fifoch->user, 0x0088/4, 0); | ||
161 | nouveau_bo_wr32(fifoch->user, 0x008c/4, 0); | ||
162 | |||
163 | /* ramfc */ | ||
164 | ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst, | ||
165 | chan->ramin->vinst, 0x100, | ||
166 | NVOBJ_FLAG_ZERO_ALLOC, &fifoch->ramfc); | ||
167 | if (ret) | ||
168 | goto error; | ||
169 | |||
170 | nv_wo32(fifoch->ramfc, 0x08, lower_32_bits(user_vinst)); | ||
171 | nv_wo32(fifoch->ramfc, 0x0c, upper_32_bits(user_vinst)); | ||
172 | nv_wo32(fifoch->ramfc, 0x10, 0x0000face); | ||
173 | nv_wo32(fifoch->ramfc, 0x30, 0xfffff902); | ||
174 | nv_wo32(fifoch->ramfc, 0x48, lower_32_bits(ib_virt)); | ||
175 | nv_wo32(fifoch->ramfc, 0x4c, drm_order(chan->dma.ib_max + 1) << 16 | | ||
176 | upper_32_bits(ib_virt)); | ||
177 | nv_wo32(fifoch->ramfc, 0x54, 0x00000002); | ||
178 | nv_wo32(fifoch->ramfc, 0x84, 0x20400000); | ||
179 | nv_wo32(fifoch->ramfc, 0x94, 0x30000001); | ||
180 | nv_wo32(fifoch->ramfc, 0x9c, 0x00000100); | ||
181 | nv_wo32(fifoch->ramfc, 0xa4, 0x1f1f1f1f); | ||
182 | nv_wo32(fifoch->ramfc, 0xa8, 0x1f1f1f1f); | ||
183 | nv_wo32(fifoch->ramfc, 0xac, 0x0000001f); | ||
184 | nv_wo32(fifoch->ramfc, 0xb8, 0xf8000000); | ||
185 | nv_wo32(fifoch->ramfc, 0xf8, 0x10003080); /* 0x002310 */ | ||
186 | nv_wo32(fifoch->ramfc, 0xfc, 0x10000010); /* 0x002350 */ | ||
187 | pinstmem->flush(dev); | ||
188 | |||
189 | nv_wr32(dev, 0x003000 + (chan->id * 8), 0xc0000000 | | ||
190 | (chan->ramin->vinst >> 12)); | ||
191 | nv_wr32(dev, 0x003004 + (chan->id * 8), 0x001f0001); | ||
192 | nvc0_fifo_playlist_update(dev); | ||
60 | return 0; | 193 | return 0; |
194 | |||
195 | error: | ||
196 | pfifo->destroy_context(chan); | ||
197 | return ret; | ||
61 | } | 198 | } |
62 | 199 | ||
63 | void | 200 | void |
64 | nvc0_fifo_destroy_context(struct nouveau_channel *chan) | 201 | nvc0_fifo_destroy_context(struct nouveau_channel *chan) |
65 | { | 202 | { |
203 | struct drm_device *dev = chan->dev; | ||
204 | struct nvc0_fifo_chan *fifoch; | ||
205 | |||
206 | nv_mask(dev, 0x003004 + (chan->id * 8), 0x00000001, 0x00000000); | ||
207 | nv_wr32(dev, 0x002634, chan->id); | ||
208 | if (!nv_wait(dev, 0x0002634, 0xffffffff, chan->id)) | ||
209 | NV_WARN(dev, "0x2634 != chid: 0x%08x\n", nv_rd32(dev, 0x2634)); | ||
210 | |||
211 | nvc0_fifo_playlist_update(dev); | ||
212 | |||
213 | nv_wr32(dev, 0x003000 + (chan->id * 8), 0x00000000); | ||
214 | |||
215 | if (chan->user) { | ||
216 | iounmap(chan->user); | ||
217 | chan->user = NULL; | ||
218 | } | ||
219 | |||
220 | fifoch = chan->fifo_priv; | ||
221 | chan->fifo_priv = NULL; | ||
222 | if (!fifoch) | ||
223 | return; | ||
224 | |||
225 | nouveau_gpuobj_ref(NULL, &fifoch->ramfc); | ||
226 | if (fifoch->user) { | ||
227 | nouveau_bo_unmap(fifoch->user); | ||
228 | nouveau_bo_unpin(fifoch->user); | ||
229 | nouveau_bo_ref(NULL, &fifoch->user); | ||
230 | } | ||
231 | kfree(fifoch); | ||
66 | } | 232 | } |
67 | 233 | ||
68 | int | 234 | int |
@@ -77,14 +243,213 @@ nvc0_fifo_unload_context(struct drm_device *dev) | |||
77 | return 0; | 243 | return 0; |
78 | } | 244 | } |
79 | 245 | ||
246 | static void | ||
247 | nvc0_fifo_destroy(struct drm_device *dev) | ||
248 | { | ||
249 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
250 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
251 | struct nvc0_fifo_priv *priv; | ||
252 | |||
253 | priv = pfifo->priv; | ||
254 | if (!priv) | ||
255 | return; | ||
256 | |||
257 | nouveau_vm_put(&priv->user_vma); | ||
258 | nouveau_gpuobj_ref(NULL, &priv->playlist[1]); | ||
259 | nouveau_gpuobj_ref(NULL, &priv->playlist[0]); | ||
260 | kfree(priv); | ||
261 | } | ||
262 | |||
80 | void | 263 | void |
81 | nvc0_fifo_takedown(struct drm_device *dev) | 264 | nvc0_fifo_takedown(struct drm_device *dev) |
82 | { | 265 | { |
266 | nv_wr32(dev, 0x002140, 0x00000000); | ||
267 | nvc0_fifo_destroy(dev); | ||
268 | } | ||
269 | |||
270 | static int | ||
271 | nvc0_fifo_create(struct drm_device *dev) | ||
272 | { | ||
273 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
274 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
275 | struct nvc0_fifo_priv *priv; | ||
276 | int ret; | ||
277 | |||
278 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
279 | if (!priv) | ||
280 | return -ENOMEM; | ||
281 | pfifo->priv = priv; | ||
282 | |||
283 | ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 0x1000, 0, | ||
284 | &priv->playlist[0]); | ||
285 | if (ret) | ||
286 | goto error; | ||
287 | |||
288 | ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 0x1000, 0, | ||
289 | &priv->playlist[1]); | ||
290 | if (ret) | ||
291 | goto error; | ||
292 | |||
293 | ret = nouveau_vm_get(dev_priv->bar1_vm, pfifo->channels * 0x1000, | ||
294 | 12, NV_MEM_ACCESS_RW, &priv->user_vma); | ||
295 | if (ret) | ||
296 | goto error; | ||
297 | |||
298 | nouveau_irq_register(dev, 8, nvc0_fifo_isr); | ||
299 | NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */ | ||
300 | return 0; | ||
301 | |||
302 | error: | ||
303 | nvc0_fifo_destroy(dev); | ||
304 | return ret; | ||
83 | } | 305 | } |
84 | 306 | ||
85 | int | 307 | int |
86 | nvc0_fifo_init(struct drm_device *dev) | 308 | nvc0_fifo_init(struct drm_device *dev) |
87 | { | 309 | { |
310 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
311 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
312 | struct nvc0_fifo_priv *priv; | ||
313 | int ret, i; | ||
314 | |||
315 | if (!pfifo->priv) { | ||
316 | ret = nvc0_fifo_create(dev); | ||
317 | if (ret) | ||
318 | return ret; | ||
319 | } | ||
320 | priv = pfifo->priv; | ||
321 | |||
322 | /* reset PFIFO, enable all available PSUBFIFO areas */ | ||
323 | nv_mask(dev, 0x000200, 0x00000100, 0x00000000); | ||
324 | nv_mask(dev, 0x000200, 0x00000100, 0x00000100); | ||
325 | nv_wr32(dev, 0x000204, 0xffffffff); | ||
326 | nv_wr32(dev, 0x002204, 0xffffffff); | ||
327 | |||
328 | priv->spoon_nr = hweight32(nv_rd32(dev, 0x002204)); | ||
329 | NV_DEBUG(dev, "PFIFO: %d subfifo(s)\n", priv->spoon_nr); | ||
330 | |||
331 | /* assign engines to subfifos */ | ||
332 | if (priv->spoon_nr >= 3) { | ||
333 | nv_wr32(dev, 0x002208, ~(1 << 0)); /* PGRAPH */ | ||
334 | nv_wr32(dev, 0x00220c, ~(1 << 1)); /* PVP */ | ||
335 | nv_wr32(dev, 0x002210, ~(1 << 1)); /* PPP */ | ||
336 | nv_wr32(dev, 0x002214, ~(1 << 1)); /* PBSP */ | ||
337 | nv_wr32(dev, 0x002218, ~(1 << 2)); /* PCE0 */ | ||
338 | nv_wr32(dev, 0x00221c, ~(1 << 1)); /* PCE1 */ | ||
339 | } | ||
340 | |||
341 | /* PSUBFIFO[n] */ | ||
342 | for (i = 0; i < 3; i++) { | ||
343 | nv_mask(dev, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000); | ||
344 | nv_wr32(dev, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */ | ||
345 | nv_wr32(dev, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTR_EN */ | ||
346 | } | ||
347 | |||
348 | nv_mask(dev, 0x002200, 0x00000001, 0x00000001); | ||
349 | nv_wr32(dev, 0x002254, 0x10000000 | priv->user_vma.offset >> 12); | ||
350 | |||
351 | nv_wr32(dev, 0x002a00, 0xffffffff); /* clears PFIFO.INTR bit 30 */ | ||
352 | nv_wr32(dev, 0x002100, 0xffffffff); | ||
353 | nv_wr32(dev, 0x002140, 0xbfffffff); | ||
88 | return 0; | 354 | return 0; |
89 | } | 355 | } |
90 | 356 | ||
357 | struct nouveau_enum nvc0_fifo_fault_unit[] = { | ||
358 | { 0, "PGRAPH" }, | ||
359 | { 3, "PEEPHOLE" }, | ||
360 | { 4, "BAR1" }, | ||
361 | { 5, "BAR3" }, | ||
362 | { 7, "PFIFO" }, | ||
363 | {} | ||
364 | }; | ||
365 | |||
366 | struct nouveau_enum nvc0_fifo_fault_reason[] = { | ||
367 | { 0, "PT_NOT_PRESENT" }, | ||
368 | { 1, "PT_TOO_SHORT" }, | ||
369 | { 2, "PAGE_NOT_PRESENT" }, | ||
370 | { 3, "VM_LIMIT_EXCEEDED" }, | ||
371 | {} | ||
372 | }; | ||
373 | |||
374 | struct nouveau_bitfield nvc0_fifo_subfifo_intr[] = { | ||
375 | /* { 0x00008000, "" } seen with null ib push */ | ||
376 | { 0x00200000, "ILLEGAL_MTHD" }, | ||
377 | { 0x00800000, "EMPTY_SUBC" }, | ||
378 | {} | ||
379 | }; | ||
380 | |||
381 | static void | ||
382 | nvc0_fifo_isr_vm_fault(struct drm_device *dev, int unit) | ||
383 | { | ||
384 | u32 inst = nv_rd32(dev, 0x2800 + (unit * 0x10)); | ||
385 | u32 valo = nv_rd32(dev, 0x2804 + (unit * 0x10)); | ||
386 | u32 vahi = nv_rd32(dev, 0x2808 + (unit * 0x10)); | ||
387 | u32 stat = nv_rd32(dev, 0x280c + (unit * 0x10)); | ||
388 | |||
389 | NV_INFO(dev, "PFIFO: %s fault at 0x%010llx [", | ||
390 | (stat & 0x00000080) ? "write" : "read", (u64)vahi << 32 | valo); | ||
391 | nouveau_enum_print(nvc0_fifo_fault_reason, stat & 0x0000000f); | ||
392 | printk("] from "); | ||
393 | nouveau_enum_print(nvc0_fifo_fault_unit, unit); | ||
394 | printk(" on channel 0x%010llx\n", (u64)inst << 12); | ||
395 | } | ||
396 | |||
397 | static void | ||
398 | nvc0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit) | ||
399 | { | ||
400 | u32 stat = nv_rd32(dev, 0x040108 + (unit * 0x2000)); | ||
401 | u32 addr = nv_rd32(dev, 0x0400c0 + (unit * 0x2000)); | ||
402 | u32 data = nv_rd32(dev, 0x0400c4 + (unit * 0x2000)); | ||
403 | u32 chid = nv_rd32(dev, 0x040120 + (unit * 0x2000)) & 0x7f; | ||
404 | u32 subc = (addr & 0x00070000); | ||
405 | u32 mthd = (addr & 0x00003ffc); | ||
406 | |||
407 | NV_INFO(dev, "PSUBFIFO %d:", unit); | ||
408 | nouveau_bitfield_print(nvc0_fifo_subfifo_intr, stat); | ||
409 | NV_INFO(dev, "PSUBFIFO %d: ch %d subc %d mthd 0x%04x data 0x%08x\n", | ||
410 | unit, chid, subc, mthd, data); | ||
411 | |||
412 | nv_wr32(dev, 0x0400c0 + (unit * 0x2000), 0x80600008); | ||
413 | nv_wr32(dev, 0x040108 + (unit * 0x2000), stat); | ||
414 | } | ||
415 | |||
416 | static void | ||
417 | nvc0_fifo_isr(struct drm_device *dev) | ||
418 | { | ||
419 | u32 stat = nv_rd32(dev, 0x002100); | ||
420 | |||
421 | if (stat & 0x10000000) { | ||
422 | u32 units = nv_rd32(dev, 0x00259c); | ||
423 | u32 u = units; | ||
424 | |||
425 | while (u) { | ||
426 | int i = ffs(u) - 1; | ||
427 | nvc0_fifo_isr_vm_fault(dev, i); | ||
428 | u &= ~(1 << i); | ||
429 | } | ||
430 | |||
431 | nv_wr32(dev, 0x00259c, units); | ||
432 | stat &= ~0x10000000; | ||
433 | } | ||
434 | |||
435 | if (stat & 0x20000000) { | ||
436 | u32 units = nv_rd32(dev, 0x0025a0); | ||
437 | u32 u = units; | ||
438 | |||
439 | while (u) { | ||
440 | int i = ffs(u) - 1; | ||
441 | nvc0_fifo_isr_subfifo_intr(dev, i); | ||
442 | u &= ~(1 << i); | ||
443 | } | ||
444 | |||
445 | nv_wr32(dev, 0x0025a0, units); | ||
446 | stat &= ~0x20000000; | ||
447 | } | ||
448 | |||
449 | if (stat) { | ||
450 | NV_INFO(dev, "PFIFO: unhandled status 0x%08x\n", stat); | ||
451 | nv_wr32(dev, 0x002100, stat); | ||
452 | } | ||
453 | |||
454 | nv_wr32(dev, 0x2140, 0); | ||
455 | } | ||