/*
 * Copyright © 2019 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 */

#include <i915_drm.h>
#include "drmtest.h"
#include "huc_copy.h"
#include "intel_allocator.h"

static void
gen9_emit_huc_virtual_addr_state(struct drm_i915_gem_exec_object2 *src,
		struct drm_i915_gem_exec_object2 *dst,
		struct drm_i915_gem_relocation_entry *reloc_src,
		struct drm_i915_gem_relocation_entry *reloc_dst,
		uint32_t *buf,
		int *i)
{
	buf[(*i)++] = HUC_VIRTUAL_ADDR_STATE;

	for (int j = 0; j < HUC_VIRTUAL_ADDR_REGION_NUM; j++) {
		if (j == HUC_VIRTUAL_ADDR_REGION_SRC) {
			buf[(*i)++] = src->offset;

			reloc_src->target_handle = src->handle;
			reloc_src->presumed_offset = src->offset;
			reloc_src->delta = 0;
			reloc_src->offset = (*i - 1) * sizeof(buf[0]);
			reloc_src->read_domains = 0;
			reloc_src->write_domain = 0;
		} else if (j == HUC_VIRTUAL_ADDR_REGION_DST) {
			buf[(*i)++] = dst->offset;

			reloc_dst->target_handle = dst->handle;
			reloc_dst->presumed_offset = dst->offset;
			reloc_dst->delta = 0;
			reloc_dst->offset = (*i - 1) * sizeof(buf[0]);
			reloc_dst->read_domains = 0;
			reloc_dst->write_domain = I915_GEM_DOMAIN_RENDER;
		} else {
			buf[(*i)++] = 0;
		}
		buf[(*i)++] = 0;
		buf[(*i)++] = 0;
	}
}

void
gen9_huc_copyfunc(int fd, uint64_t ahnd,
		  struct drm_i915_gem_exec_object2 *obj, uint64_t *objsize)
{
	struct drm_i915_gem_relocation_entry reloc[2];
	struct drm_i915_gem_execbuffer2 execbuf;
	int i = 0;
	uint32_t buf[63];

	/* load huc kernel */
	buf[i++] = HUC_IMEM_STATE;
	buf[i++] = 0;
	buf[i++] = 0;
	buf[i++] = 0;
	buf[i++] = 0x3;

	buf[i++] = HUC_MFX_WAIT;
	buf[i++] = HUC_MFX_WAIT;

	buf[i++] = HUC_PIPE_MODE_SELECT;
	buf[i++] = 0;
	buf[i++] = 0;

	buf[i++] = HUC_MFX_WAIT;

	memset(reloc, 0, sizeof(reloc));

	if (ahnd) {
		obj[0].flags = EXEC_OBJECT_PINNED;
		obj[1].flags = EXEC_OBJECT_PINNED | EXEC_OBJECT_WRITE;
		obj[2].flags = EXEC_OBJECT_PINNED;
		obj[0].offset = get_offset(ahnd, obj[0].handle, objsize[0], 0);
		obj[1].offset = get_offset(ahnd, obj[1].handle, objsize[1], 0);
		obj[2].offset = get_offset(ahnd, obj[2].handle, objsize[2], 0);
	} else {
		obj[0].offset = 1 << 20;
		obj[1].offset = ALIGN(obj[0].offset + objsize[0], 1 << 20);
		obj[2].offset = ALIGN(obj[1].offset + objsize[1], 1 << 20);
		obj[1].flags = EXEC_OBJECT_WRITE;
	}

	gen9_emit_huc_virtual_addr_state(&obj[0], &obj[1], &reloc[0], &reloc[1], buf, &i);

	buf[i++] = HUC_START;
	buf[i++] = 1;

	buf[i++] = MI_BATCH_BUFFER_END;

	gem_write(fd, obj[2].handle, 0, buf, sizeof(buf));
	obj[2].relocation_count = !ahnd ? 2 : 0;
	obj[2].relocs_ptr = to_user_pointer(reloc);

	memset(&execbuf, 0, sizeof(execbuf));
	execbuf.buffers_ptr = to_user_pointer(obj);
	execbuf.buffer_count = 3;
	execbuf.flags = I915_EXEC_BSD | I915_EXEC_NO_RELOC;

	gem_execbuf(fd, &execbuf);
}
