diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2012-05-01 20:48:08 +1000 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2012-05-24 16:56:11 +1000 |
commit | c420b2dc8dc3cdd507214f4df5c5f96f08812cbe (patch) | |
tree | 6dca9f0aba3de22a2bda5fe647d6945d4f4e986e /drivers/gpu/drm/nouveau/nv50_fifo.c | |
parent | a226c32a386bca0426e500954b79e3fd46afc0d9 (diff) |
drm/nouveau/fifo: turn all fifo modules into engine modules
Been tested on each major revision that's relevant here, but I'm sure there
are still bugs waiting to be ironed out.
This is a *very* invasive change.
There's a couple of pieces left that I don't like much (eg. other engines
using fifo_priv for the channel count), but that's an artefact of there
being a master channel list still. This is changing, slowly.
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv50_fifo.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_fifo.c | 438 |
1 files changed, 180 insertions, 258 deletions
diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c index 12e9e8270f45..55383b85db0b 100644 --- a/drivers/gpu/drm/nouveau/nv50_fifo.c +++ b/drivers/gpu/drm/nouveau/nv50_fifo.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Ben Skeggs. + * Copyright (C) 2012 Ben Skeggs. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining @@ -27,288 +27,135 @@ #include "drmP.h" #include "drm.h" #include "nouveau_drv.h" +#include "nouveau_fifo.h" #include "nouveau_ramht.h" #include "nouveau_vm.h" -static void +struct nv50_fifo_priv { + struct nouveau_fifo_priv base; + struct nouveau_gpuobj *playlist[2]; + int cur_playlist; +}; + +struct nv50_fifo_chan { + struct nouveau_fifo_chan base; +}; + +void nv50_fifo_playlist_update(struct drm_device *dev) { + struct nv50_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO); struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; struct nouveau_gpuobj *cur; int i, p; - NV_DEBUG(dev, "\n"); - - cur = pfifo->playlist[pfifo->cur_playlist]; - pfifo->cur_playlist = !pfifo->cur_playlist; + cur = priv->playlist[priv->cur_playlist]; + priv->cur_playlist = !priv->cur_playlist; - for (i = 0, p = 0; i < pfifo->channels; i++) { + for (i = 0, p = 0; i < priv->base.channels; i++) { if (nv_rd32(dev, 0x002600 + (i * 4)) & 0x80000000) nv_wo32(cur, p++ * 4, i); } dev_priv->engine.instmem.flush(dev); - nv_wr32(dev, 0x32f4, cur->vinst >> 12); - nv_wr32(dev, 0x32ec, p); - nv_wr32(dev, 0x2500, 0x101); -} - -static void -nv50_fifo_channel_enable(struct drm_device *dev, int channel) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_channel *chan = dev_priv->channels.ptr[channel]; - uint32_t inst; - - NV_DEBUG(dev, "ch%d\n", channel); - - if (dev_priv->chipset == 0x50) - inst = chan->ramfc->vinst >> 12; - else - inst = chan->ramfc->vinst >> 8; - - nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel), inst | - NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED); -} - -static void -nv50_fifo_channel_disable(struct drm_device *dev, int channel) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t inst; - - NV_DEBUG(dev, "ch%d\n", channel); - - if (dev_priv->chipset == 0x50) - inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80; - else - inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84; - nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel), inst); -} - -static void -nv50_fifo_init_reset(struct drm_device *dev) -{ - uint32_t pmc_e = NV_PMC_ENABLE_PFIFO; - - NV_DEBUG(dev, "\n"); - - nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e); - nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | pmc_e); -} - -static void -nv50_fifo_init_intr(struct drm_device *dev) -{ - NV_DEBUG(dev, "\n"); - - nouveau_irq_register(dev, 8, nv04_fifo_isr); - nv_wr32(dev, NV03_PFIFO_INTR_0, 0xFFFFFFFF); - nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0xFFFFFFFF); -} - -static void -nv50_fifo_init_context_table(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - int i; - - NV_DEBUG(dev, "\n"); - - for (i = 0; i < NV50_PFIFO_CTX_TABLE__SIZE; i++) { - if (dev_priv->channels.ptr[i]) - nv50_fifo_channel_enable(dev, i); - else - nv50_fifo_channel_disable(dev, i); - } - - nv50_fifo_playlist_update(dev); -} - -static void -nv50_fifo_init_regs__nv(struct drm_device *dev) -{ - NV_DEBUG(dev, "\n"); - - nv_wr32(dev, 0x250c, 0x6f3cfc34); -} - -static void -nv50_fifo_init_regs(struct drm_device *dev) -{ - NV_DEBUG(dev, "\n"); - - nv_wr32(dev, 0x2500, 0); - nv_wr32(dev, 0x3250, 0); - nv_wr32(dev, 0x3220, 0); - nv_wr32(dev, 0x3204, 0); - nv_wr32(dev, 0x3210, 0); - nv_wr32(dev, 0x3270, 0); - nv_wr32(dev, 0x2044, 0x01003fff); - - /* Enable dummy channels setup by nv50_instmem.c */ - nv50_fifo_channel_enable(dev, 0); - nv50_fifo_channel_enable(dev, 127); -} - -int -nv50_fifo_init(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; - int ret; - - NV_DEBUG(dev, "\n"); - - if (pfifo->playlist[0]) { - pfifo->cur_playlist = !pfifo->cur_playlist; - goto just_reset; - } - - ret = nouveau_gpuobj_new(dev, NULL, 128*4, 0x1000, - NVOBJ_FLAG_ZERO_ALLOC, - &pfifo->playlist[0]); - if (ret) { - NV_ERROR(dev, "error creating playlist 0: %d\n", ret); - return ret; - } - - ret = nouveau_gpuobj_new(dev, NULL, 128*4, 0x1000, - NVOBJ_FLAG_ZERO_ALLOC, - &pfifo->playlist[1]); - if (ret) { - nouveau_gpuobj_ref(NULL, &pfifo->playlist[0]); - NV_ERROR(dev, "error creating playlist 1: %d\n", ret); - return ret; - } - -just_reset: - nv50_fifo_init_reset(dev); - nv50_fifo_init_intr(dev); - nv50_fifo_init_context_table(dev); - nv50_fifo_init_regs__nv(dev); - nv50_fifo_init_regs(dev); - nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1); - nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1); - nv_wr32(dev, NV03_PFIFO_CACHES, 1); - - return 0; + nv_wr32(dev, 0x0032f4, cur->vinst >> 12); + nv_wr32(dev, 0x0032ec, p); + nv_wr32(dev, 0x002500, 0x00000101); } -void -nv50_fifo_takedown(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; - - NV_DEBUG(dev, "\n"); - - if (!pfifo->playlist[0]) - return; - - nv_wr32(dev, 0x2140, 0x00000000); - nouveau_irq_unregister(dev, 8); - - nouveau_gpuobj_ref(NULL, &pfifo->playlist[0]); - nouveau_gpuobj_ref(NULL, &pfifo->playlist[1]); -} - -int -nv50_fifo_create_context(struct nouveau_channel *chan) +static int +nv50_fifo_context_new(struct nouveau_channel *chan, int engine) { + struct nv50_fifo_priv *priv = nv_engine(chan->dev, engine); + struct nv50_fifo_chan *fctx; struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpuobj *ramfc = NULL; - uint64_t ib_offset = chan->pushbuf_base + chan->dma.ib_base * 4; + u64 ib_offset = chan->pushbuf_base + chan->dma.ib_base * 4; + u64 instance = chan->ramin->vinst >> 12; unsigned long flags; - int ret; + int ret = 0, i; - NV_DEBUG(dev, "ch%d\n", chan->id); - - if (dev_priv->chipset == 0x50) { - ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst, - chan->ramin->vinst, 0x100, - NVOBJ_FLAG_ZERO_ALLOC | - NVOBJ_FLAG_ZERO_FREE, - &chan->ramfc); - if (ret) - return ret; - - ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst + 0x0400, - chan->ramin->vinst + 0x0400, - 4096, 0, &chan->cache); - if (ret) - return ret; - } else { - ret = nouveau_gpuobj_new(dev, chan, 0x100, 256, - NVOBJ_FLAG_ZERO_ALLOC | - NVOBJ_FLAG_ZERO_FREE, &chan->ramfc); - if (ret) - return ret; - - ret = nouveau_gpuobj_new(dev, chan, 4096, 1024, - 0, &chan->cache); - if (ret) - return ret; - } - ramfc = chan->ramfc; + fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + atomic_inc(&chan->vm->engref[engine]); chan->user = ioremap(pci_resource_start(dev->pdev, 0) + NV50_USER(chan->id), PAGE_SIZE); - if (!chan->user) - return -ENOMEM; - - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - - nv_wo32(ramfc, 0x48, chan->pushbuf->cinst >> 4); - nv_wo32(ramfc, 0x80, ((chan->ramht->bits - 9) << 27) | - (4 << 24) /* SEARCH_FULL */ | - (chan->ramht->gpuobj->cinst >> 4)); - nv_wo32(ramfc, 0x44, 0x01003fff); - nv_wo32(ramfc, 0x60, 0x7fffffff); - nv_wo32(ramfc, 0x40, 0x00000000); - nv_wo32(ramfc, 0x7c, 0x30000001); - nv_wo32(ramfc, 0x78, 0x00000000); - nv_wo32(ramfc, 0x3c, 0x403f6078); - nv_wo32(ramfc, 0x50, lower_32_bits(ib_offset)); - nv_wo32(ramfc, 0x54, upper_32_bits(ib_offset) | - drm_order(chan->dma.ib_max + 1) << 16); - - if (dev_priv->chipset != 0x50) { - nv_wo32(chan->ramin, 0, chan->id); - nv_wo32(chan->ramin, 4, chan->ramfc->vinst >> 8); - - nv_wo32(ramfc, 0x88, chan->cache->vinst >> 10); - nv_wo32(ramfc, 0x98, chan->ramin->vinst >> 12); + if (!chan->user) { + ret = -ENOMEM; + goto error; } + for (i = 0; i < 0x100; i += 4) + nv_wo32(chan->ramin, i, 0x00000000); + nv_wo32(chan->ramin, 0x3c, 0x403f6078); + nv_wo32(chan->ramin, 0x40, 0x00000000); + nv_wo32(chan->ramin, 0x44, 0x01003fff); + nv_wo32(chan->ramin, 0x48, chan->pushbuf->cinst >> 4); + nv_wo32(chan->ramin, 0x50, lower_32_bits(ib_offset)); + nv_wo32(chan->ramin, 0x54, upper_32_bits(ib_offset) | + drm_order(chan->dma.ib_max + 1) << 16); + nv_wo32(chan->ramin, 0x60, 0x7fffffff); + nv_wo32(chan->ramin, 0x78, 0x00000000); + nv_wo32(chan->ramin, 0x7c, 0x30000001); + nv_wo32(chan->ramin, 0x80, ((chan->ramht->bits - 9) << 27) | + (4 << 24) /* SEARCH_FULL */ | + (chan->ramht->gpuobj->cinst >> 4)); + dev_priv->engine.instmem.flush(dev); - nv50_fifo_channel_enable(dev, chan->id); + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_wr32(dev, 0x002600 + (chan->id * 4), 0x80000000 | instance); nv50_fifo_playlist_update(dev); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - return 0; + +error: + if (ret) + priv->base.base.context_del(chan, engine); + return ret; } static bool -nv50_fifo_wait_kickoff(void *data) +nv50_fifo_kickoff(struct nouveau_channel *chan) { - struct drm_nouveau_private *dev_priv = data; - struct drm_device *dev = dev_priv->dev; - - if (dev_priv->chipset == 0x50) { - u32 me_enable = nv_mask(dev, 0x00b860, 0x00000001, 0x00000001); - nv_wr32(dev, 0x00b860, me_enable); + struct drm_device *dev = chan->dev; + bool done = true; + u32 me; + + /* HW bug workaround: + * + * PFIFO will hang forever if the connected engines don't report + * that they've processed the context switch request. + * + * In order for the kickoff to work, we need to ensure all the + * connected engines are in a state where they can answer. + * + * Newer chipsets don't seem to suffer from this issue, and well, + * there's also a "ignore these engines" bitmask reg we can use + * if we hit the issue there.. + */ + + /* PME: make sure engine is enabled */ + me = nv_mask(dev, 0x00b860, 0x00000001, 0x00000001); + + /* do the kickoff... */ + nv_wr32(dev, 0x0032fc, chan->ramin->vinst >> 12); + if (!nv_wait_ne(dev, 0x0032fc, 0xffffffff, 0xffffffff)) { + NV_INFO(dev, "PFIFO: channel %d unload timeout\n", chan->id); + done = false; } - return nv_rd32(dev, 0x0032fc) != 0xffffffff; + /* restore any engine states we changed, and exit */ + nv_wr32(dev, 0x00b860, me); + return done; } -void -nv50_fifo_destroy_context(struct nouveau_channel *chan) +static void +nv50_fifo_context_del(struct nouveau_channel *chan, int engine) { + struct nv50_fifo_chan *fctx = chan->engctx[engine]; struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; unsigned long flags; @@ -319,9 +166,7 @@ nv50_fifo_destroy_context(struct nouveau_channel *chan) nv50_fifo_playlist_update(dev); /* tell any engines on this channel to unload their contexts */ - nv_wr32(dev, 0x0032fc, chan->ramin->vinst >> 12); - if (!nv_wait_cb(dev, nv50_fifo_wait_kickoff, dev_priv)) - NV_INFO(dev, "PFIFO: channel %d unload timeout\n", chan->id); + nv50_fifo_kickoff(chan); nv_wr32(dev, 0x002600 + (chan->id * 4), 0x00000000); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); @@ -332,41 +177,118 @@ nv50_fifo_destroy_context(struct nouveau_channel *chan) chan->user = NULL; } - nouveau_gpuobj_ref(NULL, &chan->ramfc); - nouveau_gpuobj_ref(NULL, &chan->cache); + atomic_dec(&chan->vm->engref[engine]); + chan->engctx[engine] = NULL; + kfree(fctx); } -int -nv50_fifo_load_context(struct nouveau_channel *chan) +static int +nv50_fifo_init(struct drm_device *dev, int engine) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 instance; + int i; + + nv_mask(dev, 0x000200, 0x00000100, 0x00000000); + nv_mask(dev, 0x000200, 0x00000100, 0x00000100); + nv_wr32(dev, 0x00250c, 0x6f3cfc34); + nv_wr32(dev, 0x002044, 0x01003fff); + + nv_wr32(dev, 0x002100, 0xffffffff); + nv_wr32(dev, 0x002140, 0xffffffff); + + for (i = 0; i < 128; i++) { + struct nouveau_channel *chan = dev_priv->channels.ptr[i]; + if (chan && chan->engctx[engine]) + instance = 0x80000000 | chan->ramin->vinst >> 12; + else + instance = 0x00000000; + nv_wr32(dev, 0x002600 + (i * 4), instance); + } + + nv50_fifo_playlist_update(dev); + + nv_wr32(dev, 0x003200, 1); + nv_wr32(dev, 0x003250, 1); + nv_wr32(dev, 0x002500, 1); return 0; } -int -nv50_fifo_unload_context(struct drm_device *dev) +static int +nv50_fifo_fini(struct drm_device *dev, int engine, bool suspend) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_fifo_priv *priv = nv_engine(dev, engine); int i; /* set playlist length to zero, fifo will unload context */ nv_wr32(dev, 0x0032ec, 0); /* tell all connected engines to unload their contexts */ - for (i = 0; i < dev_priv->engine.fifo.channels; i++) { + for (i = 0; i < priv->base.channels; i++) { struct nouveau_channel *chan = dev_priv->channels.ptr[i]; - if (chan) - nv_wr32(dev, 0x0032fc, chan->ramin->vinst >> 12); - if (!nv_wait_cb(dev, nv50_fifo_wait_kickoff, dev_priv)) { - NV_INFO(dev, "PFIFO: channel %d unload timeout\n", i); + if (chan && !nv50_fifo_kickoff(chan)) return -EBUSY; - } } + nv_wr32(dev, 0x002140, 0); return 0; } void -nv50_fifo_tlb_flush(struct drm_device *dev) +nv50_fifo_tlb_flush(struct drm_device *dev, int engine) { nv50_vm_flush_engine(dev, 5); } + +void +nv50_fifo_destroy(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_fifo_priv *priv = nv_engine(dev, engine); + + nouveau_irq_unregister(dev, 8); + + nouveau_gpuobj_ref(NULL, &priv->playlist[0]); + nouveau_gpuobj_ref(NULL, &priv->playlist[1]); + + dev_priv->eng[engine] = NULL; + kfree(priv); +} + +int +nv50_fifo_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv50_fifo_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.base.destroy = nv50_fifo_destroy; + priv->base.base.init = nv50_fifo_init; + priv->base.base.fini = nv50_fifo_fini; + priv->base.base.context_new = nv50_fifo_context_new; + priv->base.base.context_del = nv50_fifo_context_del; + priv->base.base.tlb_flush = nv50_fifo_tlb_flush; + priv->base.channels = 127; + dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base; + + ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 4, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &priv->playlist[0]); + if (ret) + goto error; + + ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 4, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &priv->playlist[1]); + if (ret) + goto error; + + nouveau_irq_register(dev, 8, nv04_fifo_isr); +error: + if (ret) + priv->base.base.destroy(dev, NVOBJ_ENGINE_FIFO); + return ret; +} |