Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
[GitHub/exynos8895/android_kernel_samsung_universal8895.git] / drivers / gpu / drm / nouveau / nouveau_abi16.c
index ff23d88880e50ab727b6836c4fc7ad6d8860a222..cc79c796afee316cd810125b93dd86fbef940d5e 100644 (file)
  *
  */
 
-#include "drmP.h"
+#include <core/object.h>
+#include <core/client.h>
+#include <core/device.h>
+#include <core/class.h>
+#include <core/mm.h>
 
-#include "nouveau_drv.h"
+#include <subdev/fb.h>
+#include <subdev/timer.h>
+#include <subdev/instmem.h>
+
+#include "nouveau_drm.h"
 #include "nouveau_dma.h"
+#include "nouveau_gem.h"
+#include "nouveau_chan.h"
 #include "nouveau_abi16.h"
-#include "nouveau_ramht.h"
-#include "nouveau_software.h"
+
+struct nouveau_abi16 *
+nouveau_abi16_get(struct drm_file *file_priv, struct drm_device *dev)
+{
+       struct nouveau_cli *cli = nouveau_cli(file_priv);
+       mutex_lock(&cli->mutex);
+       if (!cli->abi16) {
+               struct nouveau_abi16 *abi16;
+               cli->abi16 = abi16 = kzalloc(sizeof(*abi16), GFP_KERNEL);
+               if (cli->abi16) {
+                       INIT_LIST_HEAD(&abi16->channels);
+                       abi16->client = nv_object(cli);
+
+                       /* allocate device object targeting client's default
+                        * device (ie. the one that belongs to the fd it
+                        * opened)
+                        */
+                       if (nouveau_object_new(abi16->client, NVDRM_CLIENT,
+                                              NVDRM_DEVICE, 0x0080,
+                                              &(struct nv_device_class) {
+                                               .device = ~0ULL,
+                                              },
+                                              sizeof(struct nv_device_class),
+                                              &abi16->device) == 0)
+                               return cli->abi16;
+
+                       kfree(cli->abi16);
+                       cli->abi16 = NULL;
+               }
+
+               mutex_unlock(&cli->mutex);
+       }
+       return cli->abi16;
+}
+
+int
+nouveau_abi16_put(struct nouveau_abi16 *abi16, int ret)
+{
+       struct nouveau_cli *cli = (void *)abi16->client;
+       mutex_unlock(&cli->mutex);
+       return ret;
+}
+
+u16
+nouveau_abi16_swclass(struct nouveau_drm *drm)
+{
+       switch (nv_device(drm->device)->card_type) {
+       case NV_04:
+               return 0x006e;
+       case NV_10:
+       case NV_20:
+       case NV_30:
+       case NV_40:
+               return 0x016e;
+       case NV_50:
+               return 0x506e;
+       case NV_C0:
+       case NV_D0:
+       case NV_E0:
+               return 0x906e;
+       }
+
+       return 0x0000;
+}
+
+static void
+nouveau_abi16_ntfy_fini(struct nouveau_abi16_chan *chan,
+                       struct nouveau_abi16_ntfy *ntfy)
+{
+       nouveau_mm_free(&chan->heap, &ntfy->node);
+       list_del(&ntfy->head);
+       kfree(ntfy);
+}
+
+static void
+nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,
+                       struct nouveau_abi16_chan *chan)
+{
+       struct nouveau_abi16_ntfy *ntfy, *temp;
+
+       /* cleanup notifier state */
+       list_for_each_entry_safe(ntfy, temp, &chan->notifiers, head) {
+               nouveau_abi16_ntfy_fini(chan, ntfy);
+       }
+
+       if (chan->ntfy) {
+               nouveau_bo_vma_del(chan->ntfy, &chan->ntfy_vma);
+               drm_gem_object_unreference_unlocked(chan->ntfy->gem);
+       }
+
+       if (chan->heap.block_size)
+               nouveau_mm_fini(&chan->heap);
+
+       /* destroy channel object, all children will be killed too */
+       if (chan->chan) {
+               abi16->handles &= ~(1 << (chan->chan->handle & 0xffff));
+               nouveau_channel_del(&chan->chan);
+       }
+
+       list_del(&chan->head);
+       kfree(chan);
+}
+
+void
+nouveau_abi16_fini(struct nouveau_abi16 *abi16)
+{
+       struct nouveau_cli *cli = (void *)abi16->client;
+       struct nouveau_abi16_chan *chan, *temp;
+
+       /* cleanup channels */
+       list_for_each_entry_safe(chan, temp, &abi16->channels, head) {
+               nouveau_abi16_chan_fini(abi16, chan);
+       }
+
+       /* destroy the device object */
+       nouveau_object_del(abi16->client, NVDRM_CLIENT, NVDRM_DEVICE);
+
+       kfree(cli->abi16);
+       cli->abi16 = NULL;
+}
 
 int
 nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
 {
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_device *device = nv_device(drm->device);
+       struct nouveau_timer *ptimer = nouveau_timer(device);
        struct drm_nouveau_getparam *getparam = data;
 
        switch (getparam->param) {
        case NOUVEAU_GETPARAM_CHIPSET_ID:
-               getparam->value = dev_priv->chipset;
+               getparam->value = device->chipset;
                break;
        case NOUVEAU_GETPARAM_PCI_VENDOR:
                getparam->value = dev->pci_vendor;
@@ -55,16 +185,16 @@ nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
                        getparam->value = 2;
                break;
        case NOUVEAU_GETPARAM_FB_SIZE:
-               getparam->value = dev_priv->fb_available_size;
+               getparam->value = drm->gem.vram_available;
                break;
        case NOUVEAU_GETPARAM_AGP_SIZE:
-               getparam->value = dev_priv->gart_info.aper_size;
+               getparam->value = drm->gem.gart_available;
                break;
        case NOUVEAU_GETPARAM_VM_VRAM_BASE:
                getparam->value = 0; /* deprecated */
                break;
        case NOUVEAU_GETPARAM_PTIMER_TIME:
-               getparam->value = dev_priv->engine.timer.read(dev);
+               getparam->value = ptimer->read(ptimer);
                break;
        case NOUVEAU_GETPARAM_HAS_BO_USAGE:
                getparam->value = 1;
@@ -76,13 +206,13 @@ nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
                /* NV40 and NV50 versions are quite different, but register
                 * address is the same. User is supposed to know the card
                 * family anyway... */
-               if (dev_priv->chipset >= 0x40) {
-                       getparam->value = nv_rd32(dev, NV40_PMC_GRAPH_UNITS);
+               if (device->chipset >= 0x40) {
+                       getparam->value = nv_rd32(device, 0x001540);
                        break;
                }
                /* FALLTHRU */
        default:
-               NV_DEBUG(dev, "unknown parameter %lld\n", getparam->param);
+               nv_debug(device, "unknown parameter %lld\n", getparam->param);
                return -EINVAL;
        }
 
@@ -98,148 +228,252 @@ nouveau_abi16_ioctl_setparam(ABI16_IOCTL_ARGS)
 int
 nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
 {
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct drm_nouveau_channel_alloc *init = data;
-       struct nouveau_channel *chan;
+       struct nouveau_cli *cli = nouveau_cli(file_priv);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
+       struct nouveau_abi16_chan *chan;
+       struct nouveau_client *client;
+       struct nouveau_device *device;
+       struct nouveau_instmem *imem;
+       struct nouveau_fb *pfb;
        int ret;
 
-       if (!dev_priv->eng[NVOBJ_ENGINE_GR])
-               return -ENODEV;
+       if (unlikely(!abi16))
+               return -ENOMEM;
+       client = nv_client(abi16->client);
 
        if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0)
-               return -EINVAL;
+               return nouveau_abi16_put(abi16, -EINVAL);
+
+       device = nv_device(abi16->device);
+       imem   = nouveau_instmem(device);
+       pfb    = nouveau_fb(device);
+
+       /* allocate "abi16 channel" data and make up a handle for it */
+       init->channel = ffsll(~abi16->handles);
+       if (!init->channel--)
+               return nouveau_abi16_put(abi16, -ENOSPC);
+
+       chan = kzalloc(sizeof(*chan), GFP_KERNEL);
+       if (!chan)
+               return nouveau_abi16_put(abi16, -ENOMEM);
+
+       INIT_LIST_HEAD(&chan->notifiers);
+       list_add(&chan->head, &abi16->channels);
+       abi16->handles |= (1 << init->channel);
+
+       /* create channel object and initialise dma and fence management */
+       if (device->card_type >= NV_E0) {
+               init->fb_ctxdma_handle = NVE0_CHANNEL_IND_ENGINE_GR;
+               init->tt_ctxdma_handle = 0;
+       }
 
-       ret = nouveau_channel_alloc(dev, &chan, file_priv,
-                                   init->fb_ctxdma_handle,
-                                   init->tt_ctxdma_handle);
+       ret = nouveau_channel_new(drm, cli, NVDRM_DEVICE, NVDRM_CHAN |
+                                 init->channel, init->fb_ctxdma_handle,
+                                 init->tt_ctxdma_handle, &chan->chan);
        if (ret)
-               return ret;
-       init->channel  = chan->id;
-
-       if (nouveau_vram_pushbuf == 0) {
-               if (chan->dma.ib_max)
-                       init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
-                                               NOUVEAU_GEM_DOMAIN_GART;
-               else if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_VRAM)
-                       init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
-               else
-                       init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
-       } else {
+               goto done;
+
+       if (device->card_type >= NV_50)
+               init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
+                                       NOUVEAU_GEM_DOMAIN_GART;
+       else
+       if (chan->chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM)
                init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
-       }
+       else
+               init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
 
-       if (dev_priv->card_type < NV_C0) {
+       if (device->card_type < NV_C0) {
                init->subchan[0].handle = 0x00000000;
                init->subchan[0].grclass = 0x0000;
                init->subchan[1].handle = NvSw;
-               init->subchan[1].grclass = NV_SW;
+               init->subchan[1].grclass = 0x506e;
                init->nr_subchan = 2;
        }
 
        /* Named memory object area */
-       ret = drm_gem_handle_create(file_priv, chan->notifier_bo->gem,
+       ret = nouveau_gem_new(dev, PAGE_SIZE, 0, NOUVEAU_GEM_DOMAIN_GART,
+                             0, 0, &chan->ntfy);
+       if (ret == 0)
+               ret = nouveau_bo_pin(chan->ntfy, TTM_PL_FLAG_TT);
+       if (ret)
+               goto done;
+
+       if (device->card_type >= NV_50) {
+               ret = nouveau_bo_vma_add(chan->ntfy, client->vm,
+                                       &chan->ntfy_vma);
+               if (ret)
+                       goto done;
+       }
+
+       ret = drm_gem_handle_create(file_priv, chan->ntfy->gem,
                                    &init->notifier_handle);
+       if (ret)
+               goto done;
 
-       if (ret == 0)
-               atomic_inc(&chan->users); /* userspace reference */
-       nouveau_channel_put(&chan);
-       return ret;
+       ret = nouveau_mm_init(&chan->heap, 0, PAGE_SIZE, 1);
+done:
+       if (ret)
+               nouveau_abi16_chan_fini(abi16, chan);
+       return nouveau_abi16_put(abi16, ret);
 }
 
+
 int
 nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS)
 {
        struct drm_nouveau_channel_free *req = data;
-       struct nouveau_channel *chan;
+       struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
+       struct nouveau_abi16_chan *chan;
+       int ret = -ENOENT;
 
-       chan = nouveau_channel_get(file_priv, req->channel);
-       if (IS_ERR(chan))
-               return PTR_ERR(chan);
+       if (unlikely(!abi16))
+               return -ENOMEM;
 
-       list_del(&chan->list);
-       atomic_dec(&chan->users);
-       nouveau_channel_put(&chan);
-       return 0;
+       list_for_each_entry(chan, &abi16->channels, head) {
+               if (chan->chan->handle == (NVDRM_CHAN | req->channel)) {
+                       nouveau_abi16_chan_fini(abi16, chan);
+                       return nouveau_abi16_put(abi16, 0);
+               }
+       }
+
+       return nouveau_abi16_put(abi16, ret);
 }
 
 int
 nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)
 {
        struct drm_nouveau_grobj_alloc *init = data;
-       struct nouveau_channel *chan;
+       struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_object *object;
        int ret;
 
+       if (unlikely(!abi16))
+               return -ENOMEM;
+
        if (init->handle == ~0)
-               return -EINVAL;
+               return nouveau_abi16_put(abi16, -EINVAL);
 
        /* compatibility with userspace that assumes 506e for all chipsets */
        if (init->class == 0x506e) {
-               init->class = nouveau_software_class(dev);
+               init->class = nouveau_abi16_swclass(drm);
                if (init->class == 0x906e)
-                       return 0;
-       } else
-       if (init->class == 0x906e) {
-               NV_ERROR(dev, "906e not supported yet\n");
-               return -EINVAL;
-       }
-
-       chan = nouveau_channel_get(file_priv, init->channel);
-       if (IS_ERR(chan))
-               return PTR_ERR(chan);
-
-       if (nouveau_ramht_find(chan, init->handle)) {
-               ret = -EEXIST;
-               goto out;
-       }
-
-       ret = nouveau_gpuobj_gr_new(chan, init->handle, init->class);
-       if (ret) {
-               NV_ERROR(dev, "Error creating object: %d (%d/0x%08x)\n",
-                        ret, init->channel, init->handle);
+                       return nouveau_abi16_put(abi16, 0);
        }
 
-out:
-       nouveau_channel_put(&chan);
-       return ret;
+       ret = nouveau_object_new(abi16->client, NVDRM_CHAN | init->channel,
+                                 init->handle, init->class, NULL, 0, &object);
+       return nouveau_abi16_put(abi16, ret);
 }
 
 int
 nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)
 {
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct drm_nouveau_notifierobj_alloc *na = data;
-       struct nouveau_channel *chan;
+       struct drm_nouveau_notifierobj_alloc *info = data;
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_device *device = nv_device(drm->device);
+       struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
+       struct nouveau_abi16_chan *chan, *temp;
+       struct nouveau_abi16_ntfy *ntfy;
+       struct nouveau_object *object;
+       struct nv_dma_class args;
        int ret;
 
+       if (unlikely(!abi16))
+               return -ENOMEM;
+
        /* completely unnecessary for these chipsets... */
-       if (unlikely(dev_priv->card_type >= NV_C0))
-               return -EINVAL;
+       if (unlikely(nv_device(abi16->device)->card_type >= NV_C0))
+               return nouveau_abi16_put(abi16, -EINVAL);
 
-       chan = nouveau_channel_get(file_priv, na->channel);
-       if (IS_ERR(chan))
-               return PTR_ERR(chan);
+       list_for_each_entry_safe(chan, temp, &abi16->channels, head) {
+               if (chan->chan->handle == (NVDRM_CHAN | info->channel))
+                       break;
+               chan = NULL;
+       }
 
-       ret = nouveau_notifier_alloc(chan, na->handle, na->size, 0, 0x1000,
-                                    &na->offset);
-       nouveau_channel_put(&chan);
-       return ret;
+       if (!chan)
+               return nouveau_abi16_put(abi16, -ENOENT);
+
+       ntfy = kzalloc(sizeof(*ntfy), GFP_KERNEL);
+       if (!ntfy)
+               return nouveau_abi16_put(abi16, -ENOMEM);
+
+       list_add(&ntfy->head, &chan->notifiers);
+       ntfy->handle = info->handle;
+
+       ret = nouveau_mm_head(&chan->heap, 1, info->size, info->size, 1,
+                             &ntfy->node);
+       if (ret)
+               goto done;
+
+       args.start = ntfy->node->offset;
+       args.limit = ntfy->node->offset + ntfy->node->length - 1;
+       if (device->card_type >= NV_50) {
+               args.flags  = NV_DMA_TARGET_VM | NV_DMA_ACCESS_VM;
+               args.start += chan->ntfy_vma.offset;
+               args.limit += chan->ntfy_vma.offset;
+       } else
+       if (drm->agp.stat == ENABLED) {
+               args.flags  = NV_DMA_TARGET_AGP | NV_DMA_ACCESS_RDWR;
+               args.start += drm->agp.base + chan->ntfy->bo.offset;
+               args.limit += drm->agp.base + chan->ntfy->bo.offset;
+       } else {
+               args.flags  = NV_DMA_TARGET_VM | NV_DMA_ACCESS_RDWR;
+               args.start += chan->ntfy->bo.offset;
+               args.limit += chan->ntfy->bo.offset;
+       }
+
+       ret = nouveau_object_new(abi16->client, chan->chan->handle,
+                                ntfy->handle, 0x003d, &args,
+                                sizeof(args), &object);
+       if (ret)
+               goto done;
+
+done:
+       if (ret)
+               nouveau_abi16_ntfy_fini(chan, ntfy);
+       return nouveau_abi16_put(abi16, ret);
 }
 
 int
 nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS)
 {
-       struct drm_nouveau_gpuobj_free *objfree = data;
-       struct nouveau_channel *chan;
+       struct drm_nouveau_gpuobj_free *fini = data;
+       struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
+       struct nouveau_abi16_chan *chan, *temp;
+       struct nouveau_abi16_ntfy *ntfy;
        int ret;
 
-       chan = nouveau_channel_get(file_priv, objfree->channel);
-       if (IS_ERR(chan))
-               return PTR_ERR(chan);
+       if (unlikely(!abi16))
+               return -ENOMEM;
+
+       list_for_each_entry_safe(chan, temp, &abi16->channels, head) {
+               if (chan->chan->handle == (NVDRM_CHAN | fini->channel))
+                       break;
+               chan = NULL;
+       }
+
+       if (!chan)
+               return nouveau_abi16_put(abi16, -ENOENT);
 
-       /* Synchronize with the user channel */
-       nouveau_channel_idle(chan);
+       /* synchronize with the user channel and destroy the gpu object */
+       nouveau_channel_idle(chan->chan);
 
-       ret = nouveau_ramht_remove(chan, objfree->handle);
-       nouveau_channel_put(&chan);
-       return ret;
+       ret = nouveau_object_del(abi16->client, chan->chan->handle, fini->handle);
+       if (ret)
+               return nouveau_abi16_put(abi16, ret);
+
+       /* cleanup extra state if this object was a notifier */
+       list_for_each_entry(ntfy, &chan->notifiers, head) {
+               if (ntfy->handle == fini->handle) {
+                       nouveau_mm_free(&chan->heap, &ntfy->node);
+                       list_del(&ntfy->head);
+                       break;
+               }
+       }
+
+       return nouveau_abi16_put(abi16, 0);
 }