1 | /* $NetBSD: nouveau_engine_fifo_base.c,v 1.5 2016/05/11 02:28:33 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_engine_fifo_base.c,v 1.5 2016/05/11 02:28:33 riastradh Exp $" ); |
29 | |
30 | #include <core/client.h> |
31 | #include <core/object.h> |
32 | #include <core/handle.h> |
33 | #include <core/event.h> |
34 | #include <core/class.h> |
35 | |
36 | #include <engine/dmaobj.h> |
37 | #include <engine/fifo.h> |
38 | |
39 | int |
40 | nouveau_fifo_channel_create_(struct nouveau_object *parent, |
41 | struct nouveau_object *engine, |
42 | struct nouveau_oclass *oclass, |
43 | int bar, u32 addr, u32 size, u32 pushbuf, |
44 | u64 engmask, int len, void **ptr) |
45 | { |
46 | struct nouveau_device *device = nv_device(engine); |
47 | struct nouveau_fifo *priv = (void *)engine; |
48 | struct nouveau_fifo_chan *chan; |
49 | struct nouveau_dmaeng *dmaeng; |
50 | unsigned long flags; |
51 | int ret; |
52 | |
53 | /* create base object class */ |
54 | ret = nouveau_namedb_create_(parent, engine, oclass, 0, NULL, |
55 | engmask, len, ptr); |
56 | chan = *ptr; |
57 | if (ret) |
58 | return ret; |
59 | |
60 | /* validate dma object representing push buffer */ |
61 | chan->pushdma = (void *)nouveau_handle_ref(parent, pushbuf); |
62 | if (!chan->pushdma) |
63 | return -ENOENT; |
64 | |
65 | dmaeng = (void *)chan->pushdma->base.engine; |
66 | switch (chan->pushdma->base.oclass->handle) { |
67 | case NV_DMA_FROM_MEMORY_CLASS: |
68 | case NV_DMA_IN_MEMORY_CLASS: |
69 | break; |
70 | default: |
71 | return -EINVAL; |
72 | } |
73 | |
74 | ret = dmaeng->bind(dmaeng, parent, chan->pushdma, &chan->pushgpu); |
75 | if (ret) |
76 | return ret; |
77 | |
78 | /* find a free fifo channel */ |
79 | spin_lock_irqsave(&priv->lock, flags); |
80 | for (chan->chid = priv->min; chan->chid < priv->max; chan->chid++) { |
81 | if (!priv->channel[chan->chid]) { |
82 | priv->channel[chan->chid] = nv_object(chan); |
83 | break; |
84 | } |
85 | } |
86 | spin_unlock_irqrestore(&priv->lock, flags); |
87 | |
88 | if (chan->chid == priv->max) { |
89 | nv_error(priv, "no free channels\n" ); |
90 | return -ENOSPC; |
91 | } |
92 | |
93 | /* map fifo control registers */ |
94 | #ifdef __NetBSD__ |
95 | if (bar == 0) { |
96 | /* |
97 | * We already map BAR 0 in the engine device base, so |
98 | * grab a subregion of that. |
99 | */ |
100 | bus_space_tag_t mmiot = nv_subdev(device)->mmiot; |
101 | bus_space_handle_t mmioh = nv_subdev(device)->mmioh; |
102 | bus_size_t mmiosz = nv_subdev(device)->mmiosz; |
103 | |
104 | /* Check whether it lies inside the region. */ |
105 | if (mmiosz < addr || |
106 | mmiosz - addr < chan->chid*size || |
107 | mmiosz - addr - chan->chid*size < size) { |
108 | ret = EIO; |
109 | nv_error(priv, "fifo channel out of range:" |
110 | " addr 0x%" PRIxMAX |
111 | " chid 0x%" PRIxMAX" size 0x%" PRIxMAX |
112 | " mmiosz 0x%" PRIxMAX"\n" , |
113 | (uintmax_t)addr, |
114 | (uintmax_t)chan->chid, (uintmax_t)size, |
115 | (uintmax_t)mmiosz); |
116 | return ret; |
117 | } |
118 | |
119 | /* Grab a subregion. */ |
120 | /* XXX errno NetBSD->Linux */ |
121 | ret = -bus_space_subregion(mmiot, mmioh, |
122 | (addr + chan->chid*size), size, &chan->bsh); |
123 | if (ret) { |
124 | nv_error(priv, "bus_space_subregion failed: %d\n" , |
125 | ret); |
126 | return ret; |
127 | } |
128 | |
129 | /* Success! No need to unmap a subregion. */ |
130 | chan->mapped = false; |
131 | chan->bst = mmiot; |
132 | } else { |
133 | chan->bst = nv_device_resource_tag(device, bar); |
134 | /* XXX errno NetBSD->Linux */ |
135 | ret = -bus_space_map(chan->bst, |
136 | (nv_device_resource_start(device, bar) + |
137 | addr + (chan->chid * size)), |
138 | size, 0, &chan->bsh); |
139 | if (ret) { |
140 | nv_error(priv, "failed to map fifo channel:" |
141 | " bar %d addr %" PRIxMAX" + %" PRIxMAX |
142 | " + (%" PRIxMAX" * %" PRIxMAX") = %" PRIxMAX |
143 | " size %" PRIxMAX": %d\n" , |
144 | bar, |
145 | (uintmax_t)nv_device_resource_start(device, bar), |
146 | (uintmax_t)addr, |
147 | (uintmax_t)chan->chid, |
148 | (uintmax_t)size, |
149 | (uintmax_t)(nv_device_resource_start(device, bar) + |
150 | addr + (chan->chid * size)), |
151 | (uintmax_t)size, |
152 | ret); |
153 | return ret; |
154 | } |
155 | chan->mapped = true; |
156 | } |
157 | #else |
158 | chan->user = ioremap(nv_device_resource_start(device, bar) + addr + |
159 | (chan->chid * size), size); |
160 | if (!chan->user) |
161 | return -EFAULT; |
162 | #endif |
163 | |
164 | nouveau_event_trigger(priv->cevent, 0); |
165 | |
166 | chan->size = size; |
167 | return 0; |
168 | } |
169 | |
170 | void |
171 | nouveau_fifo_channel_destroy(struct nouveau_fifo_chan *chan) |
172 | { |
173 | struct nouveau_fifo *priv = (void *)nv_object(chan)->engine; |
174 | unsigned long flags; |
175 | |
176 | #ifdef __NetBSD__ |
177 | if (chan->mapped) { |
178 | bus_space_unmap(chan->bst, chan->bsh, chan->size); |
179 | chan->mapped = false; |
180 | } |
181 | #else |
182 | iounmap(chan->user); |
183 | #endif |
184 | |
185 | spin_lock_irqsave(&priv->lock, flags); |
186 | priv->channel[chan->chid] = NULL; |
187 | spin_unlock_irqrestore(&priv->lock, flags); |
188 | |
189 | nouveau_gpuobj_ref(NULL, &chan->pushgpu); |
190 | nouveau_object_ref(NULL, (struct nouveau_object **)&chan->pushdma); |
191 | nouveau_namedb_destroy(&chan->base); |
192 | } |
193 | |
194 | void |
195 | _nouveau_fifo_channel_dtor(struct nouveau_object *object) |
196 | { |
197 | struct nouveau_fifo_chan *chan = (void *)object; |
198 | nouveau_fifo_channel_destroy(chan); |
199 | } |
200 | |
201 | u32 |
202 | _nouveau_fifo_channel_rd32(struct nouveau_object *object, u64 addr) |
203 | { |
204 | struct nouveau_fifo_chan *chan = (void *)object; |
205 | #ifdef __NetBSD__ |
206 | return bus_space_read_4(chan->bst, chan->bsh, addr); |
207 | #else |
208 | return ioread32_native(chan->user + addr); |
209 | #endif |
210 | } |
211 | |
212 | void |
213 | _nouveau_fifo_channel_wr32(struct nouveau_object *object, u64 addr, u32 data) |
214 | { |
215 | struct nouveau_fifo_chan *chan = (void *)object; |
216 | #ifdef __NetBSD__ |
217 | bus_space_write_4(chan->bst, chan->bsh, addr, data); |
218 | #else |
219 | iowrite32_native(data, chan->user + addr); |
220 | #endif |
221 | } |
222 | |
223 | static int |
224 | nouveau_fifo_chid(struct nouveau_fifo *priv, struct nouveau_object *object) |
225 | { |
226 | int engidx = nv_hclass(priv) & 0xff; |
227 | |
228 | while (object && object->parent) { |
229 | if ( nv_iclass(object->parent, NV_ENGCTX_CLASS) && |
230 | (nv_hclass(object->parent) & 0xff) == engidx) |
231 | return nouveau_fifo_chan(object)->chid; |
232 | object = object->parent; |
233 | } |
234 | |
235 | return -1; |
236 | } |
237 | |
238 | const char * |
239 | nouveau_client_name_for_fifo_chid(struct nouveau_fifo *fifo, u32 chid) |
240 | { |
241 | struct nouveau_fifo_chan *chan = NULL; |
242 | unsigned long flags; |
243 | |
244 | spin_lock_irqsave(&fifo->lock, flags); |
245 | if (chid >= fifo->min && chid <= fifo->max) |
246 | chan = (void *)fifo->channel[chid]; |
247 | spin_unlock_irqrestore(&fifo->lock, flags); |
248 | |
249 | return nouveau_client_name(chan); |
250 | } |
251 | |
252 | void |
253 | nouveau_fifo_destroy(struct nouveau_fifo *priv) |
254 | { |
255 | kfree(priv->channel); |
256 | nouveau_event_destroy(&priv->uevent); |
257 | nouveau_event_destroy(&priv->cevent); |
258 | nouveau_engine_destroy(&priv->base); |
259 | } |
260 | |
261 | int |
262 | nouveau_fifo_create_(struct nouveau_object *parent, |
263 | struct nouveau_object *engine, |
264 | struct nouveau_oclass *oclass, |
265 | int min, int max, int length, void **pobject) |
266 | { |
267 | struct nouveau_fifo *priv; |
268 | int ret; |
269 | |
270 | ret = nouveau_engine_create_(parent, engine, oclass, true, "PFIFO" , |
271 | "fifo" , length, pobject); |
272 | priv = *pobject; |
273 | if (ret) |
274 | return ret; |
275 | |
276 | priv->min = min; |
277 | priv->max = max; |
278 | priv->channel = kzalloc(sizeof(*priv->channel) * (max + 1), GFP_KERNEL); |
279 | if (!priv->channel) |
280 | return -ENOMEM; |
281 | |
282 | ret = nouveau_event_create(1, &priv->cevent); |
283 | if (ret) |
284 | return ret; |
285 | |
286 | ret = nouveau_event_create(1, &priv->uevent); |
287 | if (ret) |
288 | return ret; |
289 | |
290 | priv->chid = nouveau_fifo_chid; |
291 | spin_lock_init(&priv->lock); |
292 | return 0; |
293 | } |
294 | |