1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs  * Copyright 2012 Red Hat Inc.
3c39f472eSBen Skeggs  *
4c39f472eSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs  * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs  *
11c39f472eSBen Skeggs  * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs  * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs  *
14c39f472eSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17c39f472eSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs  *
22c39f472eSBen Skeggs  * Authors: Ben Skeggs
23c39f472eSBen Skeggs  */
24c39f472eSBen Skeggs 
25c39f472eSBen Skeggs #include <core/client.h>
26c39f472eSBen Skeggs #include <core/object.h>
27c39f472eSBen Skeggs #include <core/handle.h>
28c39f472eSBen Skeggs #include <core/event.h>
29c39f472eSBen Skeggs #include <nvif/unpack.h>
30c39f472eSBen Skeggs #include <nvif/class.h>
31c39f472eSBen Skeggs #include <nvif/event.h>
32c39f472eSBen Skeggs 
33c39f472eSBen Skeggs #include <engine/dmaobj.h>
34c39f472eSBen Skeggs #include <engine/fifo.h>
35c39f472eSBen Skeggs 
36c39f472eSBen Skeggs static int
37c39f472eSBen Skeggs nouveau_fifo_event_ctor(struct nouveau_object *object, void *data, u32 size,
38c39f472eSBen Skeggs 			struct nvkm_notify *notify)
39c39f472eSBen Skeggs {
40c39f472eSBen Skeggs 	if (size == 0) {
41c39f472eSBen Skeggs 		notify->size  = 0;
42c39f472eSBen Skeggs 		notify->types = 1;
43c39f472eSBen Skeggs 		notify->index = 0;
44c39f472eSBen Skeggs 		return 0;
45c39f472eSBen Skeggs 	}
46c39f472eSBen Skeggs 	return -ENOSYS;
47c39f472eSBen Skeggs }
48c39f472eSBen Skeggs 
49c39f472eSBen Skeggs static const struct nvkm_event_func
50c39f472eSBen Skeggs nouveau_fifo_event_func = {
51c39f472eSBen Skeggs 	.ctor = nouveau_fifo_event_ctor,
52c39f472eSBen Skeggs };
53c39f472eSBen Skeggs 
54c39f472eSBen Skeggs int
55c39f472eSBen Skeggs nouveau_fifo_channel_create_(struct nouveau_object *parent,
56c39f472eSBen Skeggs 			     struct nouveau_object *engine,
57c39f472eSBen Skeggs 			     struct nouveau_oclass *oclass,
58c39f472eSBen Skeggs 			     int bar, u32 addr, u32 size, u32 pushbuf,
59c39f472eSBen Skeggs 			     u64 engmask, int len, void **ptr)
60c39f472eSBen Skeggs {
61c39f472eSBen Skeggs 	struct nouveau_device *device = nv_device(engine);
62c39f472eSBen Skeggs 	struct nouveau_fifo *priv = (void *)engine;
63c39f472eSBen Skeggs 	struct nouveau_fifo_chan *chan;
64c39f472eSBen Skeggs 	struct nouveau_dmaeng *dmaeng;
65c39f472eSBen Skeggs 	unsigned long flags;
66c39f472eSBen Skeggs 	int ret;
67c39f472eSBen Skeggs 
68c39f472eSBen Skeggs 	/* create base object class */
69c39f472eSBen Skeggs 	ret = nouveau_namedb_create_(parent, engine, oclass, 0, NULL,
70c39f472eSBen Skeggs 				     engmask, len, ptr);
71c39f472eSBen Skeggs 	chan = *ptr;
72c39f472eSBen Skeggs 	if (ret)
73c39f472eSBen Skeggs 		return ret;
74c39f472eSBen Skeggs 
75c39f472eSBen Skeggs 	/* validate dma object representing push buffer */
76c39f472eSBen Skeggs 	chan->pushdma = (void *)nouveau_handle_ref(parent, pushbuf);
77c39f472eSBen Skeggs 	if (!chan->pushdma)
78c39f472eSBen Skeggs 		return -ENOENT;
79c39f472eSBen Skeggs 
80c39f472eSBen Skeggs 	dmaeng = (void *)chan->pushdma->base.engine;
81c39f472eSBen Skeggs 	switch (chan->pushdma->base.oclass->handle) {
82c39f472eSBen Skeggs 	case NV_DMA_FROM_MEMORY:
83c39f472eSBen Skeggs 	case NV_DMA_IN_MEMORY:
84c39f472eSBen Skeggs 		break;
85c39f472eSBen Skeggs 	default:
86c39f472eSBen Skeggs 		return -EINVAL;
87c39f472eSBen Skeggs 	}
88c39f472eSBen Skeggs 
89c39f472eSBen Skeggs 	ret = dmaeng->bind(chan->pushdma, parent, &chan->pushgpu);
90c39f472eSBen Skeggs 	if (ret)
91c39f472eSBen Skeggs 		return ret;
92c39f472eSBen Skeggs 
93c39f472eSBen Skeggs 	/* find a free fifo channel */
94c39f472eSBen Skeggs 	spin_lock_irqsave(&priv->lock, flags);
95c39f472eSBen Skeggs 	for (chan->chid = priv->min; chan->chid < priv->max; chan->chid++) {
96c39f472eSBen Skeggs 		if (!priv->channel[chan->chid]) {
97c39f472eSBen Skeggs 			priv->channel[chan->chid] = nv_object(chan);
98c39f472eSBen Skeggs 			break;
99c39f472eSBen Skeggs 		}
100c39f472eSBen Skeggs 	}
101c39f472eSBen Skeggs 	spin_unlock_irqrestore(&priv->lock, flags);
102c39f472eSBen Skeggs 
103c39f472eSBen Skeggs 	if (chan->chid == priv->max) {
104c39f472eSBen Skeggs 		nv_error(priv, "no free channels\n");
105c39f472eSBen Skeggs 		return -ENOSPC;
106c39f472eSBen Skeggs 	}
107c39f472eSBen Skeggs 
108c39f472eSBen Skeggs 	chan->addr = nv_device_resource_start(device, bar) +
109c39f472eSBen Skeggs 		     addr + size * chan->chid;
110c39f472eSBen Skeggs 	chan->size = size;
111c39f472eSBen Skeggs 	nvkm_event_send(&priv->cevent, 1, 0, NULL, 0);
112c39f472eSBen Skeggs 	return 0;
113c39f472eSBen Skeggs }
114c39f472eSBen Skeggs 
115c39f472eSBen Skeggs void
116c39f472eSBen Skeggs nouveau_fifo_channel_destroy(struct nouveau_fifo_chan *chan)
117c39f472eSBen Skeggs {
118c39f472eSBen Skeggs 	struct nouveau_fifo *priv = (void *)nv_object(chan)->engine;
119c39f472eSBen Skeggs 	unsigned long flags;
120c39f472eSBen Skeggs 
121c39f472eSBen Skeggs 	if (chan->user)
122c39f472eSBen Skeggs 		iounmap(chan->user);
123c39f472eSBen Skeggs 
124c39f472eSBen Skeggs 	spin_lock_irqsave(&priv->lock, flags);
125c39f472eSBen Skeggs 	priv->channel[chan->chid] = NULL;
126c39f472eSBen Skeggs 	spin_unlock_irqrestore(&priv->lock, flags);
127c39f472eSBen Skeggs 
128c39f472eSBen Skeggs 	nouveau_gpuobj_ref(NULL, &chan->pushgpu);
129c39f472eSBen Skeggs 	nouveau_object_ref(NULL, (struct nouveau_object **)&chan->pushdma);
130c39f472eSBen Skeggs 	nouveau_namedb_destroy(&chan->namedb);
131c39f472eSBen Skeggs }
132c39f472eSBen Skeggs 
133c39f472eSBen Skeggs void
134c39f472eSBen Skeggs _nouveau_fifo_channel_dtor(struct nouveau_object *object)
135c39f472eSBen Skeggs {
136c39f472eSBen Skeggs 	struct nouveau_fifo_chan *chan = (void *)object;
137c39f472eSBen Skeggs 	nouveau_fifo_channel_destroy(chan);
138c39f472eSBen Skeggs }
139c39f472eSBen Skeggs 
140c39f472eSBen Skeggs int
141c39f472eSBen Skeggs _nouveau_fifo_channel_map(struct nouveau_object *object, u64 *addr, u32 *size)
142c39f472eSBen Skeggs {
143c39f472eSBen Skeggs 	struct nouveau_fifo_chan *chan = (void *)object;
144c39f472eSBen Skeggs 	*addr = chan->addr;
145c39f472eSBen Skeggs 	*size = chan->size;
146c39f472eSBen Skeggs 	return 0;
147c39f472eSBen Skeggs }
148c39f472eSBen Skeggs 
149c39f472eSBen Skeggs u32
150c39f472eSBen Skeggs _nouveau_fifo_channel_rd32(struct nouveau_object *object, u64 addr)
151c39f472eSBen Skeggs {
152c39f472eSBen Skeggs 	struct nouveau_fifo_chan *chan = (void *)object;
153c39f472eSBen Skeggs 	if (unlikely(!chan->user)) {
154c39f472eSBen Skeggs 		chan->user = ioremap(chan->addr, chan->size);
155c39f472eSBen Skeggs 		if (WARN_ON_ONCE(chan->user == NULL))
156c39f472eSBen Skeggs 			return 0;
157c39f472eSBen Skeggs 	}
158c39f472eSBen Skeggs 	return ioread32_native(chan->user + addr);
159c39f472eSBen Skeggs }
160c39f472eSBen Skeggs 
161c39f472eSBen Skeggs void
162c39f472eSBen Skeggs _nouveau_fifo_channel_wr32(struct nouveau_object *object, u64 addr, u32 data)
163c39f472eSBen Skeggs {
164c39f472eSBen Skeggs 	struct nouveau_fifo_chan *chan = (void *)object;
165c39f472eSBen Skeggs 	if (unlikely(!chan->user)) {
166c39f472eSBen Skeggs 		chan->user = ioremap(chan->addr, chan->size);
167c39f472eSBen Skeggs 		if (WARN_ON_ONCE(chan->user == NULL))
168c39f472eSBen Skeggs 			return;
169c39f472eSBen Skeggs 	}
170c39f472eSBen Skeggs 	iowrite32_native(data, chan->user + addr);
171c39f472eSBen Skeggs }
172c39f472eSBen Skeggs 
173c39f472eSBen Skeggs int
174c39f472eSBen Skeggs nouveau_fifo_uevent_ctor(struct nouveau_object *object, void *data, u32 size,
175c39f472eSBen Skeggs 			 struct nvkm_notify *notify)
176c39f472eSBen Skeggs {
177c39f472eSBen Skeggs 	union {
178c39f472eSBen Skeggs 		struct nvif_notify_uevent_req none;
179c39f472eSBen Skeggs 	} *req = data;
180c39f472eSBen Skeggs 	int ret;
181c39f472eSBen Skeggs 
182c39f472eSBen Skeggs 	if (nvif_unvers(req->none)) {
183c39f472eSBen Skeggs 		notify->size  = sizeof(struct nvif_notify_uevent_rep);
184c39f472eSBen Skeggs 		notify->types = 1;
185c39f472eSBen Skeggs 		notify->index = 0;
186c39f472eSBen Skeggs 	}
187c39f472eSBen Skeggs 
188c39f472eSBen Skeggs 	return ret;
189c39f472eSBen Skeggs }
190c39f472eSBen Skeggs 
191c39f472eSBen Skeggs void
192c39f472eSBen Skeggs nouveau_fifo_uevent(struct nouveau_fifo *fifo)
193c39f472eSBen Skeggs {
194c39f472eSBen Skeggs 	struct nvif_notify_uevent_rep rep = {
195c39f472eSBen Skeggs 	};
196c39f472eSBen Skeggs 	nvkm_event_send(&fifo->uevent, 1, 0, &rep, sizeof(rep));
197c39f472eSBen Skeggs }
198c39f472eSBen Skeggs 
199c39f472eSBen Skeggs int
200c39f472eSBen Skeggs _nouveau_fifo_channel_ntfy(struct nouveau_object *object, u32 type,
201c39f472eSBen Skeggs 			   struct nvkm_event **event)
202c39f472eSBen Skeggs {
203c39f472eSBen Skeggs 	struct nouveau_fifo *fifo = (void *)object->engine;
204c39f472eSBen Skeggs 	switch (type) {
205c39f472eSBen Skeggs 	case G82_CHANNEL_DMA_V0_NTFY_UEVENT:
206c39f472eSBen Skeggs 		if (nv_mclass(object) >= G82_CHANNEL_DMA) {
207c39f472eSBen Skeggs 			*event = &fifo->uevent;
208c39f472eSBen Skeggs 			return 0;
209c39f472eSBen Skeggs 		}
210c39f472eSBen Skeggs 		break;
211c39f472eSBen Skeggs 	default:
212c39f472eSBen Skeggs 		break;
213c39f472eSBen Skeggs 	}
214c39f472eSBen Skeggs 	return -EINVAL;
215c39f472eSBen Skeggs }
216c39f472eSBen Skeggs 
217c39f472eSBen Skeggs static int
218c39f472eSBen Skeggs nouveau_fifo_chid(struct nouveau_fifo *priv, struct nouveau_object *object)
219c39f472eSBen Skeggs {
220c39f472eSBen Skeggs 	int engidx = nv_hclass(priv) & 0xff;
221c39f472eSBen Skeggs 
222c39f472eSBen Skeggs 	while (object && object->parent) {
223c39f472eSBen Skeggs 		if ( nv_iclass(object->parent, NV_ENGCTX_CLASS) &&
224c39f472eSBen Skeggs 		    (nv_hclass(object->parent) & 0xff) == engidx)
225c39f472eSBen Skeggs 			return nouveau_fifo_chan(object)->chid;
226c39f472eSBen Skeggs 		object = object->parent;
227c39f472eSBen Skeggs 	}
228c39f472eSBen Skeggs 
229c39f472eSBen Skeggs 	return -1;
230c39f472eSBen Skeggs }
231c39f472eSBen Skeggs 
232c39f472eSBen Skeggs const char *
233c39f472eSBen Skeggs nouveau_client_name_for_fifo_chid(struct nouveau_fifo *fifo, u32 chid)
234c39f472eSBen Skeggs {
235c39f472eSBen Skeggs 	struct nouveau_fifo_chan *chan = NULL;
236c39f472eSBen Skeggs 	unsigned long flags;
237c39f472eSBen Skeggs 
238c39f472eSBen Skeggs 	spin_lock_irqsave(&fifo->lock, flags);
239c39f472eSBen Skeggs 	if (chid >= fifo->min && chid <= fifo->max)
240c39f472eSBen Skeggs 		chan = (void *)fifo->channel[chid];
241c39f472eSBen Skeggs 	spin_unlock_irqrestore(&fifo->lock, flags);
242c39f472eSBen Skeggs 
243c39f472eSBen Skeggs 	return nouveau_client_name(chan);
244c39f472eSBen Skeggs }
245c39f472eSBen Skeggs 
246c39f472eSBen Skeggs void
247c39f472eSBen Skeggs nouveau_fifo_destroy(struct nouveau_fifo *priv)
248c39f472eSBen Skeggs {
249c39f472eSBen Skeggs 	kfree(priv->channel);
250c39f472eSBen Skeggs 	nvkm_event_fini(&priv->uevent);
251c39f472eSBen Skeggs 	nvkm_event_fini(&priv->cevent);
252c39f472eSBen Skeggs 	nouveau_engine_destroy(&priv->base);
253c39f472eSBen Skeggs }
254c39f472eSBen Skeggs 
255c39f472eSBen Skeggs int
256c39f472eSBen Skeggs nouveau_fifo_create_(struct nouveau_object *parent,
257c39f472eSBen Skeggs 		     struct nouveau_object *engine,
258c39f472eSBen Skeggs 		     struct nouveau_oclass *oclass,
259c39f472eSBen Skeggs 		     int min, int max, int length, void **pobject)
260c39f472eSBen Skeggs {
261c39f472eSBen Skeggs 	struct nouveau_fifo *priv;
262c39f472eSBen Skeggs 	int ret;
263c39f472eSBen Skeggs 
264c39f472eSBen Skeggs 	ret = nouveau_engine_create_(parent, engine, oclass, true, "PFIFO",
265c39f472eSBen Skeggs 				     "fifo", length, pobject);
266c39f472eSBen Skeggs 	priv = *pobject;
267c39f472eSBen Skeggs 	if (ret)
268c39f472eSBen Skeggs 		return ret;
269c39f472eSBen Skeggs 
270c39f472eSBen Skeggs 	priv->min = min;
271c39f472eSBen Skeggs 	priv->max = max;
272c39f472eSBen Skeggs 	priv->channel = kzalloc(sizeof(*priv->channel) * (max + 1), GFP_KERNEL);
273c39f472eSBen Skeggs 	if (!priv->channel)
274c39f472eSBen Skeggs 		return -ENOMEM;
275c39f472eSBen Skeggs 
276c39f472eSBen Skeggs 	ret = nvkm_event_init(&nouveau_fifo_event_func, 1, 1, &priv->cevent);
277c39f472eSBen Skeggs 	if (ret)
278c39f472eSBen Skeggs 		return ret;
279c39f472eSBen Skeggs 
280c39f472eSBen Skeggs 	priv->chid = nouveau_fifo_chid;
281c39f472eSBen Skeggs 	spin_lock_init(&priv->lock);
282c39f472eSBen Skeggs 	return 0;
283c39f472eSBen Skeggs }
284