diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-28 08:54:23 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-28 08:54:23 -0400 |
commit | 37be944a0270402f9cda291a930b0286f6dc92f5 (patch) | |
tree | 6a91a9eb86450f4a18a8871f04a1ef810e7b55d6 /drivers/gpu/drm/nouveau/nv31_mpeg.c | |
parent | ca836a25435ef1b9914840ed0a310c9b6ac261d1 (diff) | |
parent | 1717c0e23f411147490c7a3312b894f0ea9a5fb1 (diff) |
Merge branch 'drm-core-next' of git://people.freedesktop.org/~airlied/linux
* 'drm-core-next' of git://people.freedesktop.org/~airlied/linux: (290 commits)
Revert "drm/ttm: add a way to bo_wait for either the last read or last write"
Revert "drm/radeon/kms: add a new gem_wait ioctl with read/write flags"
vmwgfx: Don't pass unused arguments to do_dirty functions
vmwgfx: Emulate depth 32 framebuffers
drm/radeon: Lower the severity of the radeon lockup messages.
drm/i915/dp: Fix eDP on PCH DP on CPT/PPT
drm/i915/dp: Introduce is_cpu_edp()
drm/i915: use correct SPD type value
drm/i915: fix ILK+ infoframe support
drm/i915: add DP test request handling
drm/i915: read full receiver capability field during DP hot plug
drm/i915/dp: Remove eDP special cases from bandwidth checks
drm/i915/dp: Fix the math in intel_dp_link_required
drm/i915/panel: Always record the backlight level again (but cleverly)
i915: Move i915_read/write out of line
drm/i915: remove transcoder PLL mashing from mode_set per specs
drm/i915: if transcoder disable fails, say which
drm/i915: set watermarks for third pipe on IVB
drm/i915: export a CPT mode set verification function
drm/i915: fix transcoder PLL select masking
...
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv31_mpeg.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv31_mpeg.c | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nv31_mpeg.c b/drivers/gpu/drm/nouveau/nv31_mpeg.c new file mode 100644 index 000000000000..6f06a0713f00 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv31_mpeg.c | |||
@@ -0,0 +1,344 @@ | |||
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 nv31_mpeg_engine { | ||
30 | struct nouveau_exec_engine base; | ||
31 | atomic_t refcount; | ||
32 | }; | ||
33 | |||
34 | |||
35 | static int | ||
36 | nv31_mpeg_context_new(struct nouveau_channel *chan, int engine) | ||
37 | { | ||
38 | struct nv31_mpeg_engine *pmpeg = nv_engine(chan->dev, engine); | ||
39 | |||
40 | if (!atomic_add_unless(&pmpeg->refcount, 1, 1)) | ||
41 | return -EBUSY; | ||
42 | |||
43 | chan->engctx[engine] = (void *)0xdeadcafe; | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | static void | ||
48 | nv31_mpeg_context_del(struct nouveau_channel *chan, int engine) | ||
49 | { | ||
50 | struct nv31_mpeg_engine *pmpeg = nv_engine(chan->dev, engine); | ||
51 | atomic_dec(&pmpeg->refcount); | ||
52 | chan->engctx[engine] = NULL; | ||
53 | } | ||
54 | |||
55 | static int | ||
56 | nv40_mpeg_context_new(struct nouveau_channel *chan, int engine) | ||
57 | { | ||
58 | struct drm_device *dev = chan->dev; | ||
59 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
60 | struct nouveau_gpuobj *ctx = NULL; | ||
61 | unsigned long flags; | ||
62 | int ret; | ||
63 | |||
64 | NV_DEBUG(dev, "ch%d\n", chan->id); | ||
65 | |||
66 | ret = nouveau_gpuobj_new(dev, NULL, 264 * 4, 16, NVOBJ_FLAG_ZERO_ALLOC | | ||
67 | NVOBJ_FLAG_ZERO_FREE, &ctx); | ||
68 | if (ret) | ||
69 | return ret; | ||
70 | |||
71 | nv_wo32(ctx, 0x78, 0x02001ec1); | ||
72 | |||
73 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); | ||
74 | nv_mask(dev, 0x002500, 0x00000001, 0x00000000); | ||
75 | if ((nv_rd32(dev, 0x003204) & 0x1f) == chan->id) | ||
76 | nv_wr32(dev, 0x00330c, ctx->pinst >> 4); | ||
77 | nv_wo32(chan->ramfc, 0x54, ctx->pinst >> 4); | ||
78 | nv_mask(dev, 0x002500, 0x00000001, 0x00000001); | ||
79 | spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); | ||
80 | |||
81 | chan->engctx[engine] = ctx; | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | static void | ||
86 | nv40_mpeg_context_del(struct nouveau_channel *chan, int engine) | ||
87 | { | ||
88 | struct drm_nouveau_private *dev_priv = chan->dev->dev_private; | ||
89 | struct nouveau_gpuobj *ctx = chan->engctx[engine]; | ||
90 | struct drm_device *dev = chan->dev; | ||
91 | unsigned long flags; | ||
92 | u32 inst = 0x80000000 | (ctx->pinst >> 4); | ||
93 | |||
94 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); | ||
95 | nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); | ||
96 | if (nv_rd32(dev, 0x00b318) == inst) | ||
97 | nv_mask(dev, 0x00b318, 0x80000000, 0x00000000); | ||
98 | nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); | ||
99 | spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); | ||
100 | |||
101 | nouveau_gpuobj_ref(NULL, &ctx); | ||
102 | chan->engctx[engine] = NULL; | ||
103 | } | ||
104 | |||
105 | static int | ||
106 | nv31_mpeg_object_new(struct nouveau_channel *chan, int engine, | ||
107 | u32 handle, u16 class) | ||
108 | { | ||
109 | struct drm_device *dev = chan->dev; | ||
110 | struct nouveau_gpuobj *obj = NULL; | ||
111 | int ret; | ||
112 | |||
113 | ret = nouveau_gpuobj_new(dev, chan, 20, 16, NVOBJ_FLAG_ZERO_ALLOC | | ||
114 | NVOBJ_FLAG_ZERO_FREE, &obj); | ||
115 | if (ret) | ||
116 | return ret; | ||
117 | obj->engine = 2; | ||
118 | obj->class = class; | ||
119 | |||
120 | nv_wo32(obj, 0x00, class); | ||
121 | |||
122 | ret = nouveau_ramht_insert(chan, handle, obj); | ||
123 | nouveau_gpuobj_ref(NULL, &obj); | ||
124 | return ret; | ||
125 | } | ||
126 | |||
127 | static int | ||
128 | nv31_mpeg_init(struct drm_device *dev, int engine) | ||
129 | { | ||
130 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
131 | struct nv31_mpeg_engine *pmpeg = nv_engine(dev, engine); | ||
132 | int i; | ||
133 | |||
134 | /* VPE init */ | ||
135 | nv_mask(dev, 0x000200, 0x00000002, 0x00000000); | ||
136 | nv_mask(dev, 0x000200, 0x00000002, 0x00000002); | ||
137 | nv_wr32(dev, 0x00b0e0, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ | ||
138 | nv_wr32(dev, 0x00b0e8, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ | ||
139 | |||
140 | for (i = 0; i < dev_priv->engine.fb.num_tiles; i++) | ||
141 | pmpeg->base.set_tile_region(dev, i); | ||
142 | |||
143 | /* PMPEG init */ | ||
144 | nv_wr32(dev, 0x00b32c, 0x00000000); | ||
145 | nv_wr32(dev, 0x00b314, 0x00000100); | ||
146 | nv_wr32(dev, 0x00b220, nv44_graph_class(dev) ? 0x00000044 : 0x00000031); | ||
147 | nv_wr32(dev, 0x00b300, 0x02001ec1); | ||
148 | nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); | ||
149 | |||
150 | nv_wr32(dev, 0x00b100, 0xffffffff); | ||
151 | nv_wr32(dev, 0x00b140, 0xffffffff); | ||
152 | |||
153 | if (!nv_wait(dev, 0x00b200, 0x00000001, 0x00000000)) { | ||
154 | NV_ERROR(dev, "PMPEG init: 0x%08x\n", nv_rd32(dev, 0x00b200)); | ||
155 | return -EBUSY; | ||
156 | } | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static int | ||
162 | nv31_mpeg_fini(struct drm_device *dev, int engine, bool suspend) | ||
163 | { | ||
164 | /*XXX: context save? */ | ||
165 | nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); | ||
166 | nv_wr32(dev, 0x00b140, 0x00000000); | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | static int | ||
171 | nv31_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) | ||
172 | { | ||
173 | struct drm_device *dev = chan->dev; | ||
174 | u32 inst = data << 4; | ||
175 | u32 dma0 = nv_ri32(dev, inst + 0); | ||
176 | u32 dma1 = nv_ri32(dev, inst + 4); | ||
177 | u32 dma2 = nv_ri32(dev, inst + 8); | ||
178 | u32 base = (dma2 & 0xfffff000) | (dma0 >> 20); | ||
179 | u32 size = dma1 + 1; | ||
180 | |||
181 | /* only allow linear DMA objects */ | ||
182 | if (!(dma0 & 0x00002000)) | ||
183 | return -EINVAL; | ||
184 | |||
185 | if (mthd == 0x0190) { | ||
186 | /* DMA_CMD */ | ||
187 | nv_mask(dev, 0x00b300, 0x00030000, (dma0 & 0x00030000)); | ||
188 | nv_wr32(dev, 0x00b334, base); | ||
189 | nv_wr32(dev, 0x00b324, size); | ||
190 | } else | ||
191 | if (mthd == 0x01a0) { | ||
192 | /* DMA_DATA */ | ||
193 | nv_mask(dev, 0x00b300, 0x000c0000, (dma0 & 0x00030000) << 2); | ||
194 | nv_wr32(dev, 0x00b360, base); | ||
195 | nv_wr32(dev, 0x00b364, size); | ||
196 | } else { | ||
197 | /* DMA_IMAGE, VRAM only */ | ||
198 | if (dma0 & 0x000c0000) | ||
199 | return -EINVAL; | ||
200 | |||
201 | nv_wr32(dev, 0x00b370, base); | ||
202 | nv_wr32(dev, 0x00b374, size); | ||
203 | } | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int | ||
209 | nv31_mpeg_isr_chid(struct drm_device *dev, u32 inst) | ||
210 | { | ||
211 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
212 | struct nouveau_gpuobj *ctx; | ||
213 | unsigned long flags; | ||
214 | int i; | ||
215 | |||
216 | /* hardcode drm channel id on nv3x, so swmthd lookup works */ | ||
217 | if (dev_priv->card_type < NV_40) | ||
218 | return 0; | ||
219 | |||
220 | spin_lock_irqsave(&dev_priv->channels.lock, flags); | ||
221 | for (i = 0; i < dev_priv->engine.fifo.channels; i++) { | ||
222 | if (!dev_priv->channels.ptr[i]) | ||
223 | continue; | ||
224 | |||
225 | ctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_MPEG]; | ||
226 | if (ctx && ctx->pinst == inst) | ||
227 | break; | ||
228 | } | ||
229 | spin_unlock_irqrestore(&dev_priv->channels.lock, flags); | ||
230 | return i; | ||
231 | } | ||
232 | |||
233 | static void | ||
234 | nv31_vpe_set_tile_region(struct drm_device *dev, int i) | ||
235 | { | ||
236 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
237 | struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; | ||
238 | |||
239 | nv_wr32(dev, 0x00b008 + (i * 0x10), tile->pitch); | ||
240 | nv_wr32(dev, 0x00b004 + (i * 0x10), tile->limit); | ||
241 | nv_wr32(dev, 0x00b000 + (i * 0x10), tile->addr); | ||
242 | } | ||
243 | |||
244 | static void | ||
245 | nv31_mpeg_isr(struct drm_device *dev) | ||
246 | { | ||
247 | u32 inst = (nv_rd32(dev, 0x00b318) & 0x000fffff) << 4; | ||
248 | u32 chid = nv31_mpeg_isr_chid(dev, inst); | ||
249 | u32 stat = nv_rd32(dev, 0x00b100); | ||
250 | u32 type = nv_rd32(dev, 0x00b230); | ||
251 | u32 mthd = nv_rd32(dev, 0x00b234); | ||
252 | u32 data = nv_rd32(dev, 0x00b238); | ||
253 | u32 show = stat; | ||
254 | |||
255 | if (stat & 0x01000000) { | ||
256 | /* happens on initial binding of the object */ | ||
257 | if (type == 0x00000020 && mthd == 0x0000) { | ||
258 | nv_mask(dev, 0x00b308, 0x00000000, 0x00000000); | ||
259 | show &= ~0x01000000; | ||
260 | } | ||
261 | |||
262 | if (type == 0x00000010) { | ||
263 | if (!nouveau_gpuobj_mthd_call2(dev, chid, 0x3174, mthd, data)) | ||
264 | show &= ~0x01000000; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | nv_wr32(dev, 0x00b100, stat); | ||
269 | nv_wr32(dev, 0x00b230, 0x00000001); | ||
270 | |||
271 | if (show && nouveau_ratelimit()) { | ||
272 | NV_INFO(dev, "PMPEG: Ch %d [0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n", | ||
273 | chid, inst, stat, type, mthd, data); | ||
274 | } | ||
275 | } | ||
276 | |||
277 | static void | ||
278 | nv31_vpe_isr(struct drm_device *dev) | ||
279 | { | ||
280 | if (nv_rd32(dev, 0x00b100)) | ||
281 | nv31_mpeg_isr(dev); | ||
282 | |||
283 | if (nv_rd32(dev, 0x00b800)) { | ||
284 | u32 stat = nv_rd32(dev, 0x00b800); | ||
285 | NV_INFO(dev, "PMSRCH: 0x%08x\n", stat); | ||
286 | nv_wr32(dev, 0xb800, stat); | ||
287 | } | ||
288 | } | ||
289 | |||
290 | static void | ||
291 | nv31_mpeg_destroy(struct drm_device *dev, int engine) | ||
292 | { | ||
293 | struct nv31_mpeg_engine *pmpeg = nv_engine(dev, engine); | ||
294 | |||
295 | nouveau_irq_unregister(dev, 0); | ||
296 | |||
297 | NVOBJ_ENGINE_DEL(dev, MPEG); | ||
298 | kfree(pmpeg); | ||
299 | } | ||
300 | |||
301 | int | ||
302 | nv31_mpeg_create(struct drm_device *dev) | ||
303 | { | ||
304 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
305 | struct nv31_mpeg_engine *pmpeg; | ||
306 | |||
307 | pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL); | ||
308 | if (!pmpeg) | ||
309 | return -ENOMEM; | ||
310 | atomic_set(&pmpeg->refcount, 0); | ||
311 | |||
312 | pmpeg->base.destroy = nv31_mpeg_destroy; | ||
313 | pmpeg->base.init = nv31_mpeg_init; | ||
314 | pmpeg->base.fini = nv31_mpeg_fini; | ||
315 | if (dev_priv->card_type < NV_40) { | ||
316 | pmpeg->base.context_new = nv31_mpeg_context_new; | ||
317 | pmpeg->base.context_del = nv31_mpeg_context_del; | ||
318 | } else { | ||
319 | pmpeg->base.context_new = nv40_mpeg_context_new; | ||
320 | pmpeg->base.context_del = nv40_mpeg_context_del; | ||
321 | } | ||
322 | pmpeg->base.object_new = nv31_mpeg_object_new; | ||
323 | |||
324 | /* ISR vector, PMC_ENABLE bit, and TILE regs are shared between | ||
325 | * all VPE engines, for this driver's purposes the PMPEG engine | ||
326 | * will be treated as the "master" and handle the global VPE | ||
327 | * bits too | ||
328 | */ | ||
329 | pmpeg->base.set_tile_region = nv31_vpe_set_tile_region; | ||
330 | nouveau_irq_register(dev, 0, nv31_vpe_isr); | ||
331 | |||
332 | NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base); | ||
333 | NVOBJ_CLASS(dev, 0x3174, MPEG); | ||
334 | NVOBJ_MTHD (dev, 0x3174, 0x0190, nv31_mpeg_mthd_dma); | ||
335 | NVOBJ_MTHD (dev, 0x3174, 0x01a0, nv31_mpeg_mthd_dma); | ||
336 | NVOBJ_MTHD (dev, 0x3174, 0x01b0, nv31_mpeg_mthd_dma); | ||
337 | |||
338 | #if 0 | ||
339 | NVOBJ_ENGINE_ADD(dev, ME, &pme->base); | ||
340 | NVOBJ_CLASS(dev, 0x4075, ME); | ||
341 | #endif | ||
342 | return 0; | ||
343 | |||
344 | } | ||