// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2020 MediaTek Inc.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/uuid.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
#include "tee_drv_5_15_13.h"
#else
#include <linux/tee_drv.h>
#endif
#include <linux/slab.h>
#include <utpa2_Types.h>
#include <utpa2_DMX.h>

#include "mtk_iommu_dtv_api.h"

#include "mdrv_demux_common.h"
#include "mdrv_demux_debug.h"
#include "mdrv_demux_resource.h"
#include "mdrv_demux_tee_if.h"
#include "mdrv_demux_tee_comm.h"

#define DMX_TA_UUID				UUID_INIT(0x7a7c8b61, 0x5d74, 0x4b6c, \
								0xb2, 0xa9, 0x92, 0xe4, \
								0x9c, 0xe8, 0x66, 0xbd)

#define REC_TA_UUID				UUID_INIT(0x4e501575, 0xee69, 0x4eee, \
								0x94, 0xe1, 0x26, 0xb9, \
								0xed, 0x96, 0xc8, 0x5a)

#define PVR_NUM					4

#define DMX_ADDR_SHIFT				32

#define TEE_COMMON_ARG_LENGTH			16
#define TZ_CACHE_LINE_SIZE			64

#define TZ_CACHE_LINE_SIZE_MSK  (TZ_CACHE_LINE_SIZE - 1)
#define TZ_CACHE_ALIGN(x)       (((x) + TZ_CACHE_LINE_SIZE_MSK) & ~TZ_CACHE_LINE_SIZE_MSK)

typedef enum {
	DMX_TA_CMD_NULL,
	DMX_TA_CMD_LOAD_FW,
	DMX_TA_CMD_SET_VQ_BUF,
	DMX_TA_CMD_SET_PVR_BUF,
	DMX_TA_CMD_SET_FILEIN_BUF,
	DMX_TA_CMD_GET_PVR_WPTR,
	DMX_TA_CMD_SET_MMFI_BUF,
	DMX_TA_CMD_SET_PVR_SRC,
	DMX_TA_CMD_BIND_PIPEID
} dmx_ta_cmd_type_t;

typedef enum {
	DMX_TA_PIPE_TSPFW	= 0x00000001,
	DMX_TA_PIPE_TSPVQ	= 0x00000002,
	DMX_TA_PIPE_PVR0	= 0x80000000,
	DMX_TA_PIPE_PVR1	= 0x80000001,
	DMX_TA_PIPE_PVR2	= 0x80000002,
	DMX_TA_PIPE_PVR3	= 0x80000003,
	DMX_TA_PIPE_FILE0	= 0x40000000,
	DMX_TA_PIPE_FILE1	= 0x40000001,
	DMX_TA_PIPE_FILE2	= 0x40000002,
	DMX_TA_PIPE_MMFI0	= 0x41000000,
	DMX_TA_PIPE_MMFI1	= 0x41000001,
	DMX_TA_PIPE_NULL	= 0xFFFFFFFF
} dmx_ta_pipe_type_t;

typedef enum {
	REC_TA_CMD_GET_PIPELINE_ID,
	REC_TA_CMD_CONFIGURE,
	REC_TA_CMD_OPERATE,
	REC_TA_CMD_RELEASE
} rec_ta_cmd_type_t;

typedef union fixed_size_pointer {
	void *ptr;
	u32 val;
	u64 sizer;
} fixed_size_pointer;

typedef struct {
	u32 ui4Command;
	fixed_size_pointer apvArgument[TEE_COMMON_ARG_LENGTH];
	u32 ui4Length[TEE_COMMON_ARG_LENGTH];
	bool abWrite[TEE_COMMON_ARG_LENGTH];
	u8 ui1Size;
} TEE_COMMON_ARG_T;

struct dmx_tee_context {
	bool inited;
	u32 dmx_ta_session_id;
	u32 rec_ta_session_id;
	u32 pipeline_id[PVR_NUM]; // @TODO: remove it & get id from CA DD
};

static DECLARE_RWSEM(tee_rwsem);
static struct dmx_tee_context dmx_tee_ctx;

static inline bool _is_dmx_tee_ctx_inited(void)
{
	bool ret;

	down_read(&tee_rwsem);
	ret = dmx_tee_ctx.inited;
	up_read(&tee_rwsem);
	return ret;
}

