diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c new file mode 100644 index 000000000000..bc0fac618a3f --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c | |||
@@ -0,0 +1,354 @@ | |||
1 | /* | ||
2 | * Copyright 2008 Advanced Micro Devices, Inc. | ||
3 | * Copyright 2008 Red Hat Inc. | ||
4 | * Copyright 2009 Jerome Glisse. | ||
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: Dave Airlie | ||
25 | * Alex Deucher | ||
26 | * Jerome Glisse | ||
27 | * Christian König | ||
28 | */ | ||
29 | #include <linux/seq_file.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <drm/drmP.h> | ||
32 | #include <drm/amdgpu_drm.h> | ||
33 | #include "amdgpu.h" | ||
34 | #include "atom.h" | ||
35 | |||
36 | /* | ||
37 | * IB | ||
38 | * IBs (Indirect Buffers) and areas of GPU accessible memory where | ||
39 | * commands are stored. You can put a pointer to the IB in the | ||
40 | * command ring and the hw will fetch the commands from the IB | ||
41 | * and execute them. Generally userspace acceleration drivers | ||
42 | * produce command buffers which are send to the kernel and | ||
43 | * put in IBs for execution by the requested ring. | ||
44 | */ | ||
45 | static int amdgpu_debugfs_sa_init(struct amdgpu_device *adev); | ||
46 | |||
47 | /** | ||
48 | * amdgpu_ib_get - request an IB (Indirect Buffer) | ||
49 | * | ||
50 | * @ring: ring index the IB is associated with | ||
51 | * @size: requested IB size | ||
52 | * @ib: IB object returned | ||
53 | * | ||
54 | * Request an IB (all asics). IBs are allocated using the | ||
55 | * suballocator. | ||
56 | * Returns 0 on success, error on failure. | ||
57 | */ | ||
58 | int amdgpu_ib_get(struct amdgpu_ring *ring, struct amdgpu_vm *vm, | ||
59 | unsigned size, struct amdgpu_ib *ib) | ||
60 | { | ||
61 | struct amdgpu_device *adev = ring->adev; | ||
62 | int r; | ||
63 | |||
64 | if (size) { | ||
65 | r = amdgpu_sa_bo_new(adev, &adev->ring_tmp_bo, | ||
66 | &ib->sa_bo, size, 256); | ||
67 | if (r) { | ||
68 | dev_err(adev->dev, "failed to get a new IB (%d)\n", r); | ||
69 | return r; | ||
70 | } | ||
71 | |||
72 | ib->ptr = amdgpu_sa_bo_cpu_addr(ib->sa_bo); | ||
73 | |||
74 | if (!vm) | ||
75 | ib->gpu_addr = amdgpu_sa_bo_gpu_addr(ib->sa_bo); | ||
76 | else | ||
77 | ib->gpu_addr = 0; | ||
78 | |||
79 | } else { | ||
80 | ib->sa_bo = NULL; | ||
81 | ib->ptr = NULL; | ||
82 | ib->gpu_addr = 0; | ||
83 | } | ||
84 | |||
85 | amdgpu_sync_create(&ib->sync); | ||
86 | |||
87 | ib->ring = ring; | ||
88 | ib->fence = NULL; | ||
89 | ib->user = NULL; | ||
90 | ib->vm = vm; | ||
91 | ib->gds_base = 0; | ||
92 | ib->gds_size = 0; | ||
93 | ib->gws_base = 0; | ||
94 | ib->gws_size = 0; | ||
95 | ib->oa_base = 0; | ||
96 | ib->oa_size = 0; | ||
97 | ib->flags = 0; | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * amdgpu_ib_free - free an IB (Indirect Buffer) | ||
104 | * | ||
105 | * @adev: amdgpu_device pointer | ||
106 | * @ib: IB object to free | ||
107 | * | ||
108 | * Free an IB (all asics). | ||
109 | */ | ||
110 | void amdgpu_ib_free(struct amdgpu_device *adev, struct amdgpu_ib *ib) | ||
111 | { | ||
112 | amdgpu_sync_free(adev, &ib->sync, ib->fence); | ||
113 | amdgpu_sa_bo_free(adev, &ib->sa_bo, ib->fence); | ||
114 | amdgpu_fence_unref(&ib->fence); | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * amdgpu_ib_schedule - schedule an IB (Indirect Buffer) on the ring | ||
119 | * | ||
120 | * @adev: amdgpu_device pointer | ||
121 | * @num_ibs: number of IBs to schedule | ||
122 | * @ibs: IB objects to schedule | ||
123 | * @owner: owner for creating the fences | ||
124 | * | ||
125 | * Schedule an IB on the associated ring (all asics). | ||
126 | * Returns 0 on success, error on failure. | ||
127 | * | ||
128 | * On SI, there are two parallel engines fed from the primary ring, | ||
129 | * the CE (Constant Engine) and the DE (Drawing Engine). Since | ||
130 | * resource descriptors have moved to memory, the CE allows you to | ||
131 | * prime the caches while the DE is updating register state so that | ||
132 | * the resource descriptors will be already in cache when the draw is | ||
133 | * processed. To accomplish this, the userspace driver submits two | ||
134 | * IBs, one for the CE and one for the DE. If there is a CE IB (called | ||
135 | * a CONST_IB), it will be put on the ring prior to the DE IB. Prior | ||
136 | * to SI there was just a DE IB. | ||
137 | */ | ||
138 | int amdgpu_ib_schedule(struct amdgpu_device *adev, unsigned num_ibs, | ||
139 | struct amdgpu_ib *ibs, void *owner) | ||
140 | { | ||
141 | struct amdgpu_ib *ib = &ibs[0]; | ||
142 | struct amdgpu_ring *ring; | ||
143 | struct amdgpu_ctx *ctx, *old_ctx; | ||
144 | struct amdgpu_vm *vm; | ||
145 | unsigned i; | ||
146 | int r = 0; | ||
147 | |||
148 | if (num_ibs == 0) | ||
149 | return -EINVAL; | ||
150 | |||
151 | ring = ibs->ring; | ||
152 | ctx = ibs->ctx; | ||
153 | vm = ibs->vm; | ||
154 | |||
155 | if (!ring->ready) { | ||
156 | dev_err(adev->dev, "couldn't schedule ib\n"); | ||
157 | return -EINVAL; | ||
158 | } | ||
159 | |||
160 | r = amdgpu_ring_lock(ring, (256 + AMDGPU_NUM_SYNCS * 8) * num_ibs); | ||
161 | if (r) { | ||
162 | dev_err(adev->dev, "scheduling IB failed (%d).\n", r); | ||
163 | return r; | ||
164 | } | ||
165 | |||
166 | if (vm) { | ||
167 | /* grab a vm id if necessary */ | ||
168 | struct amdgpu_fence *vm_id_fence = NULL; | ||
169 | vm_id_fence = amdgpu_vm_grab_id(ibs->ring, ibs->vm); | ||
170 | amdgpu_sync_fence(&ibs->sync, vm_id_fence); | ||
171 | } | ||
172 | |||
173 | r = amdgpu_sync_rings(&ibs->sync, ring); | ||
174 | if (r) { | ||
175 | amdgpu_ring_unlock_undo(ring); | ||
176 | dev_err(adev->dev, "failed to sync rings (%d)\n", r); | ||
177 | return r; | ||
178 | } | ||
179 | |||
180 | if (vm) { | ||
181 | /* do context switch */ | ||
182 | amdgpu_vm_flush(ring, vm, ib->sync.last_vm_update); | ||
183 | |||
184 | if (ring->funcs->emit_gds_switch) | ||
185 | amdgpu_ring_emit_gds_switch(ring, ib->vm->ids[ring->idx].id, | ||
186 | ib->gds_base, ib->gds_size, | ||
187 | ib->gws_base, ib->gws_size, | ||
188 | ib->oa_base, ib->oa_size); | ||
189 | |||
190 | if (ring->funcs->emit_hdp_flush) | ||
191 | amdgpu_ring_emit_hdp_flush(ring); | ||
192 | } | ||
193 | |||
194 | old_ctx = ring->current_ctx; | ||
195 | for (i = 0; i < num_ibs; ++i) { | ||
196 | ib = &ibs[i]; | ||
197 | |||
198 | if (ib->ring != ring || ib->ctx != ctx || ib->vm != vm) { | ||
199 | ring->current_ctx = old_ctx; | ||
200 | amdgpu_ring_unlock_undo(ring); | ||
201 | return -EINVAL; | ||
202 | } | ||
203 | amdgpu_ring_emit_ib(ring, ib); | ||
204 | ring->current_ctx = ctx; | ||
205 | } | ||
206 | |||
207 | r = amdgpu_fence_emit(ring, owner, &ib->fence); | ||
208 | if (r) { | ||
209 | dev_err(adev->dev, "failed to emit fence (%d)\n", r); | ||
210 | ring->current_ctx = old_ctx; | ||
211 | amdgpu_ring_unlock_undo(ring); | ||
212 | return r; | ||
213 | } | ||
214 | |||
215 | /* wrap the last IB with fence */ | ||
216 | if (ib->user) { | ||
217 | uint64_t addr = amdgpu_bo_gpu_offset(ib->user->bo); | ||
218 | addr += ib->user->offset; | ||
219 | amdgpu_ring_emit_fence(ring, addr, ib->fence->seq, | ||
220 | AMDGPU_FENCE_FLAG_64BIT); | ||
221 | } | ||
222 | |||
223 | if (ib->vm) | ||
224 | amdgpu_vm_fence(adev, ib->vm, ib->fence); | ||
225 | |||
226 | amdgpu_ring_unlock_commit(ring); | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | /** | ||
231 | * amdgpu_ib_pool_init - Init the IB (Indirect Buffer) pool | ||
232 | * | ||
233 | * @adev: amdgpu_device pointer | ||
234 | * | ||
235 | * Initialize the suballocator to manage a pool of memory | ||
236 | * for use as IBs (all asics). | ||
237 | * Returns 0 on success, error on failure. | ||
238 | */ | ||
239 | int amdgpu_ib_pool_init(struct amdgpu_device *adev) | ||
240 | { | ||
241 | int r; | ||
242 | |||
243 | if (adev->ib_pool_ready) { | ||
244 | return 0; | ||
245 | } | ||
246 | r = amdgpu_sa_bo_manager_init(adev, &adev->ring_tmp_bo, | ||
247 | AMDGPU_IB_POOL_SIZE*64*1024, | ||
248 | AMDGPU_GPU_PAGE_SIZE, | ||
249 | AMDGPU_GEM_DOMAIN_GTT); | ||
250 | if (r) { | ||
251 | return r; | ||
252 | } | ||
253 | |||
254 | r = amdgpu_sa_bo_manager_start(adev, &adev->ring_tmp_bo); | ||
255 | if (r) { | ||
256 | return r; | ||
257 | } | ||
258 | |||
259 | adev->ib_pool_ready = true; | ||
260 | if (amdgpu_debugfs_sa_init(adev)) { | ||
261 | dev_err(adev->dev, "failed to register debugfs file for SA\n"); | ||
262 | } | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | /** | ||
267 | * amdgpu_ib_pool_fini - Free the IB (Indirect Buffer) pool | ||
268 | * | ||
269 | * @adev: amdgpu_device pointer | ||
270 | * | ||
271 | * Tear down the suballocator managing the pool of memory | ||
272 | * for use as IBs (all asics). | ||
273 | */ | ||
274 | void amdgpu_ib_pool_fini(struct amdgpu_device *adev) | ||
275 | { | ||
276 | if (adev->ib_pool_ready) { | ||
277 | amdgpu_sa_bo_manager_suspend(adev, &adev->ring_tmp_bo); | ||
278 | amdgpu_sa_bo_manager_fini(adev, &adev->ring_tmp_bo); | ||
279 | adev->ib_pool_ready = false; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * amdgpu_ib_ring_tests - test IBs on the rings | ||
285 | * | ||
286 | * @adev: amdgpu_device pointer | ||
287 | * | ||
288 | * Test an IB (Indirect Buffer) on each ring. | ||
289 | * If the test fails, disable the ring. | ||
290 | * Returns 0 on success, error if the primary GFX ring | ||
291 | * IB test fails. | ||
292 | */ | ||
293 | int amdgpu_ib_ring_tests(struct amdgpu_device *adev) | ||
294 | { | ||
295 | unsigned i; | ||
296 | int r; | ||
297 | |||
298 | for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | ||
299 | struct amdgpu_ring *ring = adev->rings[i]; | ||
300 | |||
301 | if (!ring || !ring->ready) | ||
302 | continue; | ||
303 | |||
304 | r = amdgpu_ring_test_ib(ring); | ||
305 | if (r) { | ||
306 | ring->ready = false; | ||
307 | adev->needs_reset = false; | ||
308 | |||
309 | if (ring == &adev->gfx.gfx_ring[0]) { | ||
310 | /* oh, oh, that's really bad */ | ||
311 | DRM_ERROR("amdgpu: failed testing IB on GFX ring (%d).\n", r); | ||
312 | adev->accel_working = false; | ||
313 | return r; | ||
314 | |||
315 | } else { | ||
316 | /* still not good, but we can live with it */ | ||
317 | DRM_ERROR("amdgpu: failed testing IB on ring %d (%d).\n", i, r); | ||
318 | } | ||
319 | } | ||
320 | } | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | /* | ||
325 | * Debugfs info | ||
326 | */ | ||
327 | #if defined(CONFIG_DEBUG_FS) | ||
328 | |||
329 | static int amdgpu_debugfs_sa_info(struct seq_file *m, void *data) | ||
330 | { | ||
331 | struct drm_info_node *node = (struct drm_info_node *) m->private; | ||
332 | struct drm_device *dev = node->minor->dev; | ||
333 | struct amdgpu_device *adev = dev->dev_private; | ||
334 | |||
335 | amdgpu_sa_bo_dump_debug_info(&adev->ring_tmp_bo, m); | ||
336 | |||
337 | return 0; | ||
338 | |||
339 | } | ||
340 | |||
341 | static struct drm_info_list amdgpu_debugfs_sa_list[] = { | ||
342 | {"amdgpu_sa_info", &amdgpu_debugfs_sa_info, 0, NULL}, | ||
343 | }; | ||
344 | |||
345 | #endif | ||
346 | |||
347 | static int amdgpu_debugfs_sa_init(struct amdgpu_device *adev) | ||
348 | { | ||
349 | #if defined(CONFIG_DEBUG_FS) | ||
350 | return amdgpu_debugfs_add_files(adev, amdgpu_debugfs_sa_list, 1); | ||
351 | #else | ||
352 | return 0; | ||
353 | #endif | ||
354 | } | ||