Merge branch 'for-linus' of git://git.kernel.dk/linux-block
[pandora-kernel.git] / drivers / gpu / drm / nouveau / nouveau_channel.c
index a7583a8..b0d753f 100644 (file)
 #include "nouveau_drv.h"
 #include "nouveau_drm.h"
 #include "nouveau_dma.h"
+#include "nouveau_ramht.h"
 
 static int
-nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
+nouveau_channel_pushbuf_init(struct nouveau_channel *chan)
 {
+       u32 mem = nouveau_vram_pushbuf ? TTM_PL_FLAG_VRAM : TTM_PL_FLAG_TT;
        struct drm_device *dev = chan->dev;
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nouveau_bo *pb = chan->pushbuf_bo;
-       struct nouveau_gpuobj *pushbuf = NULL;
-       int ret = 0;
+       int ret;
+
+       /* allocate buffer object */
+       ret = nouveau_bo_new(dev, 65536, 0, mem, 0, 0, &chan->pushbuf_bo);
+       if (ret)
+               goto out;
+
+       ret = nouveau_bo_pin(chan->pushbuf_bo, mem);
+       if (ret)
+               goto out;
+
+       ret = nouveau_bo_map(chan->pushbuf_bo);
+       if (ret)
+               goto out;
 
+       /* create DMA object covering the entire memtype where the push
+        * buffer resides, userspace can submit its own push buffers from
+        * anywhere within the same memtype.
+        */
+       chan->pushbuf_base = chan->pushbuf_bo->bo.offset;
        if (dev_priv->card_type >= NV_50) {
+               ret = nouveau_bo_vma_add(chan->pushbuf_bo, chan->vm,
+                                        &chan->pushbuf_vma);
+               if (ret)
+                       goto out;
+
                if (dev_priv->card_type < NV_C0) {
                        ret = nouveau_gpuobj_dma_new(chan,
                                                     NV_CLASS_DMA_IN_MEMORY, 0,
                                                     (1ULL << 40),
                                                     NV_MEM_ACCESS_RO,
                                                     NV_MEM_TARGET_VM,
-                                                    &pushbuf);
+                                                    &chan->pushbuf);
                }
-               chan->pushbuf_base = pb->bo.offset;
+               chan->pushbuf_base = chan->pushbuf_vma.offset;
        } else
-       if (pb->bo.mem.mem_type == TTM_PL_TT) {
+       if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_TT) {
                ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
                                             dev_priv->gart_info.aper_size,
                                             NV_MEM_ACCESS_RO,
-                                            NV_MEM_TARGET_GART, &pushbuf);
-               chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT;
+                                            NV_MEM_TARGET_GART,
+                                            &chan->pushbuf);
        } else
        if (dev_priv->card_type != NV_04) {
                ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
                                             dev_priv->fb_available_size,
                                             NV_MEM_ACCESS_RO,
-                                            NV_MEM_TARGET_VRAM, &pushbuf);
-               chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT;
+                                            NV_MEM_TARGET_VRAM,
+                                            &chan->pushbuf);
        } else {
                /* NV04 cmdbuf hack, from original ddx.. not sure of it's
                 * exact reason for existing :)  PCI access to cmdbuf in
@@ -70,47 +93,22 @@ nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
                                             pci_resource_start(dev->pdev, 1),
                                             dev_priv->fb_available_size,
                                             NV_MEM_ACCESS_RO,
-                                            NV_MEM_TARGET_PCI, &pushbuf);
-               chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT;
+                                            NV_MEM_TARGET_PCI,
+                                            &chan->pushbuf);
        }
 
-       nouveau_gpuobj_ref(pushbuf, &chan->pushbuf);
-       nouveau_gpuobj_ref(NULL, &pushbuf);
-       return ret;
-}
-
-static struct nouveau_bo *
-nouveau_channel_user_pushbuf_alloc(struct drm_device *dev)
-{
-       struct nouveau_bo *pushbuf = NULL;
-       int location, ret;
-
-       if (nouveau_vram_pushbuf)
-               location = TTM_PL_FLAG_VRAM;
-       else
-               location = TTM_PL_FLAG_TT;
-
-       ret = nouveau_bo_new(dev, NULL, 65536, 0, location, 0, 0x0000, &pushbuf);
-       if (ret) {
-               NV_ERROR(dev, "error allocating DMA push buffer: %d\n", ret);
-               return NULL;
-       }
-
-       ret = nouveau_bo_pin(pushbuf, location);
-       if (ret) {
-               NV_ERROR(dev, "error pinning DMA push buffer: %d\n", ret);
-               nouveau_bo_ref(NULL, &pushbuf);
-               return NULL;
-       }
-
-       ret = nouveau_bo_map(pushbuf);
+out:
        if (ret) {
-               nouveau_bo_unpin(pushbuf);
-               nouveau_bo_ref(NULL, &pushbuf);
-               return NULL;
+               NV_ERROR(dev, "error initialising pushbuf: %d\n", ret);
+               nouveau_bo_vma_del(chan->pushbuf_bo, &chan->pushbuf_vma);
+               nouveau_gpuobj_ref(NULL, &chan->pushbuf);
+               if (chan->pushbuf_bo) {
+                       nouveau_bo_unmap(chan->pushbuf_bo);
+                       nouveau_bo_ref(NULL, &chan->pushbuf_bo);
+               }
        }
 
-       return pushbuf;
+       return 0;
 }
 
 /* allocates and initializes a fifo for user space consumption */
@@ -121,6 +119,7 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+       struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
        struct nouveau_channel *chan;
        unsigned long flags;
        int ret;
@@ -160,19 +159,14 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
        INIT_LIST_HEAD(&chan->nvsw.flip);
        INIT_LIST_HEAD(&chan->fence.pending);
 
-       /* Allocate DMA push buffer */
-       chan->pushbuf_bo = nouveau_channel_user_pushbuf_alloc(dev);
-       if (!chan->pushbuf_bo) {
-               ret = -ENOMEM;
-               NV_ERROR(dev, "pushbuf %d\n", ret);
+       /* setup channel's memory and vm */
+       ret = nouveau_gpuobj_channel_init(chan, vram_handle, gart_handle);
+       if (ret) {
+               NV_ERROR(dev, "gpuobj %d\n", ret);
                nouveau_channel_put(&chan);
                return ret;
        }
 
-       nouveau_dma_pre_init(chan);
-       chan->user_put = 0x40;
-       chan->user_get = 0x44;
-
        /* Allocate space for per-channel fixed notifier memory */
        ret = nouveau_notifier_init_channel(chan);
        if (ret) {
@@ -181,21 +175,17 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
                return ret;
        }
 
-       /* Setup channel's default objects */
-       ret = nouveau_gpuobj_channel_init(chan, vram_handle, gart_handle);
+       /* Allocate DMA push buffer */
+       ret = nouveau_channel_pushbuf_init(chan);
        if (ret) {
-               NV_ERROR(dev, "gpuobj %d\n", ret);
+               NV_ERROR(dev, "pushbuf %d\n", ret);
                nouveau_channel_put(&chan);
                return ret;
        }
 
-       /* Create a dma object for the push buffer */
-       ret = nouveau_channel_pushbuf_ctxdma_init(chan);
-       if (ret) {
-               NV_ERROR(dev, "pbctxdma %d\n", ret);
-               nouveau_channel_put(&chan);
-               return ret;
-       }
+       nouveau_dma_pre_init(chan);
+       chan->user_put = 0x40;
+       chan->user_get = 0x44;
 
        /* disable the fifo caches */
        pfifo->reassign(dev, false);
@@ -220,6 +210,11 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
        nouveau_debugfs_channel_init(chan);
 
        NV_DEBUG(dev, "channel %d initialised\n", chan->id);
+       if (fpriv) {
+               spin_lock(&fpriv->lock);
+               list_add(&chan->list, &fpriv->channels);
+               spin_unlock(&fpriv->lock);
+       }
        *chan_ret = chan;
        return 0;
 }
@@ -236,29 +231,23 @@ nouveau_channel_get_unlocked(struct nouveau_channel *ref)
 }
 
 struct nouveau_channel *