static inline u32 _dmx_tee_get_dmx_ta_session_id(void)
{
	u32 session_id;

	down_read(&tee_rwsem);
	session_id = dmx_tee_ctx.dmx_ta_session_id;
	up_read(&tee_rwsem);
	return session_id;
}

static inline u32 _dmx_tee_get_rec_ta_session_id(void)
{
	u32 session_id;

	down_read(&tee_rwsem);
	session_id = dmx_tee_ctx.rec_ta_session_id;
	up_read(&tee_rwsem);
	return session_id;
}

// @TODO: remove it & get id from CA DD
static int _dmx_tee_get_pvr_pipeline_id(int pvr_eng_id)
{
	int ret;
	u32 pipeline_id;

	if (pvr_eng_id >= PVR_NUM) {
		DEMUX_DEBUG_ERR("[%s][%d] eng id out-of-range\n", __func__, __LINE__);
		return -EINVAL;
	}

	down_read(&tee_rwsem);
	pipeline_id = dmx_tee_ctx.pipeline_id[pvr_eng_id];
	up_read(&tee_rwsem);

	if (!pipeline_id) {
		TEE_COMMON_ARG_T *rec_ta_param;
		u32 rec_ta_session_id;
		u64 keyToken = pvr_eng_id;
		u8 *serialized_buf = NULL;
		u32 serialized_buf_size = 0;
		u32 serialized_buf_offset = TZ_CACHE_ALIGN(sizeof(TEE_COMMON_ARG_T));

		rec_ta_session_id = _dmx_tee_get_rec_ta_session_id();
		if (!rec_ta_session_id) {
			DEMUX_DEBUG_ERR("[%s][%d] rec ta session id invalid\n", __func__, __LINE__);
			return -EFAULT;
		}

		serialized_buf_size = TZ_CACHE_ALIGN(sizeof(TEE_COMMON_ARG_T));
		serialized_buf_size += TZ_CACHE_ALIGN(sizeof(u32));	// result
		serialized_buf_size += TZ_CACHE_ALIGN(sizeof(u64));	// token
		serialized_buf_size += TZ_CACHE_ALIGN(sizeof(u32));	// pipeline

		serialized_buf = kmalloc(serialized_buf_size, GFP_KERNEL);
		if (!serialized_buf) {
			DEMUX_DEBUG_ERR("[%s][%d] out-of-memory\n", __func__, __LINE__);
			return -ENOMEM;
		}

		memset(serialized_buf, 0, serialized_buf_size);

		rec_ta_param = (TEE_COMMON_ARG_T *)serialized_buf;
		rec_ta_param->ui4Command = REC_TA_CMD_GET_PIPELINE_ID;
		rec_ta_param->ui1Size = 3;

		rec_ta_param->apvArgument[0].ptr = (u8 *)(serialized_buf + serialized_buf_offset);
		rec_ta_param->ui4Length[0] = sizeof(u32);
		rec_ta_param->abWrite[0] = 1;
		serialized_buf_offset += TZ_CACHE_ALIGN(sizeof(u32));

		rec_ta_param->apvArgument[1].ptr = (u8 *)(serialized_buf + serialized_buf_offset);
		rec_ta_param->ui4Length[1] = sizeof(u64);
		rec_ta_param->abWrite[1] = 0;
		memcpy(rec_ta_param->apvArgument[1].ptr, &keyToken, sizeof(u64));
		serialized_buf_offset += TZ_CACHE_ALIGN(sizeof(u64));

		rec_ta_param->apvArgument[2].ptr = (u8 *)(serialized_buf + serialized_buf_offset);
		rec_ta_param->ui4Length[2] = sizeof(u32);
		rec_ta_param->abWrite[2] = 1;

		ret = mdrv_demux_tee_invoke_cmd(
			rec_ta_session_id,
			0,
			serialized_buf,
			serialized_buf_size);
		if (ret < 0) {
			DEMUX_DEBUG_ERR("[%s][%d] invoke cmd fail\n",
				__func__, __LINE__);
			kfree(serialized_buf);
			return -EFAULT;
		}

		pipeline_id = *((u32 *)(rec_ta_param->apvArgument[2].ptr));

		down_write(&tee_rwsem);
		dmx_tee_ctx.pipeline_id[pvr_eng_id] = pipeline_id;
		up_write(&tee_rwsem);

		kfree(serialized_buf);
		DEMUX_DEBUG_ERR("[%s][%d] pipeline id = %x\n", __func__, __LINE__, pipeline_id);
	}

	return pipeline_id;
}

