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