linux/drivers/gpu/drm/nouveau/core/core/gpuobj.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Red Hat Inc.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * Authors: Ben Skeggs
  23 */
  24
  25#include <core/object.h>
  26#include <core/gpuobj.h>
  27
  28#include <subdev/instmem.h>
  29#include <subdev/bar.h>
  30#include <subdev/vm.h>
  31
  32void
  33nouveau_gpuobj_destroy(struct nouveau_gpuobj *gpuobj)
  34{
  35        int i;
  36
  37        if (gpuobj->flags & NVOBJ_FLAG_ZERO_FREE) {
  38                for (i = 0; i < gpuobj->size; i += 4)
  39                        nv_wo32(gpuobj, i, 0x00000000);
  40        }
  41
  42        if (gpuobj->node) {
  43                nouveau_mm_free(&nv_gpuobj(gpuobj->parent)->heap,
  44                                &gpuobj->node);
  45        }
  46
  47        if (gpuobj->heap.block_size)
  48                nouveau_mm_fini(&gpuobj->heap);
  49
  50        nouveau_object_destroy(&gpuobj->base);
  51}
  52
  53int
  54nouveau_gpuobj_create_(struct nouveau_object *parent,
  55                       struct nouveau_object *engine,
  56                       struct nouveau_oclass *oclass, u32 pclass,
  57                       struct nouveau_object *pargpu,
  58                       u32 size, u32 align, u32 flags,
  59                       int length, void **pobject)
  60{
  61        struct nouveau_instmem *imem = nouveau_instmem(parent);
  62        struct nouveau_bar *bar = nouveau_bar(parent);
  63        struct nouveau_gpuobj *gpuobj;
  64        struct nouveau_mm *heap = NULL;
  65        int ret, i;
  66        u64 addr;
  67
  68        *pobject = NULL;
  69
  70        if (pargpu) {
  71                while ((pargpu = nv_pclass(pargpu, NV_GPUOBJ_CLASS))) {
  72                        if (nv_gpuobj(pargpu)->heap.block_size)
  73                                break;
  74                        pargpu = pargpu->parent;
  75                }
  76
  77                if (unlikely(pargpu == NULL)) {
  78                        nv_error(parent, "no gpuobj heap\n");
  79                        return -EINVAL;
  80                }
  81
  82                addr =  nv_gpuobj(pargpu)->addr;
  83                heap = &nv_gpuobj(pargpu)->heap;
  84                atomic_inc(&parent->refcount);
  85        } else {
  86                ret = imem->alloc(imem, parent, size, align, &parent);
  87                pargpu = parent;
  88                if (ret)
  89                        return ret;
  90
  91                addr = nv_memobj(pargpu)->addr;
  92                size = nv_memobj(pargpu)->size;
  93
  94                if (bar && bar->alloc) {
  95                        struct nouveau_instobj *iobj = (void *)parent;
  96                        struct nouveau_mem **mem = (void *)(iobj + 1);
  97                        struct nouveau_mem *node = *mem;
  98                        if (!bar->alloc(bar, parent, node, &pargpu)) {
  99                                nouveau_object_ref(NULL, &parent);
 100                                parent = pargpu;
 101                        }
 102                }
 103        }
 104
 105        ret = nouveau_object_create_(parent, engine, oclass, pclass |
 106                                     NV_GPUOBJ_CLASS, length, pobject);
 107        nouveau_object_ref(NULL, &parent);
 108        gpuobj = *pobject;
 109        if (ret)
 110                return ret;
 111
 112        gpuobj->parent = pargpu;
 113        gpuobj->flags = flags;
 114        gpuobj->addr = addr;
 115        gpuobj->size = size;
 116
 117        if (heap) {
 118                ret = nouveau_mm_head(heap, 1, size, size,
 119                                      max(align, (u32)1), &gpuobj->node);
 120                if (ret)
 121                        return ret;
 122
 123                gpuobj->addr += gpuobj->node->offset;
 124        }
 125
 126        if (gpuobj->flags & NVOBJ_FLAG_HEAP) {
 127                ret = nouveau_mm_init(&gpuobj->heap, 0, gpuobj->size, 1);
 128                if (ret)
 129                        return ret;
 130        }
 131
 132        if (flags & NVOBJ_FLAG_ZERO_ALLOC) {
 133                for (i = 0; i < gpuobj->size; i += 4)
 134                        nv_wo32(gpuobj, i, 0x00000000);
 135        }
 136
 137        return ret;
 138}
 139
 140struct nouveau_gpuobj_class {
 141        struct nouveau_object *pargpu;
 142        u64 size;
 143        u32 align;
 144        u32 flags;
 145};
 146
 147static int
 148_nouveau_gpuobj_ctor(struct nouveau_object *parent,
 149                     struct nouveau_object *engine,
 150                     struct nouveau_oclass *oclass, void *data, u32 size,
 151                     struct nouveau_object **pobject)
 152{
 153        struct nouveau_gpuobj_class *args = data;
 154        struct nouveau_gpuobj *object;
 155        int ret;
 156
 157        ret = nouveau_gpuobj_create(parent, engine, oclass, 0, args->pargpu,
 158                                    args->size, args->align, args->flags,
 159                                    &object);
 160        *pobject = nv_object(object);
 161        if (ret)
 162                return ret;
 163
 164        return 0;
 165}
 166
 167void
 168_nouveau_gpuobj_dtor(struct nouveau_object *object)
 169{
 170        nouveau_gpuobj_destroy(nv_gpuobj(object));
 171}
 172
 173int
 174_nouveau_gpuobj_init(struct nouveau_object *object)
 175{
 176        return nouveau_gpuobj_init(nv_gpuobj(object));
 177}
 178
 179int
 180_nouveau_gpuobj_fini(struct nouveau_object *object, bool suspend)
 181{
 182        return nouveau_gpuobj_fini(nv_gpuobj(object), suspend);
 183}
 184
 185u32
 186_nouveau_gpuobj_rd32(struct nouveau_object *object, u64 addr)
 187{
 188        struct nouveau_gpuobj *gpuobj = nv_gpuobj(object);
 189        struct nouveau_ofuncs *pfuncs = nv_ofuncs(gpuobj->parent);
 190        if (gpuobj->node)
 191                addr += gpuobj->node->offset;
 192        return pfuncs->rd32(gpuobj->parent, addr);
 193}
 194
 195void
 196_nouveau_gpuobj_wr32(struct nouveau_object *object, u64 addr, u32 data)
 197{
 198        struct nouveau_gpuobj *gpuobj = nv_gpuobj(object);
 199        struct nouveau_ofuncs *pfuncs = nv_ofuncs(gpuobj->parent);
 200        if (gpuobj->node)
 201                addr += gpuobj->node->offset;
 202        pfuncs->wr32(gpuobj->parent, addr, data);
 203}
 204
 205static struct nouveau_oclass
 206_nouveau_gpuobj_oclass = {
 207        .handle = 0x00000000,
 208        .ofuncs = &(struct nouveau_ofuncs) {
 209                .ctor = _nouveau_gpuobj_ctor,
 210                .dtor = _nouveau_gpuobj_dtor,
 211                .init = _nouveau_gpuobj_init,
 212                .fini = _nouveau_gpuobj_fini,
 213                .rd32 = _nouveau_gpuobj_rd32,
 214                .wr32 = _nouveau_gpuobj_wr32,
 215        },
 216};
 217
 218int
 219nouveau_gpuobj_new(struct nouveau_object *parent, struct nouveau_object *pargpu,
 220                   u32 size, u32 align, u32 flags,
 221                   struct nouveau_gpuobj **pgpuobj)
 222{
 223        struct nouveau_object *engine = parent;
 224        struct nouveau_gpuobj_class args = {
 225                .pargpu = pargpu,
 226                .size = size,
 227                .align = align,
 228                .flags = flags,
 229        };
 230
 231        if (!nv_iclass(engine, NV_SUBDEV_CLASS))
 232                engine = engine->engine;
 233        BUG_ON(engine == NULL);
 234
 235        return nouveau_object_ctor(parent, engine, &_nouveau_gpuobj_oclass,
 236                                   &args, sizeof(args),
 237                                   (struct nouveau_object **)pgpuobj);
 238}
 239
 240int
 241nouveau_gpuobj_map(struct nouveau_gpuobj *gpuobj, u32 access,
 242                   struct nouveau_vma *vma)
 243{
 244        struct nouveau_bar *bar = nouveau_bar(gpuobj);
 245        int ret = -EINVAL;
 246
 247        if (bar && bar->umap) {
 248                struct nouveau_instobj *iobj = (void *)
 249                        nv_pclass(nv_object(gpuobj), NV_MEMOBJ_CLASS);
 250                struct nouveau_mem **mem = (void *)(iobj + 1);
 251                ret = bar->umap(bar, *mem, access, vma);
 252        }
 253
 254        return ret;
 255}
 256
 257int
 258nouveau_gpuobj_map_vm(struct nouveau_gpuobj *gpuobj, struct nouveau_vm *vm,
 259                      u32 access, struct nouveau_vma *vma)
 260{
 261        struct nouveau_instobj *iobj = (void *)
 262                nv_pclass(nv_object(gpuobj), NV_MEMOBJ_CLASS);
 263        struct nouveau_mem **mem = (void *)(iobj + 1);
 264        int ret;
 265
 266        ret = nouveau_vm_get(vm, gpuobj->size, 12, access, vma);
 267        if (ret)
 268                return ret;
 269
 270        nouveau_vm_map(vma, *mem);
 271        return 0;
 272}
 273
 274void
 275nouveau_gpuobj_unmap(struct nouveau_vma *vma)
 276{
 277        if (vma->node) {
 278                nouveau_vm_unmap(vma);
 279                nouveau_vm_put(vma);
 280        }
 281}
 282
 283/* the below is basically only here to support sharing the paged dma object
 284 * for PCI(E)GART on <=nv4x chipsets, and should *not* be expected to work
 285 * anywhere else.
 286 */
 287
 288static void
 289nouveau_gpudup_dtor(struct nouveau_object *object)
 290{
 291        struct nouveau_gpuobj *gpuobj = (void *)object;
 292        nouveau_object_ref(NULL, &gpuobj->parent);
 293        nouveau_object_destroy(&gpuobj->base);
 294}
 295
 296static struct nouveau_oclass
 297nouveau_gpudup_oclass = {
 298        .handle = NV_GPUOBJ_CLASS,
 299        .ofuncs = &(struct nouveau_ofuncs) {
 300                .dtor = nouveau_gpudup_dtor,
 301                .init = nouveau_object_init,
 302                .fini = nouveau_object_fini,
 303        },
 304};
 305
 306int
 307nouveau_gpuobj_dup(struct nouveau_object *parent, struct nouveau_gpuobj *base,
 308                   struct nouveau_gpuobj **pgpuobj)
 309{
 310        struct nouveau_gpuobj *gpuobj;
 311        int ret;
 312
 313        ret = nouveau_object_create(parent, parent->engine,
 314                                   &nouveau_gpudup_oclass, 0, &gpuobj);
 315        *pgpuobj = gpuobj;
 316        if (ret)
 317                return ret;
 318
 319        nouveau_object_ref(nv_object(base), &gpuobj->parent);
 320        gpuobj->addr = base->addr;
 321        gpuobj->size = base->size;
 322        return 0;
 323}
 324