drm/nouveau/fifo: turn all fifo modules into engine modules
[deliverable/linux.git] / drivers / gpu / drm / nouveau / nv50_fifo.c
index 12e9e8270f45b2b674fbef3c1b7d368ec8ee27de..55383b85db0b7ea4a0ab22a35421f09d6eb11765 100644 (file)
@@ -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
 #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;
+}
This page took 0.031923 seconds and 5 git commands to generate.