diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv04_fifo.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv04_fifo.c | 240 |
1 files changed, 236 insertions, 4 deletions
diff --git a/drivers/gpu/drm/nouveau/nv04_fifo.c b/drivers/gpu/drm/nouveau/nv04_fifo.c index 708293b7ddcd..f89d104698df 100644 --- a/drivers/gpu/drm/nouveau/nv04_fifo.c +++ b/drivers/gpu/drm/nouveau/nv04_fifo.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include "drm.h" | 28 | #include "drm.h" |
29 | #include "nouveau_drv.h" | 29 | #include "nouveau_drv.h" |
30 | #include "nouveau_ramht.h" | 30 | #include "nouveau_ramht.h" |
31 | #include "nouveau_util.h" | ||
31 | 32 | ||
32 | #define NV04_RAMFC(c) (dev_priv->ramfc->pinst + ((c) * NV04_RAMFC__SIZE)) | 33 | #define NV04_RAMFC(c) (dev_priv->ramfc->pinst + ((c) * NV04_RAMFC__SIZE)) |
33 | #define NV04_RAMFC__SIZE 32 | 34 | #define NV04_RAMFC__SIZE 32 |
@@ -128,6 +129,11 @@ nv04_fifo_create_context(struct nouveau_channel *chan) | |||
128 | if (ret) | 129 | if (ret) |
129 | return ret; | 130 | return ret; |
130 | 131 | ||
132 | chan->user = ioremap(pci_resource_start(dev->pdev, 0) + | ||
133 | NV03_USER(chan->id), PAGE_SIZE); | ||
134 | if (!chan->user) | ||
135 | return -ENOMEM; | ||
136 | |||
131 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); | 137 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); |
132 | 138 | ||
133 | /* Setup initial state */ | 139 | /* Setup initial state */ |
@@ -151,10 +157,31 @@ void | |||
151 | nv04_fifo_destroy_context(struct nouveau_channel *chan) | 157 | nv04_fifo_destroy_context(struct nouveau_channel *chan) |
152 | { | 158 | { |
153 | struct drm_device *dev = chan->dev; | 159 | struct drm_device *dev = chan->dev; |
160 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
161 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
162 | unsigned long flags; | ||
154 | 163 | ||
155 | nv_wr32(dev, NV04_PFIFO_MODE, | 164 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); |
156 | nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id)); | 165 | pfifo->reassign(dev, false); |
157 | 166 | ||
167 | /* Unload the context if it's the currently active one */ | ||
168 | if (pfifo->channel_id(dev) == chan->id) { | ||
169 | pfifo->disable(dev); | ||
170 | pfifo->unload_context(dev); | ||
171 | pfifo->enable(dev); | ||
172 | } | ||
173 | |||
174 | /* Keep it from being rescheduled */ | ||
175 | nv_mask(dev, NV04_PFIFO_MODE, 1 << chan->id, 0); | ||
176 | |||
177 | pfifo->reassign(dev, true); | ||
178 | spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); | ||
179 | |||
180 | /* Free the channel resources */ | ||
181 | if (chan->user) { | ||
182 | iounmap(chan->user); | ||
183 | chan->user = NULL; | ||
184 | } | ||
158 | nouveau_gpuobj_ref(NULL, &chan->ramfc); | 185 | nouveau_gpuobj_ref(NULL, &chan->ramfc); |
159 | } | 186 | } |
160 | 187 | ||
@@ -208,7 +235,7 @@ nv04_fifo_unload_context(struct drm_device *dev) | |||
208 | if (chid < 0 || chid >= dev_priv->engine.fifo.channels) | 235 | if (chid < 0 || chid >= dev_priv->engine.fifo.channels) |
209 | return 0; | 236 | return 0; |
210 | 237 | ||
211 | chan = dev_priv->fifos[chid]; | 238 | chan = dev_priv->channels.ptr[chid]; |
212 | if (!chan) { | 239 | if (!chan) { |
213 | NV_ERROR(dev, "Inactive channel on PFIFO: %d\n", chid); | 240 | NV_ERROR(dev, "Inactive channel on PFIFO: %d\n", chid); |
214 | return -EINVAL; | 241 | return -EINVAL; |
@@ -267,6 +294,7 @@ nv04_fifo_init_ramxx(struct drm_device *dev) | |||
267 | static void | 294 | static void |
268 | nv04_fifo_init_intr(struct drm_device *dev) | 295 | nv04_fifo_init_intr(struct drm_device *dev) |
269 | { | 296 | { |
297 | nouveau_irq_register(dev, 8, nv04_fifo_isr); | ||
270 | nv_wr32(dev, 0x002100, 0xffffffff); | 298 | nv_wr32(dev, 0x002100, 0xffffffff); |
271 | nv_wr32(dev, 0x002140, 0xffffffff); | 299 | nv_wr32(dev, 0x002140, 0xffffffff); |
272 | } | 300 | } |
@@ -289,7 +317,7 @@ nv04_fifo_init(struct drm_device *dev) | |||
289 | pfifo->reassign(dev, true); | 317 | pfifo->reassign(dev, true); |
290 | 318 | ||
291 | for (i = 0; i < dev_priv->engine.fifo.channels; i++) { | 319 | for (i = 0; i < dev_priv->engine.fifo.channels; i++) { |
292 | if (dev_priv->fifos[i]) { | 320 | if (dev_priv->channels.ptr[i]) { |
293 | uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE); | 321 | uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE); |
294 | nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i)); | 322 | nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i)); |
295 | } | 323 | } |
@@ -298,3 +326,207 @@ nv04_fifo_init(struct drm_device *dev) | |||
298 | return 0; | 326 | return 0; |
299 | } | 327 | } |
300 | 328 | ||
329 | void | ||
330 | nv04_fifo_fini(struct drm_device *dev) | ||
331 | { | ||
332 | nv_wr32(dev, 0x2140, 0x00000000); | ||
333 | nouveau_irq_unregister(dev, 8); | ||
334 | } | ||
335 | |||
336 | static bool | ||
337 | nouveau_fifo_swmthd(struct drm_device *dev, u32 chid, u32 addr, u32 data) | ||
338 | { | ||
339 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
340 | struct nouveau_channel *chan = NULL; | ||
341 | struct nouveau_gpuobj *obj; | ||
342 | unsigned long flags; | ||
343 | const int subc = (addr >> 13) & 0x7; | ||
344 | const int mthd = addr & 0x1ffc; | ||
345 | bool handled = false; | ||
346 | u32 engine; | ||
347 | |||
348 | spin_lock_irqsave(&dev_priv->channels.lock, flags); | ||
349 | if (likely(chid >= 0 && chid < dev_priv->engine.fifo.channels)) | ||
350 | chan = dev_priv->channels.ptr[chid]; | ||
351 | if (unlikely(!chan)) | ||
352 | goto out; | ||
353 | |||
354 | switch (mthd) { | ||
355 | case 0x0000: /* bind object to subchannel */ | ||
356 | obj = nouveau_ramht_find(chan, data); | ||
357 | if (unlikely(!obj || obj->engine != NVOBJ_ENGINE_SW)) | ||
358 | break; | ||
359 | |||
360 | chan->sw_subchannel[subc] = obj->class; | ||
361 | engine = 0x0000000f << (subc * 4); | ||
362 | |||
363 | nv_mask(dev, NV04_PFIFO_CACHE1_ENGINE, engine, 0x00000000); | ||
364 | handled = true; | ||
365 | break; | ||
366 | default: | ||
367 | engine = nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE); | ||
368 | if (unlikely(((engine >> (subc * 4)) & 0xf) != 0)) | ||
369 | break; | ||
370 | |||
371 | if (!nouveau_gpuobj_mthd_call(chan, chan->sw_subchannel[subc], | ||
372 | mthd, data)) | ||
373 | handled = true; | ||
374 | break; | ||
375 | } | ||
376 | |||
377 | out: | ||
378 | spin_unlock_irqrestore(&dev_priv->channels.lock, flags); | ||
379 | return handled; | ||
380 | } | ||
381 | |||
382 | void | ||
383 | nv04_fifo_isr(struct drm_device *dev) | ||
384 | { | ||
385 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
386 | struct nouveau_engine *engine = &dev_priv->engine; | ||
387 | uint32_t status, reassign; | ||
388 | int cnt = 0; | ||
389 | |||
390 | reassign = nv_rd32(dev, NV03_PFIFO_CACHES) & 1; | ||
391 | while ((status = nv_rd32(dev, NV03_PFIFO_INTR_0)) && (cnt++ < 100)) { | ||
392 | uint32_t chid, get; | ||
393 | |||
394 | nv_wr32(dev, NV03_PFIFO_CACHES, 0); | ||
395 | |||
396 | chid = engine->fifo.channel_id(dev); | ||
397 | get = nv_rd32(dev, NV03_PFIFO_CACHE1_GET); | ||
398 | |||
399 | if (status & NV_PFIFO_INTR_CACHE_ERROR) { | ||
400 | uint32_t mthd, data; | ||
401 | int ptr; | ||
402 | |||
403 | /* NV_PFIFO_CACHE1_GET actually goes to 0xffc before | ||
404 | * wrapping on my G80 chips, but CACHE1 isn't big | ||
405 | * enough for this much data.. Tests show that it | ||
406 | * wraps around to the start at GET=0x800.. No clue | ||
407 | * as to why.. | ||
408 | */ | ||
409 | ptr = (get & 0x7ff) >> 2; | ||
410 | |||
411 | if (dev_priv->card_type < NV_40) { | ||
412 | mthd = nv_rd32(dev, | ||
413 | NV04_PFIFO_CACHE1_METHOD(ptr)); | ||
414 | data = nv_rd32(dev, | ||
415 | NV04_PFIFO_CACHE1_DATA(ptr)); | ||
416 | } else { | ||
417 | mthd = nv_rd32(dev, | ||
418 | NV40_PFIFO_CACHE1_METHOD(ptr)); | ||
419 | data = nv_rd32(dev, | ||
420 | NV40_PFIFO_CACHE1_DATA(ptr)); | ||
421 | } | ||
422 | |||
423 | if (!nouveau_fifo_swmthd(dev, chid, mthd, data)) { | ||
424 | NV_INFO(dev, "PFIFO_CACHE_ERROR - Ch %d/%d " | ||
425 | "Mthd 0x%04x Data 0x%08x\n", | ||
426 | chid, (mthd >> 13) & 7, mthd & 0x1ffc, | ||
427 | data); | ||
428 | } | ||
429 | |||
430 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0); | ||
431 | nv_wr32(dev, NV03_PFIFO_INTR_0, | ||
432 | NV_PFIFO_INTR_CACHE_ERROR); | ||
433 | |||
434 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, | ||
435 | nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) & ~1); | ||
436 | nv_wr32(dev, NV03_PFIFO_CACHE1_GET, get + 4); | ||
437 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, | ||
438 | nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) | 1); | ||
439 | nv_wr32(dev, NV04_PFIFO_CACHE1_HASH, 0); | ||
440 | |||
441 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, | ||
442 | nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH) | 1); | ||
443 | nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1); | ||
444 | |||
445 | status &= ~NV_PFIFO_INTR_CACHE_ERROR; | ||
446 | } | ||
447 | |||
448 | if (status & NV_PFIFO_INTR_DMA_PUSHER) { | ||
449 | u32 dma_get = nv_rd32(dev, 0x003244); | ||
450 | u32 dma_put = nv_rd32(dev, 0x003240); | ||
451 | u32 push = nv_rd32(dev, 0x003220); | ||
452 | u32 state = nv_rd32(dev, 0x003228); | ||
453 | |||
454 | if (dev_priv->card_type == NV_50) { | ||
455 | u32 ho_get = nv_rd32(dev, 0x003328); | ||
456 | u32 ho_put = nv_rd32(dev, 0x003320); | ||
457 | u32 ib_get = nv_rd32(dev, 0x003334); | ||
458 | u32 ib_put = nv_rd32(dev, 0x003330); | ||
459 | |||
460 | if (nouveau_ratelimit()) | ||
461 | NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d Get 0x%02x%08x " | ||
462 | "Put 0x%02x%08x IbGet 0x%08x IbPut 0x%08x " | ||
463 | "State 0x%08x Push 0x%08x\n", | ||
464 | chid, ho_get, dma_get, ho_put, | ||
465 | dma_put, ib_get, ib_put, state, | ||
466 | push); | ||
467 | |||
468 | /* METHOD_COUNT, in DMA_STATE on earlier chipsets */ | ||
469 | nv_wr32(dev, 0x003364, 0x00000000); | ||
470 | if (dma_get != dma_put || ho_get != ho_put) { | ||
471 | nv_wr32(dev, 0x003244, dma_put); | ||
472 | nv_wr32(dev, 0x003328, ho_put); | ||
473 | } else | ||
474 | if (ib_get != ib_put) { | ||
475 | nv_wr32(dev, 0x003334, ib_put); | ||
476 | } | ||
477 | } else { | ||
478 | NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d Get 0x%08x " | ||
479 | "Put 0x%08x State 0x%08x Push 0x%08x\n", | ||
480 | chid, dma_get, dma_put, state, push); | ||
481 | |||
482 | if (dma_get != dma_put) | ||
483 | nv_wr32(dev, 0x003244, dma_put); | ||
484 | } | ||
485 | |||
486 | nv_wr32(dev, 0x003228, 0x00000000); | ||
487 | nv_wr32(dev, 0x003220, 0x00000001); | ||
488 | nv_wr32(dev, 0x002100, NV_PFIFO_INTR_DMA_PUSHER); | ||
489 | status &= ~NV_PFIFO_INTR_DMA_PUSHER; | ||
490 | } | ||
491 | |||
492 | if (status & NV_PFIFO_INTR_SEMAPHORE) { | ||
493 | uint32_t sem; | ||
494 | |||
495 | status &= ~NV_PFIFO_INTR_SEMAPHORE; | ||
496 | nv_wr32(dev, NV03_PFIFO_INTR_0, | ||
497 | NV_PFIFO_INTR_SEMAPHORE); | ||
498 | |||
499 | sem = nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE); | ||
500 | nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, sem | 0x1); | ||
501 | |||
502 | nv_wr32(dev, NV03_PFIFO_CACHE1_GET, get + 4); | ||
503 | nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1); | ||
504 | } | ||
505 | |||
506 | if (dev_priv->card_type == NV_50) { | ||
507 | if (status & 0x00000010) { | ||
508 | nv50_fb_vm_trap(dev, 1, "PFIFO_BAR_FAULT"); | ||
509 | status &= ~0x00000010; | ||
510 | nv_wr32(dev, 0x002100, 0x00000010); | ||
511 | } | ||
512 | } | ||
513 | |||
514 | if (status) { | ||
515 | if (nouveau_ratelimit()) | ||
516 | NV_INFO(dev, "PFIFO_INTR 0x%08x - Ch %d\n", | ||
517 | status, chid); | ||
518 | nv_wr32(dev, NV03_PFIFO_INTR_0, status); | ||
519 | status = 0; | ||
520 | } | ||
521 | |||
522 | nv_wr32(dev, NV03_PFIFO_CACHES, reassign); | ||
523 | } | ||
524 | |||
525 | if (status) { | ||
526 | NV_INFO(dev, "PFIFO still angry after %d spins, halt\n", cnt); | ||
527 | nv_wr32(dev, 0x2140, 0); | ||
528 | nv_wr32(dev, 0x140, 0); | ||
529 | } | ||
530 | |||
531 | nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PFIFO_PENDING); | ||
532 | } | ||