static int _dmx_tee_put_pvr_pipeline_id(int pvr_eng_id)
{
	int ret;
	u32 pipeline_id;

	if (pvr_eng_id >= PVR_NUM) {
		DEMUX_DEBUG_ERR("[%s][%d] eng id out-of-range\n", __func__, __LINE__);
		return -EINVAL;
	}

	down_read(&tee_rwsem);
	pipeline_id = dmx_tee_ctx.pipeline_id[pvr_eng_id];
	up_read(&tee_rwsem);

	if (pipeline_id > 0) {
		TEE_COMMON_ARG_T *rec_ta_param;
		u32 rec_ta_session_id;
		u64 keyToken = pvr_eng_id;
		u8 *serialized_buf = NULL;
		u32 serialized_buf_size = 0;
		u32 serialized_buf_offset = TZ_CACHE_ALIGN(sizeof(TEE_COMMON_ARG_T));

		rec_ta_session_id = _dmx_tee_get_rec_ta_session_id();
		if (!rec_ta_session_id) {
			DEMUX_DEBUG_ERR("[%s][%d] rec ta session id invalid\n", __func__, __LINE__);
			return -EFAULT;
		}

		serialized_buf_size = TZ_CACHE_ALIGN(sizeof(TEE_COMMON_ARG_T));
		serialized_buf_size += TZ_CACHE_ALIGN(sizeof(u32));	// result
		serialized_buf_size += TZ_CACHE_ALIGN(sizeof(u64));	// token

		serialized_buf = kmalloc(serialized_buf_size, GFP_KERNEL);
		if (!serialized_buf) {
			DEMUX_DEBUG_ERR("[%s][%d] out-of-memory\n", __func__, __LINE__);
			return -ENOMEM;
		}

		memset(serialized_buf, 0, serialized_buf_size);

		rec_ta_param = (TEE_COMMON_ARG_T *)serialized_buf;
		rec_ta_param->ui4Command = REC_TA_CMD_RELEASE;
		rec_ta_param->ui1Size = 2;

		rec_ta_param->apvArgument[0].ptr = (u8 *)(serialized_buf + serialized_buf_offset);
		rec_ta_param->ui4Length[0] = sizeof(u32);
		rec_ta_param->abWrite[0] = 1;
		serialized_buf_offset += TZ_CACHE_ALIGN(sizeof(u32));

		rec_ta_param->apvArgument[1].ptr = (u8 *)(serialized_buf + serialized_buf_offset);
		rec_ta_param->ui4Length[1] = sizeof(u64);
		rec_ta_param->abWrite[1] = 0;
		memcpy(rec_ta_param->apvArgument[1].ptr, &keyToken, sizeof(u64));

		ret = mdrv_demux_tee_invoke_cmd(
			rec_ta_session_id,
			0,
			serialized_buf,
			serialized_buf_size);
		if (ret < 0) {
			DEMUX_DEBUG_ERR("[%s][%d] invoke cmd fail\n",
				__func__, __LINE__);
			kfree(serialized_buf);
			return -EFAULT;
		}

		down_write(&tee_rwsem);
		dmx_tee_ctx.pipeline_id[pvr_eng_id] = 0;
		up_write(&tee_rwsem);

		kfree(serialized_buf);
	}

	return 0;
}

static int _dmx_tee_init_context(void)
{
	int ret = 0;

	down_write(&tee_rwsem);

	ret = mdrv_demux_tee_open_ctx();
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] open tee ctx fail\n", __func__, __LINE__);
		goto FAIL;
	}

	ret = mdrv_demux_tee_open_session(&dmx_tee_ctx.dmx_ta_session_id, DMX_TA_UUID);
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] open session fail\n", __func__, __LINE__);
		goto FAIL;
	}

	ret = mdrv_demux_tee_open_session(&dmx_tee_ctx.rec_ta_session_id, REC_TA_UUID);
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] open session fail\n", __func__, __LINE__);
		goto FAIL;
	}

	dmx_tee_ctx.inited = true;
	up_write(&tee_rwsem);

	DEMUX_DEBUG_ERR("[%s][%d] dmx tee init ctx\n", __func__, __LINE__);
	return 0;

