// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2019 MediaTek Inc.
 */
#include <linux/completion.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/version.h>
#include <linux/sched/clock.h>
#include <asm-generic/div64.h>

#include <apusys_device.h>
#include <utilities/mdla_debug.h>
#include "mdla_cmd_data_v2_0.h"
#include "comm_driver.h"

#define MTK_IOVA_START_ADDR (0x200000000ULL)  //8G

int check_cmdbuf(struct command_entry *ce)
{
#ifdef MDLA_CMDBUF_DATA_CHECK
	u32 verid;
	u32 *p = (u32 *)(uintptr_t)ce->kva;
	int i, j;

	/* use the first verid as golden verid */
	verid = *(p + 0x1bc/4);

	for (i = 0; i < ce->count; i++) {
		if (*(p + 0x1bc/4) != verid) {
			mdla_drv_debug("%s(): count:%x verid:%x val:%x\n",
					__func__, i, verid, *(p + 0x1bc/4));
			goto fail_dump;
		}

		p += 0x1c0/4;
	}

	return 0;

fail_dump:

	p = (u32 *)(uintptr_t)ce->kva;
	for (i = 0; i < ce->count; i++) {
		for (j = 0; j < 0x1c0/4; j++) {
			mdla_drv_debug("%s(): count:%x offset:%x val:%x\n",
					__func__, i, j * 4, *(p + j));
		}
		p += 0x1c0/4;
	}
	return -EINVAL;
#else
	return 0;
#endif
}

static inline void mdla_fill_kmem(u32 iova, u64 size, u8 iommu, struct comm_kmem *mem)
{
	if (mem) {
		memset(mem, 0, sizeof(*mem));

		mem->iova = iova;
		mem->size = size;

		if (iommu == 1) {
			mem->iova |= MTK_IOVA_START_ADDR;
			mem->mem_type = APU_COMM_MEM_DRAM_DMA;
		} else {
			mem->mem_type = APU_COMM_MEM_VLM;
		}
	}
}

void *mdla_buf_get(void *cmd_info, u64 iova, u32 size)
{
	struct comm_kmem mem;
	struct comm_user *user;

	if (!IS_ALIGNED(iova, 4))
		return NULL;

	mdla_fill_kmem(iova, size, 1, &mem);

	if (!cmd_info) {
		if (comm_util_get_cb()->query_mem(&mem,
						COMM_QUERY_BY_IOVA,
						NULL))
			return NULL;

	} else {
		user = ((struct mdla_cmd_info *)cmd_info)->cmd_user;

		if (comm_util_get_cb()->get_user_mem(user, &mem))
			return NULL;
	}

	if (mem.status & (1 << COMM_MEM_OBSOLETE))
		return NULL;

	return (void *)(uintptr_t)mem.kva;
}

void mdla_buf_put(void *cmd_info, u64 iova, u32 size)
{
	struct comm_kmem mem;
	struct comm_user *user;

	if (!IS_ALIGNED(iova, 4))
		return;

	mdla_fill_kmem(iova, size, 1, &mem);

	if (cmd_info) {
		user = ((struct mdla_cmd_info *)cmd_info)->cmd_user;

		if (comm_util_get_cb()->put_user_mem(user, &mem))
			return;
	}
}

void *mdla_buf_map(void *cmd_info, u64 iova, u32 size)
{
	struct comm_kmem mem;
	struct comm_user *user;

	if (!IS_ALIGNED(iova, 4))
		return NULL;

	mdla_fill_kmem(iova, size, 1, &mem);

	if (!cmd_info) {
		if (comm_util_get_cb()->query_mem(&mem,
						COMM_QUERY_BY_IOVA,
						NULL))
			return NULL;

	} else {
		user = ((struct mdla_cmd_info *)cmd_info)->cmd_user;

		if (comm_util_get_cb()->search_user_mem(user,
						&mem,
						COMM_CMP_IOVA,
						NULL))
			return NULL;
	}

	if (mem.status & (1 << COMM_MEM_OBSOLETE))
		return NULL;

	return (void *)(uintptr_t)mem.kva;
}

