Line data Source code
1 : /*
2 : * Copyright 2013 Advanced Micro Devices, Inc.
3 : * All Rights Reserved.
4 : *
5 : * Permission is hereby granted, free of charge, to any person obtaining a
6 : * copy of this software and associated documentation files (the
7 : * "Software"), to deal in the Software without restriction, including
8 : * without limitation the rights to use, copy, modify, merge, publish,
9 : * distribute, sub license, and/or sell copies of the Software, and to
10 : * permit persons to whom the Software is furnished to do so, subject to
11 : * the following conditions:
12 : *
13 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 : * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
16 : * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
17 : * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18 : * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
19 : * USE OR OTHER DEALINGS IN THE SOFTWARE.
20 : *
21 : * The above copyright notice and this permission notice (including the
22 : * next paragraph) shall be included in all copies or substantial portions
23 : * of the Software.
24 : *
25 : * Authors: Christian König <christian.koenig@amd.com>
26 : */
27 :
28 : #include <dev/pci/drm/drmP.h>
29 : #include <dev/pci/drm/drm.h>
30 :
31 : #include "radeon.h"
32 : #include "radeon_asic.h"
33 : #include "sid.h"
34 :
35 : /* 1 second timeout */
36 : #define VCE_IDLE_TIMEOUT_MS 1000
37 :
38 : /* Firmware Names */
39 : #define FIRMWARE_TAHITI "radeon/TAHITI_vce.bin"
40 : #define FIRMWARE_BONAIRE "radeon/BONAIRE_vce.bin"
41 :
42 : MODULE_FIRMWARE(FIRMWARE_TAHITI);
43 : MODULE_FIRMWARE(FIRMWARE_BONAIRE);
44 :
45 : static void radeon_vce_idle_work_handler(struct work_struct *work);
46 :
47 : /**
48 : * radeon_vce_init - allocate memory, load vce firmware
49 : *
50 : * @rdev: radeon_device pointer
51 : *
52 : * First step to get VCE online, allocate memory and load the firmware
53 : */
54 0 : int radeon_vce_init(struct radeon_device *rdev)
55 : {
56 : static const char *fw_version = "[ATI LIB=VCEFW,";
57 : static const char *fb_version = "[ATI LIB=VCEFWSTATS,";
58 : unsigned long size;
59 : const char *fw_name, *c;
60 : uint8_t start, mid, end;
61 : int i, r;
62 :
63 0 : INIT_DELAYED_WORK(&rdev->vce.idle_work, radeon_vce_idle_work_handler);
64 :
65 0 : switch (rdev->family) {
66 : case CHIP_TAHITI:
67 : case CHIP_PITCAIRN:
68 : case CHIP_VERDE:
69 : case CHIP_OLAND:
70 : case CHIP_ARUBA:
71 : fw_name = FIRMWARE_TAHITI;
72 0 : break;
73 :
74 : case CHIP_BONAIRE:
75 : case CHIP_KAVERI:
76 : case CHIP_KABINI:
77 : case CHIP_HAWAII:
78 : case CHIP_MULLINS:
79 : fw_name = FIRMWARE_BONAIRE;
80 0 : break;
81 :
82 : default:
83 0 : return -EINVAL;
84 : }
85 :
86 0 : r = request_firmware(&rdev->vce_fw, fw_name, rdev->dev);
87 0 : if (r) {
88 0 : dev_err(rdev->dev, "radeon_vce: Can't load firmware \"%s\"\n",
89 : fw_name);
90 0 : return r;
91 : }
92 :
93 : /* search for firmware version */
94 :
95 0 : size = rdev->vce_fw->size - strlen(fw_version) - 9;
96 0 : c = rdev->vce_fw->data;
97 0 : for (;size > 0; --size, ++c)
98 0 : if (strncmp(c, fw_version, strlen(fw_version)) == 0)
99 : break;
100 :
101 0 : if (size == 0)
102 0 : return -EINVAL;
103 :
104 0 : c += strlen(fw_version);
105 : #ifdef notyet
106 : if (sscanf(c, "%2hhd.%2hhd.%2hhd]", &start, &mid, &end) != 3)
107 : return -EINVAL;
108 : #else
109 : #if 0
110 : int x;
111 : printf("\n%s A\n", __func__);
112 : for (x = 0; x < 16; x++) {
113 : printf("%02x ", c[x]);
114 : }
115 : #endif
116 0 : if (c[2] != '.') {
117 0 : printf("%s %s bad start value\n", rdev->self.dv_xname, __func__);
118 0 : return -EINVAL;
119 : }
120 0 : start = (10 * (c[0] - '0')) + (c[1] - '0');
121 0 : c += 3;
122 :
123 0 : if (c[1] != '.') {
124 0 : printf("%s %s bad mid value\n", rdev->self.dv_xname, __func__);
125 0 : return -EINVAL;
126 : }
127 0 : mid = c[0] - '0';
128 0 : c += 2;
129 :
130 0 : if (c[1] != ']') {
131 0 : printf("%s %s bad end value\n", rdev->self.dv_xname, __func__);
132 0 : return -EINVAL;
133 : }
134 0 : end = c[0] - '0';
135 : #endif
136 :
137 : /* search for feedback version */
138 :
139 0 : size = rdev->vce_fw->size - strlen(fb_version) - 3;
140 0 : c = rdev->vce_fw->data;
141 0 : for (;size > 0; --size, ++c)
142 0 : if (strncmp(c, fb_version, strlen(fb_version)) == 0)
143 : break;
144 :
145 0 : if (size == 0)
146 0 : return -EINVAL;
147 :
148 0 : c += strlen(fb_version);
149 : #ifdef notyet
150 : if (sscanf(c, "%2u]", &rdev->vce.fb_version) != 1)
151 : return -EINVAL;
152 : #else
153 : #if 0
154 : printf("\n%s B\n", __func__);
155 : for (x = 0; x < 16; x++) {
156 : printf("%02x ", c[x]);
157 : }
158 : printf("\n");
159 : #endif
160 0 : if (c[2] != ']') {
161 0 : printf("%s %s bad fb_version value\n", rdev->self.dv_xname, __func__);
162 0 : return -EINVAL;
163 : }
164 0 : rdev->vce.fb_version = (10 * (c[0] - '0')) + (c[1] - '0');
165 : #endif
166 :
167 : DRM_INFO("Found VCE firmware/feedback version %hhd.%hhd.%hhd / %d!\n",
168 : start, mid, end, rdev->vce.fb_version);
169 :
170 0 : rdev->vce.fw_version = (start << 24) | (mid << 16) | (end << 8);
171 :
172 : /* we can only work with this fw version for now */
173 0 : if ((rdev->vce.fw_version != ((40 << 24) | (2 << 16) | (2 << 8))) &&
174 0 : (rdev->vce.fw_version != ((50 << 24) | (0 << 16) | (1 << 8))) &&
175 0 : (rdev->vce.fw_version != ((50 << 24) | (1 << 16) | (2 << 8))))
176 0 : return -EINVAL;
177 :
178 : /* allocate firmware, stack and heap BO */
179 :
180 0 : if (rdev->family < CHIP_BONAIRE)
181 0 : size = vce_v1_0_bo_size(rdev);
182 : else
183 0 : size = vce_v2_0_bo_size(rdev);
184 0 : r = radeon_bo_create(rdev, size, PAGE_SIZE, true,
185 : RADEON_GEM_DOMAIN_VRAM, 0, NULL, NULL,
186 0 : &rdev->vce.vcpu_bo);
187 0 : if (r) {
188 0 : dev_err(rdev->dev, "(%d) failed to allocate VCE bo\n", r);
189 0 : return r;
190 : }
191 :
192 0 : r = radeon_bo_reserve(rdev->vce.vcpu_bo, false);
193 0 : if (r) {
194 0 : radeon_bo_unref(&rdev->vce.vcpu_bo);
195 0 : dev_err(rdev->dev, "(%d) failed to reserve VCE bo\n", r);
196 0 : return r;
197 : }
198 :
199 0 : r = radeon_bo_pin(rdev->vce.vcpu_bo, RADEON_GEM_DOMAIN_VRAM,
200 0 : &rdev->vce.gpu_addr);
201 0 : radeon_bo_unreserve(rdev->vce.vcpu_bo);
202 0 : if (r) {
203 0 : radeon_bo_unref(&rdev->vce.vcpu_bo);
204 0 : dev_err(rdev->dev, "(%d) VCE bo pin failed\n", r);
205 0 : return r;
206 : }
207 :
208 0 : for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
209 0 : atomic_set(&rdev->vce.handles[i], 0);
210 0 : rdev->vce.filp[i] = NULL;
211 : }
212 :
213 0 : return 0;
214 0 : }
215 :
216 : /**
217 : * radeon_vce_fini - free memory
218 : *
219 : * @rdev: radeon_device pointer
220 : *
221 : * Last step on VCE teardown, free firmware memory
222 : */
223 0 : void radeon_vce_fini(struct radeon_device *rdev)
224 : {
225 0 : if (rdev->vce.vcpu_bo == NULL)
226 : return;
227 :
228 0 : radeon_bo_unref(&rdev->vce.vcpu_bo);
229 :
230 0 : release_firmware(rdev->vce_fw);
231 0 : }
232 :
233 : /**
234 : * radeon_vce_suspend - unpin VCE fw memory
235 : *
236 : * @rdev: radeon_device pointer
237 : *
238 : */
239 0 : int radeon_vce_suspend(struct radeon_device *rdev)
240 : {
241 : int i;
242 :
243 0 : if (rdev->vce.vcpu_bo == NULL)
244 0 : return 0;
245 :
246 0 : for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i)
247 0 : if (atomic_read(&rdev->vce.handles[i]))
248 : break;
249 :
250 0 : if (i == RADEON_MAX_VCE_HANDLES)
251 0 : return 0;
252 :
253 : /* TODO: suspending running encoding sessions isn't supported */
254 0 : return -EINVAL;
255 0 : }
256 :
257 : /**
258 : * radeon_vce_resume - pin VCE fw memory
259 : *
260 : * @rdev: radeon_device pointer
261 : *
262 : */
263 0 : int radeon_vce_resume(struct radeon_device *rdev)
264 : {
265 0 : void *cpu_addr;
266 : int r;
267 :
268 0 : if (rdev->vce.vcpu_bo == NULL)
269 0 : return -EINVAL;
270 :
271 0 : r = radeon_bo_reserve(rdev->vce.vcpu_bo, false);
272 0 : if (r) {
273 0 : dev_err(rdev->dev, "(%d) failed to reserve VCE bo\n", r);
274 0 : return r;
275 : }
276 :
277 0 : r = radeon_bo_kmap(rdev->vce.vcpu_bo, &cpu_addr);
278 0 : if (r) {
279 0 : radeon_bo_unreserve(rdev->vce.vcpu_bo);
280 0 : dev_err(rdev->dev, "(%d) VCE map failed\n", r);
281 0 : return r;
282 : }
283 :
284 0 : memset(cpu_addr, 0, radeon_bo_size(rdev->vce.vcpu_bo));
285 0 : if (rdev->family < CHIP_BONAIRE)
286 0 : r = vce_v1_0_load_fw(rdev, cpu_addr);
287 : else
288 0 : memcpy(cpu_addr, rdev->vce_fw->data, rdev->vce_fw->size);
289 :
290 0 : radeon_bo_kunmap(rdev->vce.vcpu_bo);
291 :
292 0 : radeon_bo_unreserve(rdev->vce.vcpu_bo);
293 :
294 0 : return r;
295 0 : }
296 :
297 : /**
298 : * radeon_vce_idle_work_handler - power off VCE
299 : *
300 : * @work: pointer to work structure
301 : *
302 : * power of VCE when it's not used any more
303 : */
304 0 : static void radeon_vce_idle_work_handler(struct work_struct *work)
305 : {
306 : struct radeon_device *rdev =
307 0 : container_of(work, struct radeon_device, vce.idle_work.work);
308 :
309 0 : if ((radeon_fence_count_emitted(rdev, TN_RING_TYPE_VCE1_INDEX) == 0) &&
310 0 : (radeon_fence_count_emitted(rdev, TN_RING_TYPE_VCE2_INDEX) == 0)) {
311 0 : if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
312 0 : radeon_dpm_enable_vce(rdev, false);
313 0 : } else {
314 0 : radeon_set_vce_clocks(rdev, 0, 0);
315 : }
316 : } else {
317 0 : schedule_delayed_work(&rdev->vce.idle_work,
318 0 : msecs_to_jiffies(VCE_IDLE_TIMEOUT_MS));
319 : }
320 0 : }
321 :
322 : /**
323 : * radeon_vce_note_usage - power up VCE
324 : *
325 : * @rdev: radeon_device pointer
326 : *
327 : * Make sure VCE is powerd up when we want to use it
328 : */
329 0 : void radeon_vce_note_usage(struct radeon_device *rdev)
330 : {
331 : bool streams_changed = false;
332 0 : bool set_clocks = !cancel_delayed_work_sync(&rdev->vce.idle_work);
333 0 : set_clocks &= schedule_delayed_work(&rdev->vce.idle_work,
334 0 : msecs_to_jiffies(VCE_IDLE_TIMEOUT_MS));
335 :
336 0 : if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
337 : /* XXX figure out if the streams changed */
338 : streams_changed = false;
339 0 : }
340 :
341 0 : if (set_clocks || streams_changed) {
342 0 : if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
343 0 : radeon_dpm_enable_vce(rdev, true);
344 0 : } else {
345 0 : radeon_set_vce_clocks(rdev, 53300, 40000);
346 : }
347 : }
348 0 : }
349 :
350 : /**
351 : * radeon_vce_free_handles - free still open VCE handles
352 : *
353 : * @rdev: radeon_device pointer
354 : * @filp: drm file pointer
355 : *
356 : * Close all VCE handles still open by this file pointer
357 : */
358 0 : void radeon_vce_free_handles(struct radeon_device *rdev, struct drm_file *filp)
359 : {
360 : int i, r;
361 0 : for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
362 0 : uint32_t handle = atomic_read(&rdev->vce.handles[i]);
363 0 : if (!handle || rdev->vce.filp[i] != filp)
364 0 : continue;
365 :
366 0 : radeon_vce_note_usage(rdev);
367 :
368 0 : r = radeon_vce_get_destroy_msg(rdev, TN_RING_TYPE_VCE1_INDEX,
369 : handle, NULL);
370 0 : if (r)
371 0 : DRM_ERROR("Error destroying VCE handle (%d)!\n", r);
372 :
373 0 : rdev->vce.filp[i] = NULL;
374 0 : atomic_set(&rdev->vce.handles[i], 0);
375 0 : }
376 0 : }
377 :
378 : /**
379 : * radeon_vce_get_create_msg - generate a VCE create msg
380 : *
381 : * @rdev: radeon_device pointer
382 : * @ring: ring we should submit the msg to
383 : * @handle: VCE session handle to use
384 : * @fence: optional fence to return
385 : *
386 : * Open up a stream for HW test
387 : */
388 0 : int radeon_vce_get_create_msg(struct radeon_device *rdev, int ring,
389 : uint32_t handle, struct radeon_fence **fence)
390 : {
391 : const unsigned ib_size_dw = 1024;
392 0 : struct radeon_ib ib;
393 : uint64_t dummy;
394 : int i, r;
395 :
396 0 : r = radeon_ib_get(rdev, ring, &ib, NULL, ib_size_dw * 4);
397 0 : if (r) {
398 0 : DRM_ERROR("radeon: failed to get ib (%d).\n", r);
399 0 : return r;
400 : }
401 :
402 0 : dummy = ib.gpu_addr + 1024;
403 :
404 : /* stitch together an VCE create msg */
405 0 : ib.length_dw = 0;
406 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x0000000c); /* len */
407 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001); /* session cmd */
408 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(handle);
409 :
410 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000030); /* len */
411 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x01000001); /* create cmd */
412 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000000);
413 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000042);
414 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x0000000a);
415 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001);
416 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000080);
417 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000060);
418 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000100);
419 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000100);
420 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x0000000c);
421 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000000);
422 :
423 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000014); /* len */
424 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x05000005); /* feedback buffer */
425 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(upper_32_bits(dummy));
426 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(dummy);
427 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001);
428 :
429 0 : for (i = ib.length_dw; i < ib_size_dw; ++i)
430 0 : ib.ptr[i] = cpu_to_le32(0x0);
431 :
432 0 : r = radeon_ib_schedule(rdev, &ib, NULL, false);
433 0 : if (r) {
434 0 : DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
435 0 : }
436 :
437 0 : if (fence)
438 0 : *fence = radeon_fence_ref(ib.fence);
439 :
440 0 : radeon_ib_free(rdev, &ib);
441 :
442 0 : return r;
443 0 : }
444 :
445 : /**
446 : * radeon_vce_get_destroy_msg - generate a VCE destroy msg
447 : *
448 : * @rdev: radeon_device pointer
449 : * @ring: ring we should submit the msg to
450 : * @handle: VCE session handle to use
451 : * @fence: optional fence to return
452 : *
453 : * Close up a stream for HW test or if userspace failed to do so
454 : */
455 0 : int radeon_vce_get_destroy_msg(struct radeon_device *rdev, int ring,
456 : uint32_t handle, struct radeon_fence **fence)
457 : {
458 : const unsigned ib_size_dw = 1024;
459 0 : struct radeon_ib ib;
460 : uint64_t dummy;
461 : int i, r;
462 :
463 0 : r = radeon_ib_get(rdev, ring, &ib, NULL, ib_size_dw * 4);
464 0 : if (r) {
465 0 : DRM_ERROR("radeon: failed to get ib (%d).\n", r);
466 0 : return r;
467 : }
468 :
469 0 : dummy = ib.gpu_addr + 1024;
470 :
471 : /* stitch together an VCE destroy msg */
472 0 : ib.length_dw = 0;
473 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x0000000c); /* len */
474 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001); /* session cmd */
475 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(handle);
476 :
477 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000014); /* len */
478 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x05000005); /* feedback buffer */
479 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(upper_32_bits(dummy));
480 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(dummy);
481 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001);
482 :
483 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000008); /* len */
484 0 : ib.ptr[ib.length_dw++] = cpu_to_le32(0x02000001); /* destroy cmd */
485 :
486 0 : for (i = ib.length_dw; i < ib_size_dw; ++i)
487 0 : ib.ptr[i] = cpu_to_le32(0x0);
488 :
489 0 : r = radeon_ib_schedule(rdev, &ib, NULL, false);
490 0 : if (r) {
491 0 : DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
492 0 : }
493 :
494 0 : if (fence)
495 0 : *fence = radeon_fence_ref(ib.fence);
496 :
497 0 : radeon_ib_free(rdev, &ib);
498 :
499 0 : return r;
500 0 : }
501 :
502 : /**
503 : * radeon_vce_cs_reloc - command submission relocation
504 : *
505 : * @p: parser context
506 : * @lo: address of lower dword
507 : * @hi: address of higher dword
508 : * @size: size of checker for relocation buffer
509 : *
510 : * Patch relocation inside command stream with real buffer address
511 : */
512 0 : int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi,
513 : unsigned size)
514 : {
515 : struct radeon_cs_chunk *relocs_chunk;
516 : struct radeon_bo_list *reloc;
517 : uint64_t start, end, offset;
518 : unsigned idx;
519 :
520 0 : relocs_chunk = p->chunk_relocs;
521 0 : offset = radeon_get_ib_value(p, lo);
522 0 : idx = radeon_get_ib_value(p, hi);
523 :
524 0 : if (idx >= relocs_chunk->length_dw) {
525 0 : DRM_ERROR("Relocs at %d after relocations chunk end %d !\n",
526 : idx, relocs_chunk->length_dw);
527 0 : return -EINVAL;
528 : }
529 :
530 0 : reloc = &p->relocs[(idx / 4)];
531 0 : start = reloc->gpu_offset;
532 0 : end = start + radeon_bo_size(reloc->robj);
533 0 : start += offset;
534 :
535 0 : p->ib.ptr[lo] = start & 0xFFFFFFFF;
536 0 : p->ib.ptr[hi] = start >> 32;
537 :
538 0 : if (end <= start) {
539 0 : DRM_ERROR("invalid reloc offset %llX!\n", offset);
540 0 : return -EINVAL;
541 : }
542 0 : if ((end - start) < size) {
543 0 : DRM_ERROR("buffer to small (%d / %d)!\n",
544 : (unsigned)(end - start), size);
545 0 : return -EINVAL;
546 : }
547 :
548 0 : return 0;
549 0 : }
550 :
551 : /**
552 : * radeon_vce_validate_handle - validate stream handle
553 : *
554 : * @p: parser context
555 : * @handle: handle to validate
556 : * @allocated: allocated a new handle?
557 : *
558 : * Validates the handle and return the found session index or -EINVAL
559 : * we we don't have another free session index.
560 : */
561 0 : static int radeon_vce_validate_handle(struct radeon_cs_parser *p,
562 : uint32_t handle, bool *allocated)
563 : {
564 : unsigned i;
565 :
566 0 : *allocated = false;
567 :
568 : /* validate the handle */
569 0 : for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
570 0 : if (atomic_read(&p->rdev->vce.handles[i]) == handle) {
571 0 : if (p->rdev->vce.filp[i] != p->filp) {
572 0 : DRM_ERROR("VCE handle collision detected!\n");
573 0 : return -EINVAL;
574 : }
575 0 : return i;
576 : }
577 : }
578 :
579 : /* handle not found try to alloc a new one */
580 0 : for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
581 0 : if (!atomic_cmpxchg(&p->rdev->vce.handles[i], 0, handle)) {
582 0 : p->rdev->vce.filp[i] = p->filp;
583 0 : p->rdev->vce.img_size[i] = 0;
584 0 : *allocated = true;
585 0 : return i;
586 : }
587 : }
588 :
589 0 : DRM_ERROR("No more free VCE handles!\n");
590 0 : return -EINVAL;
591 0 : }
592 :
593 : /**
594 : * radeon_vce_cs_parse - parse and validate the command stream
595 : *
596 : * @p: parser context
597 : *
598 : */
599 0 : int radeon_vce_cs_parse(struct radeon_cs_parser *p)
600 : {
601 : int session_idx = -1;
602 0 : bool destroyed = false, created = false, allocated = false;
603 0 : uint32_t tmp, handle = 0;
604 : uint32_t *size = &tmp;
605 : int i, r = 0;
606 :
607 0 : while (p->idx < p->chunk_ib->length_dw) {
608 0 : uint32_t len = radeon_get_ib_value(p, p->idx);
609 0 : uint32_t cmd = radeon_get_ib_value(p, p->idx + 1);
610 :
611 0 : if ((len < 8) || (len & 3)) {
612 0 : DRM_ERROR("invalid VCE command length (%d)!\n", len);
613 : r = -EINVAL;
614 0 : goto out;
615 : }
616 :
617 0 : if (destroyed) {
618 0 : DRM_ERROR("No other command allowed after destroy!\n");
619 : r = -EINVAL;
620 0 : goto out;
621 : }
622 :
623 0 : switch (cmd) {
624 : case 0x00000001: // session
625 0 : handle = radeon_get_ib_value(p, p->idx + 2);
626 0 : session_idx = radeon_vce_validate_handle(p, handle,
627 : &allocated);
628 0 : if (session_idx < 0)
629 0 : return session_idx;
630 0 : size = &p->rdev->vce.img_size[session_idx];
631 0 : break;
632 :
633 : case 0x00000002: // task info
634 : break;
635 :
636 : case 0x01000001: // create
637 : created = true;
638 0 : if (!allocated) {
639 0 : DRM_ERROR("Handle already in use!\n");
640 : r = -EINVAL;
641 0 : goto out;
642 : }
643 :
644 0 : *size = radeon_get_ib_value(p, p->idx + 8) *
645 0 : radeon_get_ib_value(p, p->idx + 10) *
646 0 : 8 * 3 / 2;
647 0 : break;
648 :
649 : case 0x04000001: // config extension
650 : case 0x04000002: // pic control
651 : case 0x04000005: // rate control
652 : case 0x04000007: // motion estimation
653 : case 0x04000008: // rdo
654 : case 0x04000009: // vui
655 : break;
656 :
657 : case 0x03000001: // encode
658 0 : r = radeon_vce_cs_reloc(p, p->idx + 10, p->idx + 9,
659 0 : *size);
660 0 : if (r)
661 0 : goto out;
662 :
663 0 : r = radeon_vce_cs_reloc(p, p->idx + 12, p->idx + 11,
664 0 : *size / 3);
665 0 : if (r)
666 0 : goto out;
667 : break;
668 :
669 : case 0x02000001: // destroy
670 : destroyed = true;
671 0 : break;
672 :
673 : case 0x05000001: // context buffer
674 0 : r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2,
675 0 : *size * 2);
676 0 : if (r)
677 0 : goto out;
678 : break;
679 :
680 : case 0x05000004: // video bitstream buffer
681 0 : tmp = radeon_get_ib_value(p, p->idx + 4);
682 0 : r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2,
683 : tmp);
684 0 : if (r)
685 0 : goto out;
686 : break;
687 :
688 : case 0x05000005: // feedback buffer
689 0 : r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2,
690 : 4096);
691 0 : if (r)
692 0 : goto out;
693 : break;
694 :
695 : default:
696 0 : DRM_ERROR("invalid VCE command (0x%x)!\n", cmd);
697 : r = -EINVAL;
698 0 : goto out;
699 : }
700 :
701 0 : if (session_idx == -1) {
702 0 : DRM_ERROR("no session command at start of IB\n");
703 : r = -EINVAL;
704 0 : goto out;
705 : }
706 :
707 0 : p->idx += len / 4;
708 0 : }
709 :
710 0 : if (allocated && !created) {
711 0 : DRM_ERROR("New session without create command!\n");
712 : r = -ENOENT;
713 0 : }
714 :
715 : out:
716 0 : if ((!r && destroyed) || (r && allocated)) {
717 : /*
718 : * IB contains a destroy msg or we have allocated an
719 : * handle and got an error, anyway free the handle
720 : */
721 0 : for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i)
722 0 : atomic_cmpxchg(&p->rdev->vce.handles[i], handle, 0);
723 : }
724 :
725 0 : return r;
726 0 : }
727 :
728 : /**
729 : * radeon_vce_semaphore_emit - emit a semaphore command
730 : *
731 : * @rdev: radeon_device pointer
732 : * @ring: engine to use
733 : * @semaphore: address of semaphore
734 : * @emit_wait: true=emit wait, false=emit signal
735 : *
736 : */
737 0 : bool radeon_vce_semaphore_emit(struct radeon_device *rdev,
738 : struct radeon_ring *ring,
739 : struct radeon_semaphore *semaphore,
740 : bool emit_wait)
741 : {
742 0 : uint64_t addr = semaphore->gpu_addr;
743 :
744 0 : radeon_ring_write(ring, cpu_to_le32(VCE_CMD_SEMAPHORE));
745 0 : radeon_ring_write(ring, cpu_to_le32((addr >> 3) & 0x000FFFFF));
746 0 : radeon_ring_write(ring, cpu_to_le32((addr >> 23) & 0x000FFFFF));
747 0 : radeon_ring_write(ring, cpu_to_le32(0x01003000 | (emit_wait ? 1 : 0)));
748 0 : if (!emit_wait)
749 0 : radeon_ring_write(ring, cpu_to_le32(VCE_CMD_END));
750 :
751 0 : return true;
752 : }
753 :
754 : /**
755 : * radeon_vce_ib_execute - execute indirect buffer
756 : *
757 : * @rdev: radeon_device pointer
758 : * @ib: the IB to execute
759 : *
760 : */
761 0 : void radeon_vce_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
762 : {
763 0 : struct radeon_ring *ring = &rdev->ring[ib->ring];
764 0 : radeon_ring_write(ring, cpu_to_le32(VCE_CMD_IB));
765 0 : radeon_ring_write(ring, cpu_to_le32(ib->gpu_addr));
766 0 : radeon_ring_write(ring, cpu_to_le32(upper_32_bits(ib->gpu_addr)));
767 0 : radeon_ring_write(ring, cpu_to_le32(ib->length_dw));
768 0 : }
769 :
770 : /**
771 : * radeon_vce_fence_emit - add a fence command to the ring
772 : *
773 : * @rdev: radeon_device pointer
774 : * @fence: the fence
775 : *
776 : */
777 0 : void radeon_vce_fence_emit(struct radeon_device *rdev,
778 : struct radeon_fence *fence)
779 : {
780 0 : struct radeon_ring *ring = &rdev->ring[fence->ring];
781 0 : uint64_t addr = rdev->fence_drv[fence->ring].gpu_addr;
782 :
783 0 : radeon_ring_write(ring, cpu_to_le32(VCE_CMD_FENCE));
784 0 : radeon_ring_write(ring, cpu_to_le32(addr));
785 0 : radeon_ring_write(ring, cpu_to_le32(upper_32_bits(addr)));
786 0 : radeon_ring_write(ring, cpu_to_le32(fence->seq));
787 0 : radeon_ring_write(ring, cpu_to_le32(VCE_CMD_TRAP));
788 0 : radeon_ring_write(ring, cpu_to_le32(VCE_CMD_END));
789 0 : }
790 :
791 : /**
792 : * radeon_vce_ring_test - test if VCE ring is working
793 : *
794 : * @rdev: radeon_device pointer
795 : * @ring: the engine to test on
796 : *
797 : */
798 0 : int radeon_vce_ring_test(struct radeon_device *rdev, struct radeon_ring *ring)
799 : {
800 0 : uint32_t rptr = vce_v1_0_get_rptr(rdev, ring);
801 : unsigned i;
802 : int r;
803 :
804 0 : r = radeon_ring_lock(rdev, ring, 16);
805 0 : if (r) {
806 0 : DRM_ERROR("radeon: vce failed to lock ring %d (%d).\n",
807 : ring->idx, r);
808 0 : return r;
809 : }
810 0 : radeon_ring_write(ring, cpu_to_le32(VCE_CMD_END));
811 0 : radeon_ring_unlock_commit(rdev, ring, false);
812 :
813 0 : for (i = 0; i < rdev->usec_timeout; i++) {
814 0 : if (vce_v1_0_get_rptr(rdev, ring) != rptr)
815 : break;
816 0 : DRM_UDELAY(1);
817 : }
818 :
819 0 : if (i < rdev->usec_timeout) {
820 : DRM_INFO("ring test on %d succeeded in %d usecs\n",
821 : ring->idx, i);
822 : } else {
823 0 : DRM_ERROR("radeon: ring %d test failed\n",
824 : ring->idx);
825 : r = -ETIMEDOUT;
826 : }
827 :
828 0 : return r;
829 0 : }
830 :
831 : /**
832 : * radeon_vce_ib_test - test if VCE IBs are working
833 : *
834 : * @rdev: radeon_device pointer
835 : * @ring: the engine to test on
836 : *
837 : */
838 0 : int radeon_vce_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
839 : {
840 0 : struct radeon_fence *fence = NULL;
841 : int r;
842 :
843 0 : r = radeon_vce_get_create_msg(rdev, ring->idx, 1, NULL);
844 0 : if (r) {
845 0 : DRM_ERROR("radeon: failed to get create msg (%d).\n", r);
846 0 : goto error;
847 : }
848 :
849 0 : r = radeon_vce_get_destroy_msg(rdev, ring->idx, 1, &fence);
850 0 : if (r) {
851 0 : DRM_ERROR("radeon: failed to get destroy ib (%d).\n", r);
852 0 : goto error;
853 : }
854 :
855 0 : r = radeon_fence_wait(fence, false);
856 0 : if (r) {
857 0 : DRM_ERROR("radeon: fence wait failed (%d).\n", r);
858 0 : } else {
859 : DRM_INFO("ib test on ring %d succeeded\n", ring->idx);
860 : }
861 : error:
862 0 : radeon_fence_unref(&fence);
863 0 : return r;
864 0 : }
|