// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2020 MediaTek Inc.
 */
#include <linux/version.h>

#include "mtk_tv_drm_common.h"
#include "mtk_tv_drm_log.h"
#include "hwreg_common.h"
#include "hwreg_render_stub.h"
#include "mtk_tv_drm_kms.h"
#include "m_pqu_render.h"
#include "m_pqu_pq.h"

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0)
MODULE_IMPORT_NS(DMA_BUF);
#endif

#define MTK_DRM_MODEL MTK_DRM_MODEL_COMMON

#define DEFAULT_CMD_PARSER_SIZE		64

static int _mtk_render_common_map_db(struct dma_buf *db, void **va, u64 *size)
{
	int ret = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
	struct iosys_map map = {0};
#else
	struct dma_buf_map map = {0};
#endif

	if ((db == NULL) || (va == NULL) || (size == NULL)) {
#if (!MARK_METADATA_LOG)
		DRM_ERROR("[%s][%d] Invalid input, db=0x%llx, va=0x%llx, size=0x%llx\n",
			__func__, __LINE__, db, va, size);
#endif
		return -EINVAL;
	}

	if (*va != NULL) {
		kfree(*va);
		*va = NULL;
	}

	*size = db->size;

	ret = dma_buf_vmap(db, &map);
	*va = map.vaddr;
	if (IS_ERR_OR_NULL(*va)) {
		if (IS_ERR(*va)) {
			DRM_ERROR("[%s][%d] dma_buf_vmap fail, db=%p, errno = %ld\n",
				__func__, __LINE__, db, PTR_ERR(*va));
			return -EINVAL;
		}

		DRM_ERROR("[%s][%d] dma_buf_vmap fail, db=%p, null ptr!\n",
			__func__, __LINE__, db);
		return -EINVAL;
	}
/*
	DRM_INFO("[%s][%d] va=%p, size=%llu\n",
			__func__, __LINE__, *va, *size);
*/

	return ret;
}

static int _mtk_render_common_unmap_db(struct dma_buf *db, void *va)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
	struct iosys_map map = IOSYS_MAP_INIT_VADDR(va);
#else
	struct dma_buf_map map = DMA_BUF_MAP_INIT_VADDR(va);
#endif
	if ((db == NULL) || (va == NULL)) {
#if (!MARK_METADATA_LOG)
		DRM_ERROR("[%s][%d] Invalid input, db=0x%llx, va=0x%llx\n",
			__func__, __LINE__, db, va);
#endif
		return -EINVAL;
	}

	dma_buf_vunmap(db, &map);

	return 0;
}

static int _mtk_render_common_get_metaheader(
	enum mtk_render_common_metatag meta_tag,
	struct meta_header *meta_header)
{
	int ret = 0;

	if (meta_header == NULL) {
		DRM_ERROR("[%s][%d] Invalid input, meta_header is NULL, meta_tag:%d\n",
			__func__, __LINE__, meta_tag);
		return -EINVAL;
	}

	switch (meta_tag) {
	case RENDER_METATAG_FRAME_INFO:
		DRM_INFO("[%s][%d] Now this metadata tag:(%d) is unused\n",
			__func__, __LINE__, meta_tag);
		ret = -EINVAL;
		break;
	case RENDER_METATAG_SVP_INFO:
		meta_header->size = sizeof(struct meta_pq_disp_svp);
		meta_header->ver = META_PQ_DISP_SVP_VERSION;
		meta_header->tag = PQ_DISP_SVP_META_TAG;
		break;
	case RENDER_METATAG_FRAMESYNC_INFO:
		meta_header->size = sizeof(struct meta_pq_framesync_info);
		meta_header->ver = META_PQ_FRAMESYNC_INFO_VERSION;
		meta_header->tag = META_PQ_FRAMESYNC_INFO_TAG;
		break;
	case RENDER_METATAG_IDR_CTRL_INFO:
		meta_header->size = sizeof(struct meta_pq_display_idr_ctrl);
		meta_header->ver = META_PQ_DISPLAY_IDR_CTRL_VERSION;
		meta_header->tag = META_PQ_DISPLAY_IDR_CTRL_META_TAG;
		break;
	case RENDER_METATAG_DV_OUTPUT_FRAME_INFO:
		meta_header->size = sizeof(struct m_pq_dv_out_frame_info);
		meta_header->ver = M_PQ_DV_OUTPUT_FRAME_INFO_VERSION;
		meta_header->tag = M_PQ_DV_OUTPUT_FRAME_INFO_TAG;
		break;
	case RENDER_METATAG_PQDD_DISPLAY_INFO:
		meta_header->size = sizeof(struct meta_pq_display_flow_info);
		meta_header->ver = META_PQ_DISPLAY_FLOW_INFO_VERSION;
		meta_header->tag = META_PQ_DISPLAY_FLOW_INFO_TAG;
		break;
	case RENDER_METATAG_PQDD_OUTPUT_FRAME_INFO:
		meta_header->size = sizeof(struct meta_pq_output_frame_info);
		meta_header->ver = META_PQ_OUTPUT_FRAME_INFO_VERSION;
		meta_header->tag = META_PQ_OUTPUT_FRAME_INFO_TAG;
		break;
	case RENDER_METATAG_PQU_DISPLAY_INFO:
		meta_header->size = sizeof(struct m_pq_display_flow_ctrl);
		meta_header->ver = M_PQ_DISPLAY_FLOW_CTRL_VERSION;
		meta_header->tag = M_PQ_DISPLAY_FLOW_CTRL_META_TAG;
		break;
	case RENDER_METATAG_META_PQ_OUTPUT_RENDER_INFO_TAG:
		meta_header->size = sizeof(struct meta_pq_output_render_info);
		meta_header->ver = META_PQ_OUTPUT_RENDER_INFO_VERSION;
		meta_header->tag = META_PQ_OUTPUT_RENDER_INFO_TAG;
		break;
	default:
		DRM_INFO("[%s][%d] Invalid metadata tag:(%d)\n",
			__func__, __LINE__, meta_tag);
		ret = -EINVAL;
		break;
	}
	return ret;
}

