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