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