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 | |
37 | void |
38 | nouveau_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 | |
58 | int |
59 | nouveau_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 | |
145 | struct nouveau_gpuobj_class { |
146 | struct nouveau_object *pargpu; |
147 | u64 size; |
148 | u32 align; |
149 | u32 flags; |
150 | }; |
151 | |
152 | static 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 | |
172 | void |
173 | _nouveau_gpuobj_dtor(struct nouveau_object *object) |
174 | { |
175 | nouveau_gpuobj_destroy(nv_gpuobj(object)); |
176 | } |
177 | |
178 | int |
179 | _nouveau_gpuobj_init(struct nouveau_object *object) |
180 | { |
181 | return nouveau_gpuobj_init(nv_gpuobj(object)); |
182 | } |
183 | |
184 | int |
185 | _nouveau_gpuobj_fini(struct nouveau_object *object, bool suspend) |
186 | { |
187 | return nouveau_gpuobj_fini(nv_gpuobj(object), suspend); |
188 | } |
189 | |
190 | u32 |
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 | |
200 | void |
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 | |
210 | static 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 | |
223 | int |
224 | nouveau_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 | |
245 | int |
246 | nouveau_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 | |
262 | int |
263 | nouveau_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 | |
279 | void |
280 | nouveau_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 | |
293 | static void |
294 | nouveau_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 | |
301 | static struct nouveau_oclass |
302 | nouveau_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 | |
311 | int |
312 | nouveau_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 | |