19a65a38cSBen Skeggs /*
29a65a38cSBen Skeggs  * Copyright 2012 Red Hat Inc.
39a65a38cSBen Skeggs  *
49a65a38cSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
59a65a38cSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
69a65a38cSBen Skeggs  * to deal in the Software without restriction, including without limitation
79a65a38cSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
89a65a38cSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
99a65a38cSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
109a65a38cSBen Skeggs  *
119a65a38cSBen Skeggs  * The above copyright notice and this permission notice shall be included in
129a65a38cSBen Skeggs  * all copies or substantial portions of the Software.
139a65a38cSBen Skeggs  *
149a65a38cSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
159a65a38cSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
169a65a38cSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
179a65a38cSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
189a65a38cSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
199a65a38cSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
209a65a38cSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
219a65a38cSBen Skeggs  *
229a65a38cSBen Skeggs  * Authors: Ben Skeggs
239a65a38cSBen Skeggs  */
249a65a38cSBen Skeggs #include "chan.h"
25468fae7bSBen Skeggs #include "chid.h"
26468fae7bSBen Skeggs #include "cgrp.h"
2767059b9fSBen Skeggs #include "chid.h"
28468fae7bSBen Skeggs #include "runl.h"
29f5e45689SBen Skeggs #include "priv.h"
309a65a38cSBen Skeggs 
313647c53bSBen Skeggs #include <core/ramht.h>
328f0649b5SBen Skeggs #include <subdev/mmu.h>
339a65a38cSBen Skeggs #include <engine/dma.h>
349a65a38cSBen Skeggs 
358c880fd4SBen Skeggs #include <nvif/if0020.h>
368c880fd4SBen Skeggs 
37800ac1f8SBen Skeggs const struct nvkm_event_func
38800ac1f8SBen Skeggs nvkm_chan_event = {
39800ac1f8SBen Skeggs };
40800ac1f8SBen Skeggs 
41f48dd293SBen Skeggs void
nvkm_chan_cctx_bind(struct nvkm_chan * chan,struct nvkm_engn * engn,struct nvkm_cctx * cctx)428ab849d6SBen Skeggs nvkm_chan_cctx_bind(struct nvkm_chan *chan, struct nvkm_engn *engn, struct nvkm_cctx *cctx)
43f48dd293SBen Skeggs {
443a6bc9c2SBen Skeggs 	struct nvkm_cgrp *cgrp = chan->cgrp;
453a6bc9c2SBen Skeggs 	struct nvkm_runl *runl = cgrp->runl;
468ab849d6SBen Skeggs 	struct nvkm_engine *engine = engn->engine;
478ab849d6SBen Skeggs 
488ab849d6SBen Skeggs 	if (!engn->func->bind)
498ab849d6SBen Skeggs 		return;
508ab849d6SBen Skeggs 
518ab849d6SBen Skeggs 	CHAN_TRACE(chan, "%sbind cctx %d[%s]", cctx ? "" : "un", engn->id, engine->subdev.name);
523a6bc9c2SBen Skeggs 
533a6bc9c2SBen Skeggs 	/* Prevent any channel in channel group from being rescheduled, kick them
543a6bc9c2SBen Skeggs 	 * off host and any engine(s) they're loaded on.
553a6bc9c2SBen Skeggs 	 */
563a6bc9c2SBen Skeggs 	if (cgrp->hw)
573a6bc9c2SBen Skeggs 		nvkm_runl_block(runl);
5867059b9fSBen Skeggs 	else
5967059b9fSBen Skeggs 		nvkm_chan_block(chan);
60acff9415SBen Skeggs 	nvkm_chan_preempt(chan, true);
613a6bc9c2SBen Skeggs 
62f48dd293SBen Skeggs 	/* Update context pointer. */
638ab849d6SBen Skeggs 	engn->func->bind(engn, cctx, chan);
643a6bc9c2SBen Skeggs 
653a6bc9c2SBen Skeggs 	/* Resume normal operation. */
663a6bc9c2SBen Skeggs 	if (cgrp->hw)
673a6bc9c2SBen Skeggs 		nvkm_runl_allow(runl);
6867059b9fSBen Skeggs 	else
6967059b9fSBen Skeggs 		nvkm_chan_allow(chan);
70f48dd293SBen Skeggs }
71f48dd293SBen Skeggs 
72f48dd293SBen Skeggs void
nvkm_chan_cctx_put(struct nvkm_chan * chan,struct nvkm_cctx ** pcctx)73f48dd293SBen Skeggs nvkm_chan_cctx_put(struct nvkm_chan *chan, struct nvkm_cctx **pcctx)
74f48dd293SBen Skeggs {
75f48dd293SBen Skeggs 	struct nvkm_cctx *cctx = *pcctx;
76f48dd293SBen Skeggs 
77f48dd293SBen Skeggs 	if (cctx) {
78f48dd293SBen Skeggs 		struct nvkm_engn *engn = cctx->vctx->ectx->engn;
79f48dd293SBen Skeggs 
80f48dd293SBen Skeggs 		if (refcount_dec_and_mutex_lock(&cctx->refs, &chan->cgrp->mutex)) {
81f48dd293SBen Skeggs 			CHAN_TRACE(chan, "dtor cctx %d[%s]", engn->id, engn->engine->subdev.name);
82f48dd293SBen Skeggs 			nvkm_cgrp_vctx_put(chan->cgrp, &cctx->vctx);
83f48dd293SBen Skeggs 			list_del(&cctx->head);
84f48dd293SBen Skeggs 			kfree(cctx);
85f48dd293SBen Skeggs 			mutex_unlock(&chan->cgrp->mutex);
86f48dd293SBen Skeggs 		}
87f48dd293SBen Skeggs 
88f48dd293SBen Skeggs 		*pcctx = NULL;
89f48dd293SBen Skeggs 	}
90f48dd293SBen Skeggs }
91f48dd293SBen Skeggs 
92f48dd293SBen Skeggs int
nvkm_chan_cctx_get(struct nvkm_chan * chan,struct nvkm_engn * engn,struct nvkm_cctx ** pcctx,struct nvkm_client * client)93f48dd293SBen Skeggs nvkm_chan_cctx_get(struct nvkm_chan *chan, struct nvkm_engn *engn, struct nvkm_cctx **pcctx,
94f48dd293SBen Skeggs 		   struct nvkm_client *client)
95f48dd293SBen Skeggs {
96f48dd293SBen Skeggs 	struct nvkm_cgrp *cgrp = chan->cgrp;
97f48dd293SBen Skeggs 	struct nvkm_vctx *vctx;
98f48dd293SBen Skeggs 	struct nvkm_cctx *cctx;
99f48dd293SBen Skeggs 	int ret;
100f48dd293SBen Skeggs 
101f48dd293SBen Skeggs 	/* Look for an existing channel context for this engine+VEID. */
102f48dd293SBen Skeggs 	mutex_lock(&cgrp->mutex);
103f48dd293SBen Skeggs 	cctx = nvkm_list_find(cctx, &chan->cctxs, head,
104f48dd293SBen Skeggs 			      cctx->vctx->ectx->engn == engn && cctx->vctx->vmm == chan->vmm);
105f48dd293SBen Skeggs 	if (cctx) {
106f48dd293SBen Skeggs 		refcount_inc(&cctx->refs);
107f48dd293SBen Skeggs 		*pcctx = cctx;
108f48dd293SBen Skeggs 		mutex_unlock(&chan->cgrp->mutex);
109f48dd293SBen Skeggs 		return 0;
110f48dd293SBen Skeggs 	}
111f48dd293SBen Skeggs 
112f48dd293SBen Skeggs 	/* Nope - create a fresh one.  But, sub-context first. */
113f48dd293SBen Skeggs 	ret = nvkm_cgrp_vctx_get(cgrp, engn, chan, &vctx, client);
114f48dd293SBen Skeggs 	if (ret) {
115f48dd293SBen Skeggs 		CHAN_ERROR(chan, "vctx %d[%s]: %d", engn->id, engn->engine->subdev.name, ret);
116f48dd293SBen Skeggs 		goto done;
117f48dd293SBen Skeggs 	}
118f48dd293SBen Skeggs 
119f48dd293SBen Skeggs 	/* Now, create the channel context - to track engine binding. */
120f48dd293SBen Skeggs 	CHAN_TRACE(chan, "ctor cctx %d[%s]", engn->id, engn->engine->subdev.name);
121f48dd293SBen Skeggs 	if (!(cctx = *pcctx = kzalloc(sizeof(*cctx), GFP_KERNEL))) {
122f48dd293SBen Skeggs 		nvkm_cgrp_vctx_put(cgrp, &vctx);
123f48dd293SBen Skeggs 		ret = -ENOMEM;
124f48dd293SBen Skeggs 		goto done;
125f48dd293SBen Skeggs 	}
126f48dd293SBen Skeggs 
127f48dd293SBen Skeggs 	cctx->vctx = vctx;
128f48dd293SBen Skeggs 	refcount_set(&cctx->refs, 1);
129f48dd293SBen Skeggs 	refcount_set(&cctx->uses, 0);
130f48dd293SBen Skeggs 	list_add_tail(&cctx->head, &chan->cctxs);
131f48dd293SBen Skeggs done:
132f48dd293SBen Skeggs 	mutex_unlock(&cgrp->mutex);
133f48dd293SBen Skeggs 	return ret;
134f48dd293SBen Skeggs }
135f48dd293SBen Skeggs 
136acff9415SBen Skeggs int
nvkm_chan_preempt_locked(struct nvkm_chan * chan,bool wait)137acff9415SBen Skeggs nvkm_chan_preempt_locked(struct nvkm_chan *chan, bool wait)
138acff9415SBen Skeggs {
139acff9415SBen Skeggs 	struct nvkm_runl *runl = chan->cgrp->runl;
140acff9415SBen Skeggs 
141acff9415SBen Skeggs 	CHAN_TRACE(chan, "preempt");
142acff9415SBen Skeggs 	chan->func->preempt(chan);
143acff9415SBen Skeggs 	if (!wait)
144acff9415SBen Skeggs 		return 0;
145acff9415SBen Skeggs 
146acff9415SBen Skeggs 	return nvkm_runl_preempt_wait(runl);
147acff9415SBen Skeggs }
148acff9415SBen Skeggs 
149acff9415SBen Skeggs int
nvkm_chan_preempt(struct nvkm_chan * chan,bool wait)150acff9415SBen Skeggs nvkm_chan_preempt(struct nvkm_chan *chan, bool wait)
151acff9415SBen Skeggs {
152acff9415SBen Skeggs 	int ret;
153acff9415SBen Skeggs 
154acff9415SBen Skeggs 	if (!chan->func->preempt)
155acff9415SBen Skeggs 		return 0;
156acff9415SBen Skeggs 
157acff9415SBen Skeggs 	mutex_lock(&chan->cgrp->runl->mutex);
158acff9415SBen Skeggs 	ret = nvkm_chan_preempt_locked(chan, wait);
159acff9415SBen Skeggs 	mutex_unlock(&chan->cgrp->runl->mutex);
160acff9415SBen Skeggs 	return ret;
161acff9415SBen Skeggs }
162acff9415SBen Skeggs 
163b084fff2SBen Skeggs void
nvkm_chan_remove_locked(struct nvkm_chan * chan)164b084fff2SBen Skeggs nvkm_chan_remove_locked(struct nvkm_chan *chan)
1659a65a38cSBen Skeggs {
166b084fff2SBen Skeggs 	struct nvkm_cgrp *cgrp = chan->cgrp;
167b084fff2SBen Skeggs 	struct nvkm_runl *runl = cgrp->runl;
168b084fff2SBen Skeggs 
169b084fff2SBen Skeggs 	if (list_empty(&chan->head))
170b084fff2SBen Skeggs 		return;
171b084fff2SBen Skeggs 
172b084fff2SBen Skeggs 	CHAN_TRACE(chan, "remove");
173b084fff2SBen Skeggs 	if (!--cgrp->chan_nr) {
174b084fff2SBen Skeggs 		runl->cgrp_nr--;
175b084fff2SBen Skeggs 		list_del(&cgrp->head);
176b084fff2SBen Skeggs 	}
177b084fff2SBen Skeggs 	runl->chan_nr--;
178b084fff2SBen Skeggs 	list_del_init(&chan->head);
179b084fff2SBen Skeggs 	atomic_set(&runl->changed, 1);
1808f0649b5SBen Skeggs }
1818f0649b5SBen Skeggs 
182b084fff2SBen Skeggs void
nvkm_chan_remove(struct nvkm_chan * chan,bool preempt)183b084fff2SBen Skeggs nvkm_chan_remove(struct nvkm_chan *chan, bool preempt)
1848f0649b5SBen Skeggs {
185b084fff2SBen Skeggs 	struct nvkm_runl *runl = chan->cgrp->runl;
186b084fff2SBen Skeggs 
187b084fff2SBen Skeggs 	mutex_lock(&runl->mutex);
188b084fff2SBen Skeggs 	if (preempt && chan->func->preempt)
189b084fff2SBen Skeggs 		nvkm_chan_preempt_locked(chan, true);
190b084fff2SBen Skeggs 	nvkm_chan_remove_locked(chan);
191b084fff2SBen Skeggs 	nvkm_runl_update_locked(runl, true);
192b084fff2SBen Skeggs 	mutex_unlock(&runl->mutex);
193b084fff2SBen Skeggs }
194b084fff2SBen Skeggs 
195b084fff2SBen Skeggs void
nvkm_chan_insert(struct nvkm_chan * chan)196b084fff2SBen Skeggs nvkm_chan_insert(struct nvkm_chan *chan)
197b084fff2SBen Skeggs {
198b084fff2SBen Skeggs 	struct nvkm_cgrp *cgrp = chan->cgrp;
199b084fff2SBen Skeggs 	struct nvkm_runl *runl = cgrp->runl;
200b084fff2SBen Skeggs 
201b084fff2SBen Skeggs 	mutex_lock(&runl->mutex);
202b084fff2SBen Skeggs 	if (WARN_ON(!list_empty(&chan->head))) {
203b084fff2SBen Skeggs 		mutex_unlock(&runl->mutex);
204b084fff2SBen Skeggs 		return;
205b084fff2SBen Skeggs 	}
206b084fff2SBen Skeggs 
207b084fff2SBen Skeggs 	CHAN_TRACE(chan, "insert");
208b084fff2SBen Skeggs 	list_add_tail(&chan->head, &cgrp->chans);
209b084fff2SBen Skeggs 	runl->chan_nr++;
210b084fff2SBen Skeggs 	if (!cgrp->chan_nr++) {
211b084fff2SBen Skeggs 		list_add_tail(&cgrp->head, &cgrp->runl->cgrps);
212b084fff2SBen Skeggs 		runl->cgrp_nr++;
213b084fff2SBen Skeggs 	}
214b084fff2SBen Skeggs 	atomic_set(&runl->changed, 1);
215b084fff2SBen Skeggs 	nvkm_runl_update_locked(runl, true);
216b084fff2SBen Skeggs 	mutex_unlock(&runl->mutex);
2178f0649b5SBen Skeggs }
2188f0649b5SBen Skeggs 
21967059b9fSBen Skeggs static void
nvkm_chan_block_locked(struct nvkm_chan * chan)22067059b9fSBen Skeggs nvkm_chan_block_locked(struct nvkm_chan *chan)
22167059b9fSBen Skeggs {
22267059b9fSBen Skeggs 	CHAN_TRACE(chan, "block %d", atomic_read(&chan->blocked));
22367059b9fSBen Skeggs 	if (atomic_inc_return(&chan->blocked) == 1)
22467059b9fSBen Skeggs 		chan->func->stop(chan);
22567059b9fSBen Skeggs }
22667059b9fSBen Skeggs 
22767059b9fSBen Skeggs void
nvkm_chan_error(struct nvkm_chan * chan,bool preempt)22867059b9fSBen Skeggs nvkm_chan_error(struct nvkm_chan *chan, bool preempt)
22967059b9fSBen Skeggs {
23067059b9fSBen Skeggs 	unsigned long flags;
23167059b9fSBen Skeggs 
23267059b9fSBen Skeggs 	spin_lock_irqsave(&chan->lock, flags);
23367059b9fSBen Skeggs 	if (atomic_inc_return(&chan->errored) == 1) {
23467059b9fSBen Skeggs 		CHAN_ERROR(chan, "errored - disabling channel");
23567059b9fSBen Skeggs 		nvkm_chan_block_locked(chan);
236acff9415SBen Skeggs 		if (preempt)
237acff9415SBen Skeggs 			chan->func->preempt(chan);
23867059b9fSBen Skeggs 		nvkm_event_ntfy(&chan->cgrp->runl->chid->event, chan->id, NVKM_CHAN_EVENT_ERRORED);
23967059b9fSBen Skeggs 	}
24067059b9fSBen Skeggs 	spin_unlock_irqrestore(&chan->lock, flags);
24167059b9fSBen Skeggs }
24267059b9fSBen Skeggs 
24367059b9fSBen Skeggs void
nvkm_chan_block(struct nvkm_chan * chan)24467059b9fSBen Skeggs nvkm_chan_block(struct nvkm_chan *chan)
24567059b9fSBen Skeggs {
24667059b9fSBen Skeggs 	spin_lock_irq(&chan->lock);
24767059b9fSBen Skeggs 	nvkm_chan_block_locked(chan);
24867059b9fSBen Skeggs 	spin_unlock_irq(&chan->lock);
24967059b9fSBen Skeggs }
25067059b9fSBen Skeggs 
25167059b9fSBen Skeggs void
nvkm_chan_allow(struct nvkm_chan * chan)25267059b9fSBen Skeggs nvkm_chan_allow(struct nvkm_chan *chan)
25367059b9fSBen Skeggs {
25467059b9fSBen Skeggs 	spin_lock_irq(&chan->lock);
25567059b9fSBen Skeggs 	CHAN_TRACE(chan, "allow %d", atomic_read(&chan->blocked));
25667059b9fSBen Skeggs 	if (atomic_dec_and_test(&chan->blocked))
25767059b9fSBen Skeggs 		chan->func->start(chan);
25867059b9fSBen Skeggs 	spin_unlock_irq(&chan->lock);
25967059b9fSBen Skeggs }
26067059b9fSBen Skeggs 
261f5e45689SBen Skeggs void
nvkm_chan_del(struct nvkm_chan ** pchan)262f5e45689SBen Skeggs nvkm_chan_del(struct nvkm_chan **pchan)
263f5e45689SBen Skeggs {
264f5e45689SBen Skeggs 	struct nvkm_chan *chan = *pchan;
265f5e45689SBen Skeggs 
266f5e45689SBen Skeggs 	if (!chan)
267f5e45689SBen Skeggs 		return;
268f5e45689SBen Skeggs 
2693647c53bSBen Skeggs 	if (chan->func->ramfc->clear)
2703647c53bSBen Skeggs 		chan->func->ramfc->clear(chan);
2713647c53bSBen Skeggs 
2723647c53bSBen Skeggs 	nvkm_ramht_del(&chan->ramht);
2733647c53bSBen Skeggs 	nvkm_gpuobj_del(&chan->pgd);
2743647c53bSBen Skeggs 	nvkm_gpuobj_del(&chan->eng);
2753647c53bSBen Skeggs 	nvkm_gpuobj_del(&chan->cache);
2763647c53bSBen Skeggs 	nvkm_gpuobj_del(&chan->ramfc);
2773647c53bSBen Skeggs 
278fbe9f433SBen Skeggs 	nvkm_memory_unref(&chan->userd.mem);
279fbe9f433SBen Skeggs 
280468fae7bSBen Skeggs 	if (chan->cgrp) {
281468fae7bSBen Skeggs 		nvkm_chid_put(chan->cgrp->runl->chid, chan->id, &chan->cgrp->lock);
282468fae7bSBen Skeggs 		nvkm_cgrp_unref(&chan->cgrp);
283468fae7bSBen Skeggs 	}
284468fae7bSBen Skeggs 
285f66c57d9SBen Skeggs 	if (chan->vmm) {
286f66c57d9SBen Skeggs 		nvkm_vmm_part(chan->vmm, chan->inst->memory);
287f66c57d9SBen Skeggs 		nvkm_vmm_unref(&chan->vmm);
288f66c57d9SBen Skeggs 	}
2899a65a38cSBen Skeggs 
2908f0649b5SBen Skeggs 	nvkm_gpuobj_del(&chan->push);
2918f0649b5SBen Skeggs 	nvkm_gpuobj_del(&chan->inst);
292*06db7fdeSBen Skeggs 	kfree(chan);
2939a65a38cSBen Skeggs }
2949a65a38cSBen Skeggs 
295c358f538SBen Skeggs void
nvkm_chan_put(struct nvkm_chan ** pchan,unsigned long irqflags)296c358f538SBen Skeggs nvkm_chan_put(struct nvkm_chan **pchan, unsigned long irqflags)
297c358f538SBen Skeggs {
298c358f538SBen Skeggs 	struct nvkm_chan *chan = *pchan;
299c358f538SBen Skeggs 
300c358f538SBen Skeggs 	if (!chan)
301c358f538SBen Skeggs 		return;
302c358f538SBen Skeggs 
303c358f538SBen Skeggs 	*pchan = NULL;
304c358f538SBen Skeggs 	spin_unlock_irqrestore(&chan->cgrp->lock, irqflags);
305c358f538SBen Skeggs }
306c358f538SBen Skeggs 
307c358f538SBen Skeggs struct nvkm_chan *
nvkm_chan_get_inst(struct nvkm_engine * engine,u64 inst,unsigned long * pirqflags)308c358f538SBen Skeggs nvkm_chan_get_inst(struct nvkm_engine *engine, u64 inst, unsigned long *pirqflags)
309c358f538SBen Skeggs {
310c358f538SBen Skeggs 	struct nvkm_fifo *fifo = engine->subdev.device->fifo;
311c358f538SBen Skeggs 	struct nvkm_runl *runl;
312c358f538SBen Skeggs 	struct nvkm_engn *engn;
313c358f538SBen Skeggs 	struct nvkm_chan *chan;
314c358f538SBen Skeggs 
315c358f538SBen Skeggs 	nvkm_runl_foreach(runl, fifo) {
316c358f538SBen Skeggs 		nvkm_runl_foreach_engn(engn, runl) {
317c358f538SBen Skeggs 			if (engine == &fifo->engine || engn->engine == engine) {
318c358f538SBen Skeggs 				chan = nvkm_runl_chan_get_inst(runl, inst, pirqflags);
319c358f538SBen Skeggs 				if (chan || engn->engine == engine)
320c358f538SBen Skeggs 					return chan;
321c358f538SBen Skeggs 			}
322c358f538SBen Skeggs 		}
323c358f538SBen Skeggs 	}
324c358f538SBen Skeggs 
325c358f538SBen Skeggs 	return NULL;
326c358f538SBen Skeggs }
327c358f538SBen Skeggs 
328c358f538SBen Skeggs struct nvkm_chan *
nvkm_chan_get_chid(struct nvkm_engine * engine,int id,unsigned long * pirqflags)329c358f538SBen Skeggs nvkm_chan_get_chid(struct nvkm_engine *engine, int id, unsigned long *pirqflags)
330c358f538SBen Skeggs {
331c358f538SBen Skeggs 	struct nvkm_fifo *fifo = engine->subdev.device->fifo;
332c358f538SBen Skeggs 	struct nvkm_runl *runl;
333c358f538SBen Skeggs 	struct nvkm_engn *engn;
334c358f538SBen Skeggs 
335c358f538SBen Skeggs 	nvkm_runl_foreach(runl, fifo) {
336c358f538SBen Skeggs 		nvkm_runl_foreach_engn(engn, runl) {
337c358f538SBen Skeggs 			if (fifo->chid || engn->engine == engine)
338c358f538SBen Skeggs 				return nvkm_runl_chan_get_chid(runl, id, pirqflags);
339c358f538SBen Skeggs 		}
340c358f538SBen Skeggs 	}
341c358f538SBen Skeggs 
342c358f538SBen Skeggs 	return NULL;
343c358f538SBen Skeggs }
344c358f538SBen Skeggs 
3459a65a38cSBen Skeggs int
nvkm_chan_new_(const struct nvkm_chan_func * func,struct nvkm_runl * runl,int runq,struct nvkm_cgrp * cgrp,const char * name,bool priv,u32 devm,struct nvkm_vmm * vmm,struct nvkm_dmaobj * dmaobj,u64 offset,u64 length,struct nvkm_memory * userd,u64 ouserd,struct nvkm_chan ** pchan)346*06db7fdeSBen Skeggs nvkm_chan_new_(const struct nvkm_chan_func *func, struct nvkm_runl *runl, int runq,
347*06db7fdeSBen Skeggs 	       struct nvkm_cgrp *cgrp, const char *name, bool priv, u32 devm, struct nvkm_vmm *vmm,
348*06db7fdeSBen Skeggs 	       struct nvkm_dmaobj *dmaobj, u64 offset, u64 length,
349*06db7fdeSBen Skeggs 	       struct nvkm_memory *userd, u64 ouserd, struct nvkm_chan **pchan)
3509a65a38cSBen Skeggs {
351*06db7fdeSBen Skeggs 	struct nvkm_fifo *fifo = runl->fifo;
3528f0649b5SBen Skeggs 	struct nvkm_device *device = fifo->engine.subdev.device;
353*06db7fdeSBen Skeggs 	struct nvkm_chan *chan;
3549a65a38cSBen Skeggs 	int ret;
3559a65a38cSBen Skeggs 
356*06db7fdeSBen Skeggs 	/* Validate arguments against class requirements. */
357*06db7fdeSBen Skeggs 	if ((runq && runq >= runl->func->runqs) ||
358*06db7fdeSBen Skeggs 	    (!func->inst->vmm != !vmm) ||
359*06db7fdeSBen Skeggs 	    ((func->userd->bar < 0) == !userd) ||
360*06db7fdeSBen Skeggs 	    (!func->ramfc->ctxdma != !dmaobj) ||
361*06db7fdeSBen Skeggs 	    ((func->ramfc->devm < devm) && devm != BIT(0)) ||
362*06db7fdeSBen Skeggs 	    (!func->ramfc->priv && priv)) {
363*06db7fdeSBen Skeggs 		RUNL_DEBUG(runl, "args runq:%d:%d vmm:%d:%p userd:%d:%p "
364*06db7fdeSBen Skeggs 				 "push:%d:%p devm:%08x:%08x priv:%d:%d",
365*06db7fdeSBen Skeggs 			   runl->func->runqs, runq, func->inst->vmm, vmm,
366*06db7fdeSBen Skeggs 			   func->userd->bar < 0, userd, func->ramfc->ctxdma, dmaobj,
367*06db7fdeSBen Skeggs 			   func->ramfc->devm, devm, func->ramfc->priv, priv);
368*06db7fdeSBen Skeggs 		return -EINVAL;
369468fae7bSBen Skeggs 	}
370468fae7bSBen Skeggs 
371*06db7fdeSBen Skeggs 	if (!(chan = *pchan = kzalloc(sizeof(*chan), GFP_KERNEL)))
372f5e45689SBen Skeggs 		return -ENOMEM;
373f5e45689SBen Skeggs 
3748f0649b5SBen Skeggs 	chan->func = func;
375*06db7fdeSBen Skeggs 	strscpy(chan->name, name, sizeof(chan->name));
376*06db7fdeSBen Skeggs 	chan->runq = runq;
377468fae7bSBen Skeggs 	chan->id = -1;
37867059b9fSBen Skeggs 	spin_lock_init(&chan->lock);
37967059b9fSBen Skeggs 	atomic_set(&chan->blocked, 1);
38067059b9fSBen Skeggs 	atomic_set(&chan->errored, 0);
381f48dd293SBen Skeggs 	INIT_LIST_HEAD(&chan->cctxs);
3828f0649b5SBen Skeggs 	INIT_LIST_HEAD(&chan->head);
3838f0649b5SBen Skeggs 
384468fae7bSBen Skeggs 	/* Join channel group.
385468fae7bSBen Skeggs 	 *
386468fae7bSBen Skeggs 	 * GK110 and newer support channel groups (aka TSGs), where individual channels
387468fae7bSBen Skeggs 	 * share a timeslice, and, engine context(s).
388468fae7bSBen Skeggs 	 *
389468fae7bSBen Skeggs 	 * As such, engine contexts are tracked in nvkm_cgrp and we need them even when
390468fae7bSBen Skeggs 	 * channels aren't in an API channel group, and on HW that doesn't support TSGs.
391468fae7bSBen Skeggs 	 */
392468fae7bSBen Skeggs 	if (!cgrp) {
393468fae7bSBen Skeggs 		ret = nvkm_cgrp_new(runl, chan->name, vmm, fifo->func->cgrp.force, &chan->cgrp);
394468fae7bSBen Skeggs 		if (ret) {
395468fae7bSBen Skeggs 			RUNL_DEBUG(runl, "cgrp %d", ret);
396468fae7bSBen Skeggs 			return ret;
397468fae7bSBen Skeggs 		}
398468fae7bSBen Skeggs 
399468fae7bSBen Skeggs 		cgrp = chan->cgrp;
400468fae7bSBen Skeggs 	} else {
401468fae7bSBen Skeggs 		if (cgrp->runl != runl || cgrp->vmm != vmm) {
402468fae7bSBen Skeggs 			RUNL_DEBUG(runl, "cgrp %d %d", cgrp->runl != runl, cgrp->vmm != vmm);
403468fae7bSBen Skeggs 			return -EINVAL;
404468fae7bSBen Skeggs 		}
405468fae7bSBen Skeggs 
406468fae7bSBen Skeggs 		chan->cgrp = nvkm_cgrp_ref(cgrp);
407468fae7bSBen Skeggs 	}
408468fae7bSBen Skeggs 
409d3e7a439SBen Skeggs 	/* Allocate instance block. */
410d3e7a439SBen Skeggs 	ret = nvkm_gpuobj_new(device, func->inst->size, 0x1000, func->inst->zero, NULL,
411d3e7a439SBen Skeggs 			      &chan->inst);
412d3e7a439SBen Skeggs 	if (ret) {
413d3e7a439SBen Skeggs 		RUNL_DEBUG(runl, "inst %d", ret);
4149a65a38cSBen Skeggs 		return ret;
415d3e7a439SBen Skeggs 	}
416d3e7a439SBen Skeggs 
417d3e7a439SBen Skeggs 	/* Initialise virtual address-space. */
418d3e7a439SBen Skeggs 	if (func->inst->vmm) {
419d3e7a439SBen Skeggs 		if (WARN_ON(vmm->mmu != device->mmu))
420d3e7a439SBen Skeggs 			return -EINVAL;
421d3e7a439SBen Skeggs 
422d3e7a439SBen Skeggs 		ret = nvkm_vmm_join(vmm, chan->inst->memory);
423d3e7a439SBen Skeggs 		if (ret) {
424d3e7a439SBen Skeggs 			RUNL_DEBUG(runl, "vmm %d", ret);
425d3e7a439SBen Skeggs 			return ret;
426d3e7a439SBen Skeggs 		}
427d3e7a439SBen Skeggs 
428d3e7a439SBen Skeggs 		chan->vmm = nvkm_vmm_ref(vmm);
429d3e7a439SBen Skeggs 	}
4309a65a38cSBen Skeggs 
4313647c53bSBen Skeggs 	/* Allocate HW ctxdma for push buffer. */
4323647c53bSBen Skeggs 	if (func->ramfc->ctxdma) {
4333647c53bSBen Skeggs 		ret = nvkm_object_bind(&dmaobj->object, chan->inst, -16, &chan->push);
4343647c53bSBen Skeggs 		if (ret) {
4353647c53bSBen Skeggs 			RUNL_DEBUG(runl, "bind %d", ret);
4369a65a38cSBen Skeggs 			return ret;
4379a65a38cSBen Skeggs 		}
4383647c53bSBen Skeggs 	}
4399a65a38cSBen Skeggs 
440468fae7bSBen Skeggs 	/* Allocate channel ID. */
441468fae7bSBen Skeggs 	chan->id = nvkm_chid_get(runl->chid, chan);
442468fae7bSBen Skeggs 	if (chan->id < 0) {
443468fae7bSBen Skeggs 		RUNL_ERROR(runl, "!chids");
4449a65a38cSBen Skeggs 		return -ENOSPC;
4459a65a38cSBen Skeggs 	}
446468fae7bSBen Skeggs 
447b084fff2SBen Skeggs 	if (cgrp->id < 0)
448b084fff2SBen Skeggs 		cgrp->id = chan->id;
4499a65a38cSBen Skeggs 
450fbe9f433SBen Skeggs 	/* Initialise USERD. */
451*06db7fdeSBen Skeggs 	if (func->userd->bar < 0) {
452*06db7fdeSBen Skeggs 		if (ouserd + chan->func->userd->size >= nvkm_memory_size(userd)) {
453*06db7fdeSBen Skeggs 			RUNL_DEBUG(runl, "ouserd %llx", ouserd);
454*06db7fdeSBen Skeggs 			return -EINVAL;
455*06db7fdeSBen Skeggs 		}
456*06db7fdeSBen Skeggs 
457*06db7fdeSBen Skeggs 		ret = nvkm_memory_kmap(userd, &chan->userd.mem);
458*06db7fdeSBen Skeggs 		if (ret) {
459*06db7fdeSBen Skeggs 			RUNL_DEBUG(runl, "userd %d", ret);
460*06db7fdeSBen Skeggs 			return ret;
461*06db7fdeSBen Skeggs 		}
462*06db7fdeSBen Skeggs 
463*06db7fdeSBen Skeggs 		chan->userd.base = ouserd;
464*06db7fdeSBen Skeggs 	} else {
465fbe9f433SBen Skeggs 		chan->userd.mem = nvkm_memory_ref(fifo->userd.mem);
466fbe9f433SBen Skeggs 		chan->userd.base = chan->id * chan->func->userd->size;
467fbe9f433SBen Skeggs 	}
468fbe9f433SBen Skeggs 
469fbe9f433SBen Skeggs 	if (chan->func->userd->clear)
470fbe9f433SBen Skeggs 		chan->func->userd->clear(chan);
471fbe9f433SBen Skeggs 
472*06db7fdeSBen Skeggs 	/* Initialise RAMFC. */
473*06db7fdeSBen Skeggs 	ret = chan->func->ramfc->write(chan, offset, length, devm, priv);
474*06db7fdeSBen Skeggs 	if (ret) {
475*06db7fdeSBen Skeggs 		RUNL_DEBUG(runl, "ramfc %d", ret);
476*06db7fdeSBen Skeggs 		return ret;
477*06db7fdeSBen Skeggs 	}
478*06db7fdeSBen Skeggs 
4799a65a38cSBen Skeggs 	return 0;
4809a65a38cSBen Skeggs }
481