diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2011-04-04 02:08:24 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2011-05-15 20:48:56 -0400 |
commit | a02ccc7f97d9e9121aa641aca33ba2a2978aef31 (patch) | |
tree | 6c977171670bc47cd6a55bfe6810461e3fcb8975 /drivers/gpu/drm/nouveau/nv40_mpeg.c | |
parent | d5a27370b507be810bd32a01fe493adef7ad85d9 (diff) |
drm/nv40/vpe: add support for PMPEG
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv40_mpeg.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv40_mpeg.c | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nv40_mpeg.c b/drivers/gpu/drm/nouveau/nv40_mpeg.c new file mode 100644 index 000000000000..6d2af292a2e3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv40_mpeg.c | |||
@@ -0,0 +1,311 @@ | |||
1 | /* | ||
2 | * Copyright 2011 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 | |||
25 | #include "drmP.h" | ||
26 | #include "nouveau_drv.h" | ||
27 | #include "nouveau_ramht.h" | ||
28 | |||
29 | struct nv40_mpeg_engine { | ||
30 | struct nouveau_exec_engine base; | ||
31 | }; | ||
32 | |||
33 | static int | ||
34 | nv40_mpeg_context_new(struct nouveau_channel *chan, int engine) | ||
35 | { | ||
36 | struct drm_device *dev = chan->dev; | ||
37 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
38 | struct nouveau_gpuobj *ctx = NULL; | ||
39 | unsigned long flags; | ||
40 | int ret; | ||
41 | |||
42 | NV_DEBUG(dev, "ch%d\n", chan->id); | ||
43 | |||
44 | ret = nouveau_gpuobj_new(dev, NULL, 264 * 4, 16, NVOBJ_FLAG_ZERO_ALLOC | | ||
45 | NVOBJ_FLAG_ZERO_FREE, &ctx); | ||
46 | if (ret) | ||
47 | return ret; | ||
48 | |||
49 | nv_wo32(ctx, 0x78, 0x02001ec1); | ||
50 | |||
51 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); | ||
52 | nv_mask(dev, 0x002500, 0x00000001, 0x00000000); | ||
53 | if ((nv_rd32(dev, 0x003204) & 0x1f) == chan->id) | ||
54 | nv_wr32(dev, 0x00330c, ctx->pinst >> 4); | ||
55 | nv_wo32(chan->ramfc, 0x54, ctx->pinst >> 4); | ||
56 | nv_mask(dev, 0x002500, 0x00000001, 0x00000001); | ||
57 | spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); | ||
58 | |||
59 | chan->engctx[engine] = ctx; | ||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | static void | ||
64 | nv40_mpeg_context_del(struct nouveau_channel *chan, int engine) | ||
65 | { | ||
66 | struct drm_nouveau_private *dev_priv = chan->dev->dev_private; | ||
67 | struct nouveau_gpuobj *ctx = chan->engctx[engine]; | ||
68 | struct drm_device *dev = chan->dev; | ||
69 | unsigned long flags; | ||
70 | u32 inst = 0x80000000 | (ctx->pinst >> 4); | ||
71 | |||
72 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); | ||
73 | nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); | ||
74 | if (nv_rd32(dev, 0x00b318) == inst) | ||
75 | nv_mask(dev, 0x00b318, 0x80000000, 0x00000000); | ||
76 | nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); | ||
77 | spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); | ||
78 | |||
79 | nouveau_gpuobj_ref(NULL, &ctx); | ||
80 | chan->engctx[engine] = NULL; | ||
81 | } | ||
82 | |||
83 | static int | ||
84 | nv40_mpeg_object_new(struct nouveau_channel *chan, int engine, | ||
85 | u32 handle, u16 class) | ||
86 | { | ||
87 | struct drm_device *dev = chan->dev; | ||
88 | struct nouveau_gpuobj *obj = NULL; | ||
89 | int ret; | ||
90 | |||
91 | ret = nouveau_gpuobj_new(dev, chan, 20, 16, NVOBJ_FLAG_ZERO_ALLOC | | ||
92 | NVOBJ_FLAG_ZERO_FREE, &obj); | ||
93 | if (ret) | ||
94 | return ret; | ||
95 | obj->engine = 2; | ||
96 | obj->class = class; | ||
97 | |||
98 | nv_wo32(obj, 0x00, class); | ||
99 | |||
100 | ret = nouveau_ramht_insert(chan, handle, obj); | ||
101 | nouveau_gpuobj_ref(NULL, &obj); | ||
102 | return ret; | ||
103 | } | ||
104 | |||
105 | static int | ||
106 | nv40_mpeg_init(struct drm_device *dev, int engine) | ||
107 | { | ||
108 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
109 | struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine); | ||
110 | int i; | ||
111 | |||
112 | /* VPE init */ | ||
113 | nv_mask(dev, 0x000200, 0x00000002, 0x00000000); | ||
114 | nv_mask(dev, 0x000200, 0x00000002, 0x00000002); | ||
115 | nv_wr32(dev, 0x00b0e0, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ | ||
116 | nv_wr32(dev, 0x00b0e8, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ | ||
117 | |||
118 | for (i = 0; i < dev_priv->engine.fb.num_tiles; i++) | ||
119 | pmpeg->base.set_tile_region(dev, i); | ||
120 | |||
121 | /* PMPEG init */ | ||
122 | nv_wr32(dev, 0x00b32c, 0x00000000); | ||
123 | nv_wr32(dev, 0x00b314, 0x00000100); | ||
124 | nv_wr32(dev, 0x00b220, 0x00000044); | ||
125 | nv_wr32(dev, 0x00b300, 0x02001ec1); | ||
126 | nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); | ||
127 | |||
128 | nv_wr32(dev, 0x00b100, 0xffffffff); | ||
129 | nv_wr32(dev, 0x00b140, 0xffffffff); | ||
130 | |||
131 | if (!nv_wait(dev, 0x00b200, 0x00000001, 0x00000000)) { | ||
132 | NV_ERROR(dev, "PMPEG init: 0x%08x\n", nv_rd32(dev, 0x00b200)); | ||
133 | return -EBUSY; | ||
134 | } | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static int | ||
140 | nv40_mpeg_fini(struct drm_device *dev, int engine) | ||
141 | { | ||
142 | /*XXX: context save? */ | ||
143 | nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); | ||
144 | nv_wr32(dev, 0x00b140, 0x00000000); | ||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | static int | ||
149 | nv40_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) | ||
150 | { | ||
151 | struct drm_device *dev = chan->dev; | ||
152 | u32 inst = data << 4; | ||
153 | u32 dma0 = nv_ri32(dev, inst + 0); | ||
154 | u32 dma1 = nv_ri32(dev, inst + 4); | ||
155 | u32 dma2 = nv_ri32(dev, inst + 8); | ||
156 | u32 base = (dma2 & 0xfffff000) | (dma0 >> 20); | ||
157 | u32 size = dma1 + 1; | ||
158 | |||
159 | /* only allow linear DMA objects */ | ||
160 | if (!(dma0 & 0x00002000)) | ||
161 | return -EINVAL; | ||
162 | |||
163 | if (mthd == 0x0190) { | ||
164 | /* DMA_CMD */ | ||
165 | nv_mask(dev, 0x00b300, 0x00030000, (dma0 & 0x00030000)); | ||
166 | nv_wr32(dev, 0x00b334, base); | ||
167 | nv_wr32(dev, 0x00b324, size); | ||
168 | } else | ||
169 | if (mthd == 0x01a0) { | ||
170 | /* DMA_DATA */ | ||
171 | nv_mask(dev, 0x00b300, 0x000c0000, (dma0 & 0x00030000) << 2); | ||
172 | nv_wr32(dev, 0x00b360, base); | ||
173 | nv_wr32(dev, 0x00b364, size); | ||
174 | } else { | ||
175 | /* DMA_IMAGE, VRAM only */ | ||
176 | if (dma0 & 0x000c0000) | ||
177 | return -EINVAL; | ||
178 | |||
179 | nv_wr32(dev, 0x00b370, base); | ||
180 | nv_wr32(dev, 0x00b374, size); | ||
181 | } | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int | ||
187 | nv40_mpeg_isr_chid(struct drm_device *dev, u32 inst) | ||
188 | { | ||
189 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
190 | struct nouveau_gpuobj *ctx; | ||
191 | unsigned long flags; | ||
192 | int i; | ||
193 | |||
194 | spin_lock_irqsave(&dev_priv->channels.lock, flags); | ||
195 | for (i = 0; i < dev_priv->engine.fifo.channels; i++) { | ||
196 | if (!dev_priv->channels.ptr[i]) | ||
197 | continue; | ||
198 | |||
199 | ctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_MPEG]; | ||
200 | if (ctx && ctx->pinst == inst) | ||
201 | break; | ||
202 | } | ||
203 | spin_unlock_irqrestore(&dev_priv->channels.lock, flags); | ||
204 | return i; | ||
205 | } | ||
206 | |||
207 | static void | ||
208 | nv40_vpe_set_tile_region(struct drm_device *dev, int i) | ||
209 | { | ||
210 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
211 | struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; | ||
212 | |||
213 | nv_wr32(dev, 0x00b008 + (i * 0x10), tile->pitch); | ||
214 | nv_wr32(dev, 0x00b004 + (i * 0x10), tile->limit); | ||
215 | nv_wr32(dev, 0x00b000 + (i * 0x10), tile->addr); | ||
216 | } | ||
217 | |||
218 | static void | ||
219 | nv40_mpeg_isr(struct drm_device *dev) | ||
220 | { | ||
221 | u32 inst = (nv_rd32(dev, 0x00b318) & 0x000fffff) << 4; | ||
222 | u32 chid = nv40_mpeg_isr_chid(dev, inst); | ||
223 | u32 stat = nv_rd32(dev, 0x00b100); | ||
224 | u32 type = nv_rd32(dev, 0x00b230); | ||
225 | u32 mthd = nv_rd32(dev, 0x00b234); | ||
226 | u32 data = nv_rd32(dev, 0x00b238); | ||
227 | u32 show = stat; | ||
228 | |||
229 | if (stat & 0x01000000) { | ||
230 | /* happens on initial binding of the object */ | ||
231 | if (type == 0x00000020 && mthd == 0x0000) { | ||
232 | nv_mask(dev, 0x00b308, 0x00000000, 0x00000000); | ||
233 | show &= ~0x01000000; | ||
234 | } | ||
235 | |||
236 | if (type == 0x00000010) { | ||
237 | if (!nouveau_gpuobj_mthd_call2(dev, chid, 0x3174, mthd, data)) | ||
238 | show &= ~0x01000000; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | nv_wr32(dev, 0x00b100, stat); | ||
243 | nv_wr32(dev, 0x00b230, 0x00000001); | ||
244 | |||
245 | if (show && nouveau_ratelimit()) { | ||
246 | NV_INFO(dev, "PMPEG: Ch %d [0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n", | ||
247 | chid, inst, stat, type, mthd, data); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | static void | ||
252 | nv40_vpe_isr(struct drm_device *dev) | ||
253 | { | ||
254 | if (nv_rd32(dev, 0x00b100)) | ||
255 | nv40_mpeg_isr(dev); | ||
256 | |||
257 | if (nv_rd32(dev, 0x00b800)) { | ||
258 | u32 stat = nv_rd32(dev, 0x00b800); | ||
259 | NV_INFO(dev, "PMSRCH: 0x%08x\n", stat); | ||
260 | nv_wr32(dev, 0xb800, stat); | ||
261 | } | ||
262 | } | ||
263 | |||
264 | static void | ||
265 | nv40_mpeg_destroy(struct drm_device *dev, int engine) | ||
266 | { | ||
267 | struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine); | ||
268 | |||
269 | nouveau_irq_unregister(dev, 0); | ||
270 | |||
271 | NVOBJ_ENGINE_DEL(dev, MPEG); | ||
272 | kfree(pmpeg); | ||
273 | } | ||
274 | |||
275 | int | ||
276 | nv40_mpeg_create(struct drm_device *dev) | ||
277 | { | ||
278 | struct nv40_mpeg_engine *pmpeg; | ||
279 | |||
280 | pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL); | ||
281 | if (!pmpeg) | ||
282 | return -ENOMEM; | ||
283 | |||
284 | pmpeg->base.destroy = nv40_mpeg_destroy; | ||
285 | pmpeg->base.init = nv40_mpeg_init; | ||
286 | pmpeg->base.fini = nv40_mpeg_fini; | ||
287 | pmpeg->base.context_new = nv40_mpeg_context_new; | ||
288 | pmpeg->base.context_del = nv40_mpeg_context_del; | ||
289 | pmpeg->base.object_new = nv40_mpeg_object_new; | ||
290 | |||
291 | /* ISR vector, PMC_ENABLE bit, and TILE regs are shared between | ||
292 | * all VPE engines, for this driver's purposes the PMPEG engine | ||
293 | * will be treated as the "master" and handle the global VPE | ||
294 | * bits too | ||
295 | */ | ||
296 | pmpeg->base.set_tile_region = nv40_vpe_set_tile_region; | ||
297 | nouveau_irq_register(dev, 0, nv40_vpe_isr); | ||
298 | |||
299 | NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base); | ||
300 | NVOBJ_CLASS(dev, 0x3174, MPEG); | ||
301 | NVOBJ_MTHD (dev, 0x3174, 0x0190, nv40_mpeg_mthd_dma); | ||
302 | NVOBJ_MTHD (dev, 0x3174, 0x01a0, nv40_mpeg_mthd_dma); | ||
303 | NVOBJ_MTHD (dev, 0x3174, 0x01b0, nv40_mpeg_mthd_dma); | ||
304 | |||
305 | #if 0 | ||
306 | NVOBJ_ENGINE_ADD(dev, ME, &pme->base); | ||
307 | NVOBJ_CLASS(dev, 0x4075, ME); | ||
308 | #endif | ||
309 | return 0; | ||
310 | |||
311 | } | ||