Commit | Line | Data |
---|---|---|
ebb945a9 BS |
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 | ||
a2896ced | 25 | #include <core/client.h> |
ebb945a9 BS |
26 | #include <core/object.h> |
27 | #include <core/handle.h> | |
9bd2ddba | 28 | #include <core/event.h> |
6c1689a0 | 29 | #include <core/class.h> |
ebb945a9 BS |
30 | |
31 | #include <engine/dmaobj.h> | |
32 | #include <engine/fifo.h> | |
33 | ||
34 | int | |
35 | nouveau_fifo_channel_create_(struct nouveau_object *parent, | |
36 | struct nouveau_object *engine, | |
37 | struct nouveau_oclass *oclass, | |
38 | int bar, u32 addr, u32 size, u32 pushbuf, | |
507ceb15 | 39 | u64 engmask, int len, void **ptr) |
ebb945a9 BS |
40 | { |
41 | struct nouveau_device *device = nv_device(engine); | |
42 | struct nouveau_fifo *priv = (void *)engine; | |
43 | struct nouveau_fifo_chan *chan; | |
44 | struct nouveau_dmaeng *dmaeng; | |
45 | unsigned long flags; | |
46 | int ret; | |
47 | ||
48 | /* create base object class */ | |
49 | ret = nouveau_namedb_create_(parent, engine, oclass, 0, NULL, | |
50 | engmask, len, ptr); | |
51 | chan = *ptr; | |
52 | if (ret) | |
53 | return ret; | |
54 | ||
55 | /* validate dma object representing push buffer */ | |
56 | chan->pushdma = (void *)nouveau_handle_ref(parent, pushbuf); | |
57 | if (!chan->pushdma) | |
58 | return -ENOENT; | |
59 | ||
60 | dmaeng = (void *)chan->pushdma->base.engine; | |
61 | switch (chan->pushdma->base.oclass->handle) { | |
6c1689a0 BS |
62 | case NV_DMA_FROM_MEMORY_CLASS: |
63 | case NV_DMA_IN_MEMORY_CLASS: | |
ebb945a9 BS |
64 | break; |
65 | default: | |
66 | return -EINVAL; | |
67 | } | |
68 | ||
82d23aea BS |
69 | ret = dmaeng->bind(dmaeng, parent, chan->pushdma, &chan->pushgpu); |
70 | if (ret) | |
71 | return ret; | |
ebb945a9 BS |
72 | |
73 | /* find a free fifo channel */ | |
74 | spin_lock_irqsave(&priv->lock, flags); | |
75 | for (chan->chid = priv->min; chan->chid < priv->max; chan->chid++) { | |
76 | if (!priv->channel[chan->chid]) { | |
77 | priv->channel[chan->chid] = nv_object(chan); | |
78 | break; | |
79 | } | |
80 | } | |
81 | spin_unlock_irqrestore(&priv->lock, flags); | |
82 | ||
83 | if (chan->chid == priv->max) { | |
84 | nv_error(priv, "no free channels\n"); | |
85 | return -ENOSPC; | |
86 | } | |
87 | ||
88 | /* map fifo control registers */ | |
420b9469 | 89 | chan->user = ioremap(nv_device_resource_start(device, bar) + addr + |
ebb945a9 BS |
90 | (chan->chid * size), size); |
91 | if (!chan->user) | |
92 | return -EFAULT; | |
93 | ||
893e90c5 BS |
94 | nouveau_event_trigger(priv->cevent, 0); |
95 | ||
ebb945a9 BS |
96 | chan->size = size; |
97 | return 0; | |
98 | } | |
99 | ||
100 | void | |
101 | nouveau_fifo_channel_destroy(struct nouveau_fifo_chan *chan) | |
102 | { | |
103 | struct nouveau_fifo *priv = (void *)nv_object(chan)->engine; | |
104 | unsigned long flags; | |
105 | ||
106 | iounmap(chan->user); | |
107 | ||
108 | spin_lock_irqsave(&priv->lock, flags); | |
109 | priv->channel[chan->chid] = NULL; | |
110 | spin_unlock_irqrestore(&priv->lock, flags); | |
111 | ||
112 | nouveau_gpuobj_ref(NULL, &chan->pushgpu); | |
113 | nouveau_object_ref(NULL, (struct nouveau_object **)&chan->pushdma); | |
114 | nouveau_namedb_destroy(&chan->base); | |
115 | } | |
116 | ||
117 | void | |
118 | _nouveau_fifo_channel_dtor(struct nouveau_object *object) | |
119 | { | |
120 | struct nouveau_fifo_chan *chan = (void *)object; | |
121 | nouveau_fifo_channel_destroy(chan); | |
122 | } | |
123 | ||
124 | u32 | |
0a32241d | 125 | _nouveau_fifo_channel_rd32(struct nouveau_object *object, u64 addr) |
ebb945a9 BS |
126 | { |
127 | struct nouveau_fifo_chan *chan = (void *)object; | |
128 | return ioread32_native(chan->user + addr); | |
129 | } | |
130 | ||
131 | void | |
0a32241d | 132 | _nouveau_fifo_channel_wr32(struct nouveau_object *object, u64 addr, u32 data) |
ebb945a9 BS |
133 | { |
134 | struct nouveau_fifo_chan *chan = (void *)object; | |
135 | iowrite32_native(data, chan->user + addr); | |
136 | } | |
137 | ||
5b8a43ae | 138 | static int |
0c5b8cec BS |
139 | nouveau_fifo_chid(struct nouveau_fifo *priv, struct nouveau_object *object) |
140 | { | |
141 | int engidx = nv_hclass(priv) & 0xff; | |
142 | ||
143 | while (object && object->parent) { | |
144 | if ( nv_iclass(object->parent, NV_ENGCTX_CLASS) && | |
145 | (nv_hclass(object->parent) & 0xff) == engidx) | |
146 | return nouveau_fifo_chan(object)->chid; | |
147 | object = object->parent; | |
148 | } | |
149 | ||
150 | return -1; | |
151 | } | |
152 | ||
a2896ced MS |
153 | const char * |
154 | nouveau_client_name_for_fifo_chid(struct nouveau_fifo *fifo, u32 chid) | |
155 | { | |
156 | struct nouveau_fifo_chan *chan = NULL; | |
157 | unsigned long flags; | |
158 | ||
159 | spin_lock_irqsave(&fifo->lock, flags); | |
160 | if (chid >= fifo->min && chid <= fifo->max) | |
161 | chan = (void *)fifo->channel[chid]; | |
162 | spin_unlock_irqrestore(&fifo->lock, flags); | |
163 | ||
164 | return nouveau_client_name(chan); | |
165 | } | |
166 | ||
ebb945a9 BS |
167 | void |
168 | nouveau_fifo_destroy(struct nouveau_fifo *priv) | |
169 | { | |
170 | kfree(priv->channel); | |
9bd2ddba | 171 | nouveau_event_destroy(&priv->uevent); |
893e90c5 | 172 | nouveau_event_destroy(&priv->cevent); |
ebb945a9 BS |
173 | nouveau_engine_destroy(&priv->base); |
174 | } | |
175 | ||
176 | int | |
177 | nouveau_fifo_create_(struct nouveau_object *parent, | |
178 | struct nouveau_object *engine, | |
179 | struct nouveau_oclass *oclass, | |
180 | int min, int max, int length, void **pobject) | |
181 | { | |
182 | struct nouveau_fifo *priv; | |
183 | int ret; | |
184 | ||
185 | ret = nouveau_engine_create_(parent, engine, oclass, true, "PFIFO", | |
186 | "fifo", length, pobject); | |
187 | priv = *pobject; | |
188 | if (ret) | |
189 | return ret; | |
190 | ||
191 | priv->min = min; | |
192 | priv->max = max; | |
193 | priv->channel = kzalloc(sizeof(*priv->channel) * (max + 1), GFP_KERNEL); | |
194 | if (!priv->channel) | |
195 | return -ENOMEM; | |
196 | ||
893e90c5 BS |
197 | ret = nouveau_event_create(1, &priv->cevent); |
198 | if (ret) | |
199 | return ret; | |
200 | ||
9bd2ddba BS |
201 | ret = nouveau_event_create(1, &priv->uevent); |
202 | if (ret) | |
203 | return ret; | |
204 | ||
0c5b8cec | 205 | priv->chid = nouveau_fifo_chid; |
ebb945a9 BS |
206 | spin_lock_init(&priv->lock); |
207 | return 0; | |
208 | } |