FAIL:
	up_write(&tee_rwsem);
	return -EFAULT;
}

static int _dmx_tee_exit_context(void)
{
	int ret;

	if (!_is_dmx_tee_ctx_inited()) {
		DEMUX_DEBUG_ERR("[%s][%d] already exit\n", __func__, __LINE__);
		return -EFAULT;
	}

	down_write(&tee_rwsem);

	ret = mdrv_demux_tee_close_session(dmx_tee_ctx.rec_ta_session_id);
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] close session fail\n", __func__, __LINE__);
		goto FAIL;
	}

	ret = mdrv_demux_tee_close_session(dmx_tee_ctx.dmx_ta_session_id);
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] close session fail\n", __func__, __LINE__);
		goto FAIL;
	}

	ret = mdrv_demux_tee_close_ctx();
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] close tee ctx fail\n", __func__, __LINE__);
		goto FAIL;
	}

	dmx_tee_ctx.inited = false;
	up_write(&tee_rwsem);
	return 0;

FAIL:
	up_write(&tee_rwsem);
	return -EFAULT;
}

int mdrv_demux_tee_load_fw(void)
{
	int ret;
	u32 dmx_ta_session_id;
	DMX_TEE_BUF_Param dmx_ta_param;

	if (!_is_dmx_tee_ctx_inited())
		_dmx_tee_init_context();

	dmx_ta_session_id = _dmx_tee_get_dmx_ta_session_id();
	if (!dmx_ta_session_id) {
		DEMUX_DEBUG_ERR("[%s][%d] dmx ta session id invalid\n", __func__, __LINE__);
		return -EFAULT;
	}

	memset(&dmx_ta_param, 0, sizeof(dmx_ta_param));
	ret = mdrv_demux_tee_invoke_cmd(
		dmx_ta_session_id,
		DMX_TA_CMD_LOAD_FW,
		&dmx_ta_param,
		sizeof(dmx_ta_param));
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] load tsp fw fail\n", __func__, __LINE__);
		return -EFAULT;
	}

	return 0;
}

int mdrv_demux_tee_set_vq(void)
{
	int ret;
	u32 dmx_ta_session_id;
	DMX_TEE_BUF_Param dmx_ta_param;

	if (!_is_dmx_tee_ctx_inited())
		_dmx_tee_init_context();

	dmx_ta_session_id = _dmx_tee_get_dmx_ta_session_id();
	if (!dmx_ta_session_id) {
		DEMUX_DEBUG_ERR("[%s][%d] dmx ta session id invalid\n", __func__, __LINE__);
		return -EFAULT;
	}

	memset(&dmx_ta_param, 0, sizeof(dmx_ta_param));
	ret = mdrv_demux_tee_invoke_cmd(
		dmx_ta_session_id,
		DMX_TA_CMD_SET_VQ_BUF,
		&dmx_ta_param,
		sizeof(dmx_ta_param));
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] set vq buffer fail\n", __func__, __LINE__);
		return -EFAULT;
	}

	return 0;
}

int mdrv_demux_tee_lock_pvr_buffer(int pvr_eng_id, struct mem_info *pvr_buffer)
{
	int ret, pipeline_id;
	u32 dmx_ta_session_id;
	DMX_TEE_BUF_Param dmx_ta_param;

	if (!_is_dmx_tee_ctx_inited())
		_dmx_tee_init_context();

	// @TODO: remove it & get id from CA DD
	pipeline_id = _dmx_tee_get_pvr_pipeline_id(pvr_eng_id);

	dmx_ta_session_id = _dmx_tee_get_dmx_ta_session_id();
	if (!dmx_ta_session_id) {
		DEMUX_DEBUG_ERR("[%s][%d] dmx ta session id invalid\n", __func__, __LINE__);
		return -EFAULT;
	}

	memset(&dmx_ta_param, 0, sizeof(dmx_ta_param));
	dmx_ta_param.u32PipelineId = (MS_U32)pipeline_id;
	dmx_ta_param.u32PipeId = DMX_TA_PIPE_PVR0 + pvr_eng_id;
	ret = mdrv_demux_tee_invoke_cmd(
		dmx_ta_session_id,
		DMX_TA_CMD_BIND_PIPEID,
		&dmx_ta_param,
		sizeof(dmx_ta_param));
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] bind pipeline id fail\n", __func__, __LINE__);
		return -EFAULT;
	}

	ret = mtkd_iommu_buffer_authorize(
		PVR_BUF_ID,
		pvr_buffer->paddr,
		pvr_buffer->size,
		pipeline_id);
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] authorize buffer fail\n", __func__, __LINE__);
		return -EFAULT;
	}

	memset(&dmx_ta_param, 0, sizeof(dmx_ta_param));
	dmx_ta_param.u32EngId = pvr_eng_id;
	dmx_ta_param.u32Opt = 0;
	dmx_ta_param.u32BufAddr = (MS_U32)pvr_buffer->paddr;
	dmx_ta_param.u32BufAddr_H = (MS_U32)(pvr_buffer->paddr >> DMX_ADDR_SHIFT);
	dmx_ta_param.u32BufSize = pvr_buffer->size;
	ret = mdrv_demux_tee_invoke_cmd(
		dmx_ta_session_id,
		DMX_TA_CMD_SET_PVR_BUF,
		&dmx_ta_param,
		sizeof(dmx_ta_param));
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] set pvr buffer fail\n", __func__, __LINE__);
		return -EFAULT;
	}

	return 0;
}

int mdrv_demux_tee_unlock_pvr_buffer(int pvr_eng_id, struct mem_info *pvr_buffer)
{
	int ret;
	u32 dmx_ta_session_id;
	DMX_TEE_BUF_Param dmx_ta_param;

	if (!_is_dmx_tee_ctx_inited()) {
		DEMUX_DEBUG_ERR("[%s][%d] context not inited\n", __func__, __LINE__);
		return -EFAULT;
	}

	dmx_ta_session_id = _dmx_tee_get_dmx_ta_session_id();
	if (!dmx_ta_session_id) {
		DEMUX_DEBUG_ERR("[%s][%d] dmx ta session id invalid\n", __func__, __LINE__);
		return -EFAULT;
	}

	memset(&dmx_ta_param, 0, sizeof(dmx_ta_param));
	dmx_ta_param.u32EngId = pvr_eng_id;
	dmx_ta_param.u32Opt = 0;
	dmx_ta_param.u32BufAddr = 0;
	dmx_ta_param.u32BufAddr_H = 0;
	dmx_ta_param.u32BufSize = 0;
	ret = mdrv_demux_tee_invoke_cmd(
		dmx_ta_session_id,
		DMX_TA_CMD_SET_PVR_BUF,
		&dmx_ta_param,
		sizeof(dmx_ta_param));
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] set pvr buffer fail\n", __func__, __LINE__);
		return -EFAULT;
	}

	ret = mtkd_iommu_buffer_unauthorize(pvr_buffer->paddr);
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] un-authorize buffer fail\n", __func__, __LINE__);
		return -EFAULT;
	}

	// @TODO: remove it & get id from CA DD
	if (_dmx_tee_put_pvr_pipeline_id(pvr_eng_id) < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] put pvr pipeline id fail\n", __func__, __LINE__);
		return -EFAULT;
	}

	return 0;
}

int mdrv_demux_tee_pvr_encrypt_config(int pvr_eng_id, struct pvr_encrypt_config *cfg)
{
	int ret;
	u32 rec_ta_session_id;
	size_t len;
	TEE_COMMON_ARG_T *rec_ta_param;
	u64 keyToken = pvr_eng_id;
	u8 *serialized_buf = NULL;
	u32 serialized_buf_size = 0;
	u32 serialized_buf_offset = TZ_CACHE_ALIGN(sizeof(TEE_COMMON_ARG_T));

	if (!_is_dmx_tee_ctx_inited())
		_dmx_tee_init_context();

	rec_ta_session_id = _dmx_tee_get_rec_ta_session_id();
	if (!rec_ta_session_id) {
		DEMUX_DEBUG_ERR("[%s][%d] rec ta session id invalid\n", __func__, __LINE__);
		return -EFAULT;
	}

	if (cfg->pid_num >= PID_INFO_MAX_NUM) {
		DEMUX_DEBUG_ERR("[%s][%d] pid num out-of-range\n", __func__, __LINE__);
		return -EFAULT;
	}

	len = cfg->pid_num * sizeof(struct pvr_pid_info);

	serialized_buf_size = TZ_CACHE_ALIGN(sizeof(TEE_COMMON_ARG_T));
	serialized_buf_size += TZ_CACHE_ALIGN(sizeof(u32));	// result
	serialized_buf_size += TZ_CACHE_ALIGN(sizeof(u64));	// token
	serialized_buf_size += TZ_CACHE_ALIGN(len);			// pid info

	serialized_buf = kmalloc(serialized_buf_size, GFP_KERNEL);
	if (!serialized_buf) {
		DEMUX_DEBUG_ERR("[%s][%d] out-of-memory\n", __func__, __LINE__);
		return -ENOMEM;
	}

	memset(serialized_buf, 0, serialized_buf_size);

	rec_ta_param = (TEE_COMMON_ARG_T *)serialized_buf;
	rec_ta_param->ui4Command = REC_TA_CMD_CONFIGURE;
	rec_ta_param->ui1Size = 6;

	rec_ta_param->apvArgument[0].ptr = (u8 *)(serialized_buf + serialized_buf_offset);
	rec_ta_param->ui4Length[0] = sizeof(u32);
	rec_ta_param->abWrite[0] = 1;
	serialized_buf_offset += TZ_CACHE_ALIGN(sizeof(u32));

	rec_ta_param->apvArgument[1].ptr = (u8 *)(serialized_buf + serialized_buf_offset);
	rec_ta_param->ui4Length[1] = sizeof(u64);
	rec_ta_param->abWrite[1] = 0;
	memcpy(rec_ta_param->apvArgument[1].ptr, &keyToken, sizeof(u64));
	serialized_buf_offset += TZ_CACHE_ALIGN(sizeof(u64));

	rec_ta_param->apvArgument[2].val = cfg->format;
	rec_ta_param->ui4Length[2] = 0;
	rec_ta_param->abWrite[2] = 0;

	rec_ta_param->apvArgument[3].val = cfg->type;
	rec_ta_param->ui4Length[3] = 0;
	rec_ta_param->abWrite[3] = 0;

	rec_ta_param->apvArgument[4].val = cfg->pid_num;
	rec_ta_param->ui4Length[4] = 0;
	rec_ta_param->abWrite[4] = 0;

	rec_ta_param->apvArgument[5].ptr = (u8 *)(serialized_buf + serialized_buf_offset);
	rec_ta_param->ui4Length[5] = len;
	rec_ta_param->abWrite[5] = 1;
	memcpy(rec_ta_param->apvArgument[5].ptr, cfg->pid_info, len);

	ret = mdrv_demux_tee_invoke_cmd(
		rec_ta_session_id,
		0,
		serialized_buf,
		serialized_buf_size);
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] invoke cmd fail\n",
			__func__, __LINE__);
		kfree(serialized_buf);
		return -EFAULT;
	}

	kfree(serialized_buf);
	return 0;
}

int mdrv_demux_tee_pvr_encrypt_buffer(int pvr_eng_id, struct pvr_encrypt_param *param)
{
	int ret;
	u32 rec_ta_session_id;
	size_t len;
	struct pvr_metadata *metadata;
	TEE_COMMON_ARG_T *rec_ta_param;
	u64 keyToken = pvr_eng_id;
	u8 *serialized_buf = NULL;
	u32 serialized_buf_size = 0;
	u32 serialized_buf_offset = TZ_CACHE_ALIGN(sizeof(TEE_COMMON_ARG_T));

	if (param->index_num > PVR_METADATA_MAX_NUM) {
		DEMUX_DEBUG_ERR("[%s][%d] Max index number is %u\n",
			__func__, __LINE__, PVR_METADATA_MAX_NUM);
		return -EFAULT;
	}

	if (!_is_dmx_tee_ctx_inited())
		_dmx_tee_init_context();

	rec_ta_session_id = _dmx_tee_get_rec_ta_session_id();
	if (!rec_ta_session_id) {
		DEMUX_DEBUG_ERR("[%s][%d] rec ta session id invalid\n", __func__, __LINE__);
		return -EFAULT;
	}

	len = PVR_METADATA_MAX_NUM * sizeof(struct pvr_metadata);

	serialized_buf_size = TZ_CACHE_ALIGN(sizeof(TEE_COMMON_ARG_T));
	serialized_buf_size += TZ_CACHE_ALIGN(sizeof(u32));	// result
	serialized_buf_size += TZ_CACHE_ALIGN(sizeof(u64));	// token
	serialized_buf_size += TZ_CACHE_ALIGN(sizeof(u64));	// src addr
	serialized_buf_size += TZ_CACHE_ALIGN(sizeof(u64));	// dst addr
	serialized_buf_size += TZ_CACHE_ALIGN(len);			// pvr metadata

	serialized_buf = kmalloc(serialized_buf_size, GFP_KERNEL);
	if (!serialized_buf) {
		DEMUX_DEBUG_ERR("[%s][%d] out-of-memory\n", __func__, __LINE__);
		return -ENOMEM;
	}

	memset(serialized_buf, 0, serialized_buf_size);

	rec_ta_param = (TEE_COMMON_ARG_T *)serialized_buf;
	rec_ta_param->ui4Command = REC_TA_CMD_OPERATE;
	rec_ta_param->ui1Size = 6;

	rec_ta_param->apvArgument[0].ptr = (u8 *)(serialized_buf + serialized_buf_offset);
	rec_ta_param->ui4Length[0] = sizeof(u32);
	rec_ta_param->abWrite[0] = 1;
	serialized_buf_offset += TZ_CACHE_ALIGN(sizeof(u32));

	rec_ta_param->apvArgument[1].ptr = (u8 *)(serialized_buf + serialized_buf_offset);
	rec_ta_param->ui4Length[1] = sizeof(u64);
	rec_ta_param->abWrite[1] = 0;
	memcpy(rec_ta_param->apvArgument[1].ptr, &keyToken, sizeof(u64));
	serialized_buf_offset += TZ_CACHE_ALIGN(sizeof(u64));

	rec_ta_param->apvArgument[2].ptr = (u8 *)(serialized_buf + serialized_buf_offset);
	rec_ta_param->ui4Length[2] = sizeof(u64);
	rec_ta_param->abWrite[2] = 0;
	memcpy(rec_ta_param->apvArgument[2].ptr, &param->src_addr, sizeof(u64));
	serialized_buf_offset += TZ_CACHE_ALIGN(sizeof(u64));

	rec_ta_param->apvArgument[3].ptr = (u8 *)(serialized_buf + serialized_buf_offset);
	rec_ta_param->ui4Length[3] = sizeof(u64);
	rec_ta_param->abWrite[3] = 0;
	memcpy(rec_ta_param->apvArgument[3].ptr, &param->dst_addr, sizeof(u64));
	serialized_buf_offset += TZ_CACHE_ALIGN(sizeof(u64));

	rec_ta_param->apvArgument[4].val = param->size;
	rec_ta_param->ui4Length[4] = 0;
	rec_ta_param->abWrite[4] = 0;

	rec_ta_param->apvArgument[5].ptr = (u8 *)(serialized_buf + serialized_buf_offset);
	rec_ta_param->ui4Length[5] = len;
	rec_ta_param->abWrite[5] = 1;
	memset(rec_ta_param->apvArgument[5].ptr, 0, len);

	ret = mdrv_demux_tee_invoke_cmd(
		rec_ta_session_id,
		0,
		serialized_buf,
		serialized_buf_size);

	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] invoke cmd fail\n",
			__func__, __LINE__);
		kfree(serialized_buf);
		return -EFAULT;
	}

	metadata = (struct pvr_metadata *)rec_ta_param->apvArgument[5].ptr;
	memcpy(param->metadata, metadata, param->index_num * sizeof(*metadata));

	kfree(serialized_buf);
	return 0;
}
