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 #include "chan.h"
25 
26 #include <core/client.h>
27 #include <core/oproxy.h>
28 #include <subdev/mmu.h>
29 #include <engine/dma.h>
30 
31 struct nvkm_fifo_chan_object {
32 	struct nvkm_oproxy oproxy;
33 	struct nvkm_fifo_chan *chan;
34 	int hash;
35 };
36 
37 static int
38 nvkm_fifo_chan_child_fini(struct nvkm_oproxy *base, bool suspend)
39 {
40 	struct nvkm_fifo_chan_object *object =
41 		container_of(base, typeof(*object), oproxy);
42 	struct nvkm_engine *engine  = object->oproxy.object->engine;
43 	struct nvkm_fifo_chan *chan = object->chan;
44 	struct nvkm_fifo_engn *engn = &chan->engn[engine->subdev.index];
45 	const char *name = nvkm_subdev_name[engine->subdev.index];
46 	int ret = 0;
47 
48 	if (--engn->usecount)
49 		return 0;
50 
51 	if (chan->func->engine_fini) {
52 		ret = chan->func->engine_fini(chan, engine, suspend);
53 		if (ret) {
54 			nvif_error(&chan->object,
55 				   "detach %s failed, %d\n", name, ret);
56 			return ret;
57 		}
58 	}
59 
60 	if (engn->object) {
61 		ret = nvkm_object_fini(engn->object, suspend);
62 		if (ret && suspend)
63 			return ret;
64 	}
65 
66 	nvif_trace(&chan->object, "detached %s\n", name);
67 	return ret;
68 }
69 
70 static int
71 nvkm_fifo_chan_child_init(struct nvkm_oproxy *base)
72 {
73 	struct nvkm_fifo_chan_object *object =
74 		container_of(base, typeof(*object), oproxy);
75 	struct nvkm_engine *engine  = object->oproxy.object->engine;
76 	struct nvkm_fifo_chan *chan = object->chan;
77 	struct nvkm_fifo_engn *engn = &chan->engn[engine->subdev.index];
78 	const char *name = nvkm_subdev_name[engine->subdev.index];
79 	int ret;
80 
81 	if (engn->usecount++)
82 		return 0;
83 
84 	if (engn->object) {
85 		ret = nvkm_object_init(engn->object);
86 		if (ret)
87 			return ret;
88 	}
89 
90 	if (chan->func->engine_init) {
91 		ret = chan->func->engine_init(chan, engine);
92 		if (ret) {
93 			nvif_error(&chan->object,
94 				   "attach %s failed, %d\n", name, ret);
95 			return ret;
96 		}
97 	}
98 
99 	nvif_trace(&chan->object, "attached %s\n", name);
100 	return 0;
101 }
102 
103 static void
104 nvkm_fifo_chan_child_del(struct nvkm_oproxy *base)
105 {
106 	struct nvkm_fifo_chan_object *object =
107 		container_of(base, typeof(*object), oproxy);
108 	struct nvkm_engine *engine  = object->oproxy.base.engine;
109 	struct nvkm_fifo_chan *chan = object->chan;
110 	struct nvkm_fifo_engn *engn = &chan->engn[engine->subdev.index];
111 
112 	if (chan->func->object_dtor)
113 		chan->func->object_dtor(chan, object->hash);
114 
115 	if (!--engn->refcount) {
116 		if (chan->func->engine_dtor)
117 			chan->func->engine_dtor(chan, engine);
118 		nvkm_object_ref(NULL, &engn->object);
119 		if (chan->vm)
120 			atomic_dec(&chan->vm->engref[engine->subdev.index]);
121 	}
122 }
123 
124 static const struct nvkm_oproxy_func
125 nvkm_fifo_chan_child_func = {
126 	.dtor[0] = nvkm_fifo_chan_child_del,
127 	.init[0] = nvkm_fifo_chan_child_init,
128 	.fini[0] = nvkm_fifo_chan_child_fini,
129 };
130 
131 static int
132 nvkm_fifo_chan_child_new(const struct nvkm_oclass *oclass, void *data, u32 size,
133 			 struct nvkm_object **pobject)
134 {
135 	struct nvkm_engine *engine = oclass->engine;
136 	struct nvkm_fifo_chan *chan = nvkm_fifo_chan(oclass->parent);
137 	struct nvkm_fifo_engn *engn = &chan->engn[engine->subdev.index];
138 	struct nvkm_fifo_chan_object *object;
139 	int ret = 0;
140 
141 	if (!(object = kzalloc(sizeof(*object), GFP_KERNEL)))
142 		return -ENOMEM;
143 	nvkm_oproxy_ctor(&nvkm_fifo_chan_child_func, oclass, &object->oproxy);
144 	object->chan = chan;
145 	*pobject = &object->oproxy.base;
146 
147 	if (!engn->refcount++) {
148 		struct nvkm_oclass cclass = {
149 			.client = oclass->client,
150 			.engine = oclass->engine,
151 		};
152 
153 		if (chan->vm)
154 			atomic_inc(&chan->vm->engref[engine->subdev.index]);
155 
156 		if (engine->func->fifo.cclass) {
157 			ret = engine->func->fifo.cclass(chan, &cclass,
158 							&engn->object);
159 		} else
160 		if (engine->func->cclass) {
161 			ret = nvkm_object_new_(engine->func->cclass, &cclass,
162 					       NULL, 0, &engn->object);
163 		}
164 		if (ret)
165 			return ret;
166 
167 		if (chan->func->engine_ctor) {
168 			ret = chan->func->engine_ctor(chan, oclass->engine,
169 						      engn->object);
170 			if (ret)
171 				return ret;
172 		}
173 	}
174 
175 	ret = oclass->base.ctor(&(const struct nvkm_oclass) {
176 					.base = oclass->base,
177 					.engn = oclass->engn,
178 					.handle = oclass->handle,
179 					.object = oclass->object,
180 					.client = oclass->client,
181 					.parent = engn->object ?
182 						  engn->object :
183 						  oclass->parent,
184 					.engine = engine,
185 				}, data, size, &object->oproxy.object);
186 	if (ret)
187 		return ret;
188 
189 	if (chan->func->object_ctor) {
190 		object->hash =
191 			chan->func->object_ctor(chan, object->oproxy.object);
192 		if (object->hash < 0)
193 			return object->hash;
194 	}
195 
196 	return 0;
197 }
198 
199 static int
200 nvkm_fifo_chan_child_get(struct nvkm_object *object, int index,
201 			 struct nvkm_oclass *oclass)
202 {
203 	struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object);
204 	struct nvkm_fifo *fifo = chan->fifo;
205 	struct nvkm_device *device = fifo->engine.subdev.device;
206 	struct nvkm_engine *engine;
207 	u64 mask = chan->engines;
208 	int ret, i, c;
209 
210 	for (; c = 0, i = __ffs64(mask), mask; mask &= ~(1ULL << i)) {
211 		if (!(engine = nvkm_device_engine(device, i)))
212 			continue;
213 		oclass->engine = engine;
214 		oclass->base.oclass = 0;
215 
216 		if (engine->func->fifo.sclass) {
217 			ret = engine->func->fifo.sclass(oclass, index);
218 			if (oclass->base.oclass) {
219 				if (!oclass->base.ctor)
220 					oclass->base.ctor = nvkm_object_new;
221 				oclass->ctor = nvkm_fifo_chan_child_new;
222 				return 0;
223 			}
224 
225 			index -= ret;
226 			continue;
227 		}
228 
229 		while (engine->func->sclass[c].oclass) {
230 			if (c++ == index) {
231 				oclass->base = engine->func->sclass[index];
232 				if (!oclass->base.ctor)
233 					oclass->base.ctor = nvkm_object_new;
234 				oclass->ctor = nvkm_fifo_chan_child_new;
235 				return 0;
236 			}
237 		}
238 		index -= c;
239 	}
240 
241 	return -EINVAL;
242 }
243 
244 static int
245 nvkm_fifo_chan_ntfy(struct nvkm_object *object, u32 type,
246 		    struct nvkm_event **pevent)
247 {
248 	struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object);
249 	if (chan->func->ntfy)
250 		return chan->func->ntfy(chan, type, pevent);
251 	return -ENODEV;
252 }
253 
254 static int
255 nvkm_fifo_chan_map(struct nvkm_object *object, u64 *addr, u32 *size)
256 {
257 	struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object);
258 	*addr = chan->addr;
259 	*size = chan->size;
260 	return 0;
261 }
262 
263 static int
264 nvkm_fifo_chan_rd32(struct nvkm_object *object, u64 addr, u32 *data)
265 {
266 	struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object);
267 	if (unlikely(!chan->user)) {
268 		chan->user = ioremap(chan->addr, chan->size);
269 		if (!chan->user)
270 			return -ENOMEM;
271 	}
272 	if (unlikely(addr + 4 > chan->size))
273 		return -EINVAL;
274 	*data = ioread32_native(chan->user + addr);
275 	return 0;
276 }
277 
278 static int
279 nvkm_fifo_chan_wr32(struct nvkm_object *object, u64 addr, u32 data)
280 {
281 	struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object);
282 	if (unlikely(!chan->user)) {
283 		chan->user = ioremap(chan->addr, chan->size);
284 		if (!chan->user)
285 			return -ENOMEM;
286 	}
287 	if (unlikely(addr + 4 > chan->size))
288 		return -EINVAL;
289 	iowrite32_native(data, chan->user + addr);
290 	return 0;
291 }
292 
293 static int
294 nvkm_fifo_chan_fini(struct nvkm_object *object, bool suspend)
295 {
296 	struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object);
297 	chan->func->fini(chan);
298 	return 0;
299 }
300 
301 static int
302 nvkm_fifo_chan_init(struct nvkm_object *object)
303 {
304 	struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object);
305 	chan->func->init(chan);
306 	return 0;
307 }
308 
309 static void *
310 nvkm_fifo_chan_dtor(struct nvkm_object *object)
311 {
312 	struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object);
313 	struct nvkm_fifo *fifo = chan->fifo;
314 	void *data = chan->func->dtor(chan);
315 	unsigned long flags;
316 
317 	spin_lock_irqsave(&fifo->lock, flags);
318 	if (!list_empty(&chan->head)) {
319 		__clear_bit(chan->chid, fifo->mask);
320 		list_del(&chan->head);
321 	}
322 	spin_unlock_irqrestore(&fifo->lock, flags);
323 
324 	if (chan->user)
325 		iounmap(chan->user);
326 
327 	nvkm_vm_ref(NULL, &chan->vm, NULL);
328 
329 	nvkm_gpuobj_del(&chan->push);
330 	nvkm_gpuobj_del(&chan->inst);
331 	return data;
332 }
333 
334 const struct nvkm_object_func
335 nvkm_fifo_chan_func = {
336 	.dtor = nvkm_fifo_chan_dtor,
337 	.init = nvkm_fifo_chan_init,
338 	.fini = nvkm_fifo_chan_fini,
339 	.ntfy = nvkm_fifo_chan_ntfy,
340 	.map = nvkm_fifo_chan_map,
341 	.rd32 = nvkm_fifo_chan_rd32,
342 	.wr32 = nvkm_fifo_chan_wr32,
343 	.sclass = nvkm_fifo_chan_child_get,
344 };
345 
346 int
347 nvkm_fifo_chan_ctor(const struct nvkm_fifo_chan_func *func,
348 		    struct nvkm_fifo *fifo, u32 size, u32 align, bool zero,
349 		    u64 vm, u64 push, u64 engines, int bar, u32 base, u32 user,
350 		    const struct nvkm_oclass *oclass,
351 		    struct nvkm_fifo_chan *chan)
352 {
353 	struct nvkm_client *client = oclass->client;
354 	struct nvkm_device *device = fifo->engine.subdev.device;
355 	struct nvkm_mmu *mmu = device->mmu;
356 	struct nvkm_dmaobj *dmaobj;
357 	unsigned long flags;
358 	int ret;
359 
360 	nvkm_object_ctor(&nvkm_fifo_chan_func, oclass, &chan->object);
361 	chan->func = func;
362 	chan->fifo = fifo;
363 	chan->engines = engines;
364 	INIT_LIST_HEAD(&chan->head);
365 
366 	/* instance memory */
367 	ret = nvkm_gpuobj_new(device, size, align, zero, NULL, &chan->inst);
368 	if (ret)
369 		return ret;
370 
371 	/* allocate push buffer ctxdma instance */
372 	if (push) {
373 		dmaobj = nvkm_dma_search(device->dma, oclass->client, push);
374 		if (!dmaobj)
375 			return -ENOENT;
376 
377 		ret = nvkm_object_bind(&dmaobj->object, chan->inst, -16,
378 				       &chan->push);
379 		if (ret)
380 			return ret;
381 	}
382 
383 	/* channel address space */
384 	if (!vm && mmu) {
385 		if (!client->vm || client->vm->mmu == mmu) {
386 			ret = nvkm_vm_ref(client->vm, &chan->vm, NULL);
387 			if (ret)
388 				return ret;
389 		} else {
390 			return -EINVAL;
391 		}
392 	} else {
393 		return -ENOENT;
394 	}
395 
396 	/* allocate channel id */
397 	spin_lock_irqsave(&fifo->lock, flags);
398 	chan->chid = find_first_zero_bit(fifo->mask, NVKM_FIFO_CHID_NR);
399 	if (chan->chid >= NVKM_FIFO_CHID_NR) {
400 		spin_unlock_irqrestore(&fifo->lock, flags);
401 		return -ENOSPC;
402 	}
403 	list_add(&chan->head, &fifo->chan);
404 	__set_bit(chan->chid, fifo->mask);
405 	spin_unlock_irqrestore(&fifo->lock, flags);
406 
407 	/* determine address of this channel's user registers */
408 	chan->addr = nv_device_resource_start(device, bar) +
409 		     base + user * chan->chid;
410 	chan->size = user;
411 
412 	nvkm_event_send(&fifo->cevent, 1, 0, NULL, 0);
413 	return 0;
414 }
415