int mtk_render_common_read_metadata(struct dma_buf *meta_db,
	enum mtk_render_common_metatag meta_tag, void *meta_content)
{
	int ret = 0;
	unsigned char meta_ret = 0;
	void *va = NULL;
	u64 size = 0;
	struct meta_buffer buffer;
	struct meta_header header;

	if (meta_content == NULL) {
		DRM_ERROR("[%s][%d] Invalid input, meta_content is NULL, db=%p, meta_tag:%d\n",
			__func__, __LINE__, meta_db, meta_tag);
		return -EINVAL;
	}

	memset(&buffer, 0, sizeof(struct meta_buffer));
	memset(&header, 0, sizeof(struct meta_header));

	ret = _mtk_render_common_map_db(meta_db, &va, &size);
	if (ret) {
#if (!MARK_METADATA_LOG)
		DRM_ERROR("[%s][%d] trans fd to va fail, db=%p, meta_tag:%d\n",
			__func__, __LINE__, meta_db, meta_tag);
#endif
		goto out;
	}

	ret = _mtk_render_common_get_metaheader(meta_tag, &header);
	if (ret) {
		DRM_ERROR("[%s][%d] get meta header fail, meta_tag:%d\n",
			__func__, __LINE__, meta_tag);
		goto out;
	}

	buffer.paddr = (unsigned char *)va;
	buffer.size = (unsigned int)size;

	meta_ret = query_metadata_header(&buffer, &header);
	if (!meta_ret) {
#if (!MARK_METADATA_LOG)
		DRM_ERROR("[%s][%d] query metadata header fail, meta_tag:%d\n",
			__func__, __LINE__, meta_tag);
#endif
		ret = -EPERM;
		goto out;
	}

	meta_ret = query_metadata_content(&buffer, &header, meta_content);
	if (!meta_ret) {
		DRM_ERROR("[%s][%d] query metadata content fail, meta_tag:%d\n",
			__func__, __LINE__, meta_tag);
		ret = -EPERM;
		goto out;
	}

out:
	_mtk_render_common_unmap_db(meta_db, va);
	return ret;
}

int mtk_render_common_write_metadata(struct dma_buf *meta_db,
	enum mtk_render_common_metatag meta_tag, void *meta_content)
{
	int ret = 0;
	unsigned char meta_ret = 0;
	void *va = NULL;
	u64 size = 0;
	struct meta_buffer buffer;
	struct meta_header header;

	DRM_INFO("[%s][%d] write db=%p meta_tag:%d\n", __func__, __LINE__, meta_db, meta_tag);

	if (meta_content == NULL) {
		DRM_ERROR("[%s][%d] Invalid input, meta_content is NULL, db=%p, meta_tag:%d\n",
			__func__, __LINE__, meta_db, meta_tag);
		return -EINVAL;
	}

	memset(&buffer, 0, sizeof(struct meta_buffer));
	memset(&header, 0, sizeof(struct meta_header));

	ret = _mtk_render_common_map_db(meta_db, &va, &size);
	if (ret) {
		DRM_ERROR("[%s][%d] trans fd to va fail, db=%p, meta_tag:%d\n",
			__func__, __LINE__, meta_db, meta_tag);
		goto out;
	}

	ret = _mtk_render_common_get_metaheader(meta_tag, &header);
	if (ret) {
		DRM_ERROR("[%s][%d] get meta header fail, meta_tag:%d\n",
			__func__, __LINE__, meta_tag);
		goto out;
	}

	buffer.paddr = (unsigned char *)va;
	buffer.size = (unsigned int)size;
	meta_ret = attach_metadata(&buffer, header, meta_content);
	if (!meta_ret) {
		DRM_ERROR("[%s][%d] attach metadata fail, meta_tag:%d\n",
			__func__, __LINE__, meta_tag);
		ret = -EPERM;
		goto out;
	}

out:
	_mtk_render_common_unmap_db(meta_db, va);
	return ret;
}

int mtk_render_common_delete_metadata(struct dma_buf *meta_db,
	enum mtk_render_common_metatag meta_tag)
{
	int ret = 0;
	unsigned char meta_ret = 0;
	void *va = NULL;
	u64 size = 0;
	struct meta_buffer buffer;
	struct meta_header header;

	memset(&buffer, 0, sizeof(struct meta_buffer));
	memset(&header, 0, sizeof(struct meta_header));

	ret = _mtk_render_common_map_db(meta_db, &va, &size);
	if (ret) {
		DRM_ERROR("[%s][%d] trans fd to va fail, db=%p, meta_tag:%d\n",
			__func__, __LINE__, meta_db, meta_tag);
		goto out;
	}

	ret = _mtk_render_common_get_metaheader(meta_tag, &header);
	if (ret) {
		DRM_ERROR("[%s][%d] get meta header fail, meta_tag:%d\n",
			__func__, __LINE__, meta_tag);
		goto out;
	}

	buffer.paddr = (unsigned char *)va;
	buffer.size = (unsigned int)size;
	meta_ret = delete_metadata(&buffer, &header);
	if (!meta_ret) {
		DRM_INFO("[%s][%d] delete metadata fail, meta_tag:%d\n",
			__func__, __LINE__, meta_tag);
		ret = -EPERM;
		goto out;
	}

out:
	_mtk_render_common_unmap_db(meta_db, va);
	return ret;
}

int mtk_render_common_set_stub_mode(struct mtk_tv_drm_crtc *crtc,
	struct drm_mtk_tv_common_stub *stubInfo)
{
	int ret = 0;
	struct mtk_tv_kms_context *pctx = NULL;

	if (!crtc || !stubInfo) {
		DRM_ERROR("[%s][%d] null ptr, crtc=%p, stubInfo=%p\n",
			__func__, __LINE__, crtc, stubInfo);
		return -EINVAL;
	}

	DRM_INFO("[%s,%d] stub = %d\n", __func__, __LINE__, stubInfo->stub);
	ret = drv_hwreg_common_set_stub(stubInfo->stub);
	if (ret < 0) {
		DRM_ERROR("[%s][%d] set stub fail\n",
			__func__, __LINE__);
		return ret;
	}

	ret = drv_hwreg_render_stub_set_mode(stubInfo->stub);
	if (ret < 0) {
		DRM_ERROR("[%s][%d] set render stub fail\n", __func__, __LINE__);
		return ret;
	}

	pctx = (struct mtk_tv_kms_context *)crtc->crtc_private;
	pctx->stub = stubInfo->stub;
	pctx->b2r_src_stubmode = stubInfo->b2r_src_stubmode;
	pctx->panel_priv.cus.fs_mode = stubInfo->vtt2fpll_stubmode;

	return ret;
}

int mtk_render_common_parser_cmd_int(const char *cmd, char *key, uint64_t *val)
{
	char key_buf[DEFAULT_CMD_PARSER_SIZE] = {0};
	char val_buf[DEFAULT_CMD_PARSER_SIZE] = {0};
	char *pch = NULL;
	long value;
	int i, ret = -1;

	ret = snprintf(key_buf, DEFAULT_CMD_PARSER_SIZE, "%s=", key);
	if (ret < 0 || ret >= DEFAULT_CMD_PARSER_SIZE) {
		DRM_ERROR("[%s, %d]: key'%s' too long", __func__, __LINE__, key);
		goto EXIT;
	}

	pch = strstr(cmd, key_buf);
	if (pch == NULL || (pch > cmd && *(pch - 1) != ' ')) {
		DRM_ERROR("[%s, %d]: key'%s' not found in cmd'%s'", __func__, __LINE__, key, cmd);
		goto EXIT;
	}
	pch += strlen(key_buf);

	for (i = 0; i < DEFAULT_CMD_PARSER_SIZE; ++i) {
		if (isalnum(*pch) || *pch == '-') {
			val_buf[i] = *pch;
			pch++;
		} else {
			val_buf[i] = '\0';
			break;
		}
	}

	ret = kstrtol(val_buf, 0, &value);
	if (ret) {
		DRM_ERROR("[%s, %d]: kstrtol fail, val_buf:'%s', ret:%d",
			__func__, __LINE__, val_buf, ret);
		goto EXIT;
	}
	DRM_INFO("[%s][%d]: cmd: '%s', key: '%s', value: %ld", __func__, __LINE__, cmd, key, value);
	*val = (uint64_t)value;
EXIT:
	return ret;
}

void *mtk_render_common_ioremap(uint64_t phy, uint64_t size)
{
	return ioremap(phy, size);
}

void mtk_render_common_iounmap(void *addr)
{
	iounmap(addr);
}

size_t mtk_render_common_pa2va(uint64_t phy_addr, uint32_t len, bool *remap)
{
	size_t vir_addr = 0;

	vir_addr =  (size_t)ioremap_wc(phy_addr, len);
	if (remap)
		*remap = TRUE;

	DRM_INFO("phy_addr=0x%llx -> vir_addr=0x%zx, size=0x%x\n", phy_addr, vir_addr, len);

	return vir_addr;
}