-nouveau_channel_get(struct drm_device *dev, struct drm_file *file_priv, int id)
+nouveau_channel_get(struct drm_file *file_priv, int id)
 {
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
+       struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
        struct nouveau_channel *chan;
-       unsigned long flags;
-
-       if (unlikely(id < 0 || id >= NOUVEAU_MAX_CHANNEL_NR))
-               return ERR_PTR(-EINVAL);
-
-       spin_lock_irqsave(&dev_priv->channels.lock, flags);
-       chan = nouveau_channel_get_unlocked(dev_priv->channels.ptr[id]);
-       spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
-
-       if (unlikely(!chan))
-               return ERR_PTR(-EINVAL);
 
-       if (unlikely(file_priv && chan->file_priv != file_priv)) {
-               nouveau_channel_put_unlocked(&chan);
-               return ERR_PTR(-EINVAL);
+       spin_lock(&fpriv->lock);
+       list_for_each_entry(chan, &fpriv->channels, list) {
+               if (chan->id == id) {
+                       chan = nouveau_channel_get_unlocked(chan);
+                       spin_unlock(&fpriv->lock);
+                       mutex_lock(&chan->mutex);
+                       return chan;
+               }
        }
+       spin_unlock(&fpriv->lock);
 
-       mutex_lock(&chan->mutex);
-       return chan;
+       return ERR_PTR(-EINVAL);
 }
 
 void
@@ -312,12 +301,14 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan)
        /* destroy any resources the channel owned */
        nouveau_gpuobj_ref(NULL, &chan->pushbuf);
        if (chan->pushbuf_bo) {
+               nouveau_bo_vma_del(chan->pushbuf_bo, &chan->pushbuf_vma);
                nouveau_bo_unmap(chan->pushbuf_bo);
                nouveau_bo_unpin(chan->pushbuf_bo);
                nouveau_bo_ref(NULL, &chan->pushbuf_bo);
        }
-       nouveau_gpuobj_channel_takedown(chan);
+       nouveau_ramht_ref(NULL, &chan->ramht, chan);
        nouveau_notifier_takedown_channel(chan);
+       nouveau_gpuobj_channel_takedown(chan);
 
        nouveau_channel_ref(NULL, pchan);
 }
@@ -383,10 +374,11 @@ nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv)
 
        NV_DEBUG(dev, "clearing FIFO enables from file_priv\n");
        for (i = 0; i < engine->fifo.channels; i++) {
-               chan = nouveau_channel_get(dev, file_priv, i);
+               chan = nouveau_channel_get(file_priv, i);
                if (IS_ERR(chan))
                        continue;
 
+               list_del(&chan->list);
                atomic_dec(&chan->users);
                nouveau_channel_put(&chan);
        }
@@ -459,10 +451,11 @@ nouveau_ioctl_fifo_free(struct drm_device *dev, void *data,
        struct drm_nouveau_channel_free *req = data;
        struct nouveau_channel *chan;
 
-       chan = nouveau_channel_get(dev, file_priv, req->channel);
+       chan = nouveau_channel_get(file_priv, req->channel);
        if (IS_ERR(chan))
                return PTR_ERR(chan);
 
+       list_del(&chan->list);
        atomic_dec(&chan->users);
        nouveau_channel_put(&chan);
        return 0;