// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
 * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
 */

#include "hfi_packetization.h"
#include "msm_cvp_debug.h"

/* Set up look-up tables to convert HAL_* to HFI_*.
 *
 * The tables below mostly take advantage of the fact that most
 * HAL_* types are defined bitwise. So if we index them normally
 * when declaring the tables, we end up with huge arrays with wasted
 * space.  So before indexing them, we apply log2 to use a more
 * sensible index.
 */

int cvp_create_pkt_cmd_sys_init(struct cvp_hfi_cmd_sys_init_packet *pkt,
			   u32 arch_type)
{
	int rc = 0;

	if (!pkt)
		return -EINVAL;

	pkt->packet_type = HFI_CMD_SYS_INIT;
	pkt->size = sizeof(struct cvp_hfi_cmd_sys_init_packet);
	pkt->arch_type = arch_type;
	return rc;
}

int cvp_create_pkt_cmd_sys_pc_prep(struct cvp_hfi_cmd_sys_pc_prep_packet *pkt)
{
	int rc = 0;

	if (!pkt)
		return -EINVAL;

	pkt->packet_type = HFI_CMD_SYS_PC_PREP;
	pkt->size = sizeof(struct cvp_hfi_cmd_sys_pc_prep_packet);
	return rc;
}

int cvp_create_pkt_cmd_sys_debug_config(
	struct cvp_hfi_cmd_sys_set_property_packet *pkt,
	u32 mode)
{
	struct cvp_hfi_debug_config *hfi;

	if (!pkt)
		return -EINVAL;

	pkt->size = sizeof(struct cvp_hfi_cmd_sys_set_property_packet) +
		sizeof(struct cvp_hfi_debug_config) + sizeof(u32);
	pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY;
	pkt->num_properties = 1;
	pkt->rg_property_data[0] = HFI_PROPERTY_SYS_DEBUG_CONFIG;
	hfi = (struct cvp_hfi_debug_config *) &pkt->rg_property_data[1];
	hfi->debug_config = mode;
	hfi->debug_mode = HFI_DEBUG_MODE_QUEUE;
	if (msm_cvp_fw_debug_mode
			<= (HFI_DEBUG_MODE_QUEUE | HFI_DEBUG_MODE_QDSS))
		hfi->debug_mode = msm_cvp_fw_debug_mode;
	return 0;
}

int cvp_create_pkt_cmd_sys_coverage_config(
	struct cvp_hfi_cmd_sys_set_property_packet *pkt,
	u32 mode)
{
	if (!pkt) {
		dprintk(CVP_ERR, "In %s(), No input packet\n", __func__);
		return -EINVAL;
	}

	pkt->size = sizeof(struct cvp_hfi_cmd_sys_set_property_packet) +
		sizeof(u32);
	pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY;
	pkt->num_properties = 1;
	pkt->rg_property_data[0] = HFI_PROPERTY_SYS_CONFIG_COVERAGE;
	pkt->rg_property_data[1] = mode;
	dprintk(CVP_PKT, "Firmware coverage mode %d\n",
			pkt->rg_property_data[1]);
	return 0;
}

int cvp_create_pkt_cmd_sys_set_idle_indicator(
	struct cvp_hfi_cmd_sys_set_property_packet *pkt,
	u32 mode)
{
	if (!pkt) {
		dprintk(CVP_ERR, "In %s(), No input packet\n", __func__);
		return -EINVAL;
	}

	pkt->size = sizeof(struct cvp_hfi_cmd_sys_set_property_packet) +
		sizeof(u32);
	pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY;
	pkt->num_properties = 1;
	pkt->rg_property_data[0] = HFI_PROPERTY_SYS_IDLE_INDICATOR;
	pkt->rg_property_data[1] = mode;
	dprintk(CVP_PKT, "Firmware idle indicator mode %d\n",
			pkt->rg_property_data[1]);
	return 0;
}

int cvp_create_pkt_cmd_sys_set_resource(
		struct cvp_hfi_cmd_sys_set_resource_packet *pkt,
		struct cvp_resource_hdr *res_hdr,
		void *res_value)
{
	int rc = 0;
	u32 i = 0;

	if (!pkt || !res_hdr || !res_value) {
		dprintk(CVP_ERR,
			"Invalid paramas pkt %pK res_hdr %pK res_value %pK\n",
				pkt, res_hdr, res_value);
		return -EINVAL;
	}

	pkt->packet_type = HFI_CMD_SYS_SET_RESOURCE;
	pkt->size = sizeof(struct cvp_hfi_cmd_sys_set_resource_packet);
	pkt->resource_handle = hash32_ptr(res_hdr->resource_handle);

	switch (res_hdr->resource_id) {
	case CVP_RESOURCE_SYSCACHE:
	{
		struct cvp_hfi_resource_syscache_info_type *res_sc_info =
			(struct cvp_hfi_resource_syscache_info_type *)res_value;
		struct cvp_hfi_resource_subcache_type *res_sc =
			(struct cvp_hfi_resource_subcache_type *)
				&(res_sc_info->rg_subcache_entries[0]);

		struct cvp_hfi_resource_syscache_info_type *hfi_sc_info =
			(struct cvp_hfi_resource_syscache_info_type *)
				&pkt->rg_resource_data[0];

		struct cvp_hfi_resource_subcache_type *hfi_sc =
			(struct cvp_hfi_resource_subcache_type *)
			&(hfi_sc_info->rg_subcache_entries[0]);

		pkt->resource_type = HFI_RESOURCE_SYSCACHE;
		hfi_sc_info->num_entries = res_sc_info->num_entries;

		pkt->size += (sizeof(struct cvp_hfi_resource_subcache_type))
				 * hfi_sc_info->num_entries;

		for (i = 0; i < hfi_sc_info->num_entries; i++) {
			hfi_sc[i] = res_sc[i];
		dprintk(CVP_PKT, "entry hfi#%d, sc_id %d, size %d\n",
				 i, hfi_sc[i].sc_id, hfi_sc[i].size);
		}
		break;
	}
	default:
		dprintk(CVP_ERR,
			"Invalid resource_id %d\n", res_hdr->resource_id);
		rc = -ENOTSUPP;
	}

	return rc;
}

int cvp_create_pkt_cmd_sys_release_resource(
		struct cvp_hfi_cmd_sys_release_resource_packet *pkt,
		struct cvp_resource_hdr *res_hdr)
{
	int rc = 0;

	if (!pkt || !res_hdr) {
		dprintk(CVP_ERR,
			"Invalid paramas pkt %pK res_hdr %pK\n",
				pkt, res_hdr);
		return -EINVAL;
	}

	pkt->size = sizeof(struct cvp_hfi_cmd_sys_release_resource_packet);
	pkt->packet_type = HFI_CMD_SYS_RELEASE_RESOURCE;
	pkt->resource_handle = hash32_ptr(res_hdr->resource_handle);

	switch (res_hdr->resource_id) {
	case CVP_RESOURCE_SYSCACHE:
		pkt->resource_type = HFI_RESOURCE_SYSCACHE;
		break;
	default:
		dprintk(CVP_ERR,
			 "Invalid resource_id %d\n", res_hdr->resource_id);
		rc = -ENOTSUPP;
	}

	dprintk(CVP_PKT,
		"rel_res: pkt_type 0x%x res_type 0x%x prepared\n",
		pkt->packet_type, pkt->resource_type);

	return rc;
}

inline int cvp_create_pkt_cmd_sys_session_init(
		struct cvp_hfi_cmd_sys_session_init_packet *pkt,
		struct cvp_hal_session *session)
{
	int rc = 0;
	struct msm_cvp_inst *inst = session->session_id;

	if (!pkt || !inst)
		return -EINVAL;

	pkt->size = sizeof(struct cvp_hfi_cmd_sys_session_init_packet);
	pkt->packet_type = HFI_CMD_SYS_SESSION_INIT;
	pkt->session_id = hash32_ptr(session);
	pkt->session_type = inst->prop.type;
	pkt->session_kmask = inst->prop.kernel_mask;
	pkt->session_prio = inst->prop.priority;
	pkt->is_secure = inst->prop.is_secure;
	pkt->dsp_ac_mask = inst->prop.dsp_mask;

	return rc;
}

static int create_pkt_cmd_sys_ubwc_config(
		struct cvp_hfi_cmd_sys_set_property_packet *pkt,
		struct msm_cvp_ubwc_config_data *ubwc_config)
{
	int rc = 0;
	struct cvp_hfi_cmd_sys_set_ubwc_config_packet_type *hfi;

	if (!pkt)
		return -EINVAL;

	pkt->size = sizeof(struct cvp_hfi_cmd_sys_set_property_packet) +
		sizeof(struct cvp_hfi_cmd_sys_set_ubwc_config_packet_type)
		+ sizeof(u32);

	pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY;
	pkt->num_properties = 1;
	pkt->rg_property_data[0] = HFI_PROPERTY_SYS_UBWC_CONFIG;
	hfi = (struct cvp_hfi_cmd_sys_set_ubwc_config_packet_type *)
		&pkt->rg_property_data[1];

	hfi->max_channels = ubwc_config->max_channels;
	hfi->override_bit_info.max_channel_override =
		ubwc_config->override_bit_info.max_channel_override;

	hfi->mal_length = ubwc_config->mal_length;
	hfi->override_bit_info.mal_length_override =
		ubwc_config->override_bit_info.mal_length_override;

	hfi->highest_bank_bit = ubwc_config->highest_bank_bit;
	hfi->override_bit_info.hb_override =
		ubwc_config->override_bit_info.hb_override;

	hfi->bank_swzl_level = ubwc_config->bank_swzl_level;
	hfi->override_bit_info.bank_swzl_level_override =
		ubwc_config->override_bit_info.bank_swzl_level_override;

	hfi->bank_spreading = ubwc_config->bank_spreading;
	hfi->override_bit_info.bank_spreading_override =
		ubwc_config->override_bit_info.bank_spreading_override;
	hfi->size = sizeof(struct cvp_hfi_cmd_sys_set_ubwc_config_packet_type);

	return rc;
}

int cvp_create_pkt_cmd_session_cmd(struct cvp_hal_session_cmd_pkt *pkt,
			int pkt_type, struct cvp_hal_session *session)
{
	int rc = 0;

	if (!pkt)
		return -EINVAL;

	pkt->size = sizeof(struct cvp_hal_session_cmd_pkt);
	pkt->packet_type = pkt_type;
	pkt->session_id = hash32_ptr(session);

	return rc;
}

int cvp_create_pkt_cmd_sys_power_control(
	struct cvp_hfi_cmd_sys_set_property_packet *pkt, u32 enable)
{
	struct cvp_hfi_enable *hfi;

	if (!pkt) {
		dprintk(CVP_ERR, "No input packet\n");
		return -EINVAL;
	}

	pkt->size = sizeof(struct cvp_hfi_cmd_sys_set_property_packet) +
		sizeof(struct cvp_hfi_enable) + sizeof(u32);
	pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY;
	pkt->num_properties = 1;
	pkt->rg_property_data[0] = HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL;
	hfi = (struct cvp_hfi_enable *) &pkt->rg_property_data[1];
	hfi->enable = enable;
	return 0;
}

int cvp_create_pkt_cmd_session_set_buffers(
		void *cmd,
		struct cvp_hal_session *session,
		u32 iova,
		u32 size)
{
	int rc = 0;
	struct cvp_hfi_cmd_session_set_buffers_packet *pkt;

	if (!cmd || !session)
		return -EINVAL;

	pkt = (struct cvp_hfi_cmd_session_set_buffers_packet *)cmd;
	pkt->packet_type = HFI_CMD_SESSION_CVP_SET_BUFFERS;
	pkt->session_id = hash32_ptr(session);
	pkt->buf_type.iova = iova;
	pkt->buf_type.size = size;
	pkt->size = sizeof(struct cvp_hfi_cmd_session_set_buffers_packet);

	return rc;
}

int cvp_create_pkt_cmd_session_release_buffers(
		void *cmd,
		struct cvp_hal_session *session)
{
	struct cvp_session_release_buffers_packet *pkt;

	if (!cmd || !session || session == (void *)0xdeadbeef)
		return -EINVAL;

	pkt = (struct cvp_session_release_buffers_packet *)cmd;
	pkt->packet_type = HFI_CMD_SESSION_CVP_RELEASE_BUFFERS;
	pkt->session_id = hash32_ptr(session);
	pkt->num_buffers = 1;
	pkt->buffer_type = 0;
	pkt->size = sizeof(struct cvp_session_release_buffers_packet) +
			((pkt->num_buffers - 1) * sizeof(u32));

	return 0;
}

int cvp_create_pkt_cmd_session_send(
		struct eva_kmd_hfi_packet *out_pkt,
		struct cvp_hal_session *session,
		struct eva_kmd_hfi_packet *in_pkt)
{
	int def_idx;
	struct cvp_hal_session_cmd_pkt *ptr =
		(struct cvp_hal_session_cmd_pkt *)in_pkt;

	if (!out_pkt || !in_pkt || !session)
		return -EINVAL;

	if (ptr->size > MAX_HFI_PKT_SIZE * sizeof(unsigned int))
		goto error_hfi_packet;

	if (ptr->session_id != hash32_ptr(session))
		goto error_hfi_packet;

	def_idx = get_pkt_index(ptr);
	if (def_idx < 0) {
		memcpy(out_pkt, in_pkt, ptr->size);
		return 0;
	}

	if (cvp_hfi_defs[def_idx].type != ptr->packet_type)
		goto error_hfi_packet;

	memcpy(out_pkt, in_pkt, ptr->size);

	return 0;

error_hfi_packet:
	dprintk(CVP_ERR, "%s incorrect packet: size=%d type=%d sessionid=%d\n",
		__func__, ptr->size, ptr->packet_type, ptr->session_id);

	return -EINVAL;
}

static int get_hfi_ssr_type(enum hal_ssr_trigger_type type)
{
	int rc = HFI_TEST_SSR_HW_WDOG_IRQ;

	switch (type) {
	case SSR_ERR_FATAL:
		rc = HFI_TEST_SSR_SW_ERR_FATAL;
		break;
	case SSR_SW_DIV_BY_ZERO:
		rc = HFI_TEST_SSR_SW_DIV_BY_ZERO;
		break;
	case SSR_HW_WDOG_IRQ:
		rc = HFI_TEST_SSR_HW_WDOG_IRQ;
		break;
	default:
		dprintk(CVP_WARN,
			"SSR trigger type not recognized, using WDOG.\n");
	}
	return rc;
}

int cvp_create_pkt_ssr_cmd(enum hal_ssr_trigger_type type,
		struct cvp_hfi_cmd_sys_test_ssr_packet *pkt)
{
	if (!pkt) {
		dprintk(CVP_ERR, "Invalid params, device: %pK\n", pkt);
		return -EINVAL;
	}
	pkt->size = sizeof(struct cvp_hfi_cmd_sys_test_ssr_packet);
	pkt->packet_type = HFI_CMD_SYS_TEST_SSR;
	pkt->trigger_type = get_hfi_ssr_type(type);
	return 0;
}

int cvp_create_pkt_cmd_sys_image_version(
		struct cvp_hfi_cmd_sys_get_property_packet *pkt)
{
	if (!pkt) {
		dprintk(CVP_ERR, "%s invalid param :%pK\n", __func__, pkt);
		return -EINVAL;
	}
	pkt->size = sizeof(struct cvp_hfi_cmd_sys_get_property_packet);
	pkt->packet_type = HFI_CMD_SYS_GET_PROPERTY;
	pkt->num_properties = 1;
	pkt->rg_property_data[0] = HFI_PROPERTY_SYS_IMAGE_VERSION;
	return 0;
}

static struct cvp_hfi_packetization_ops hfi_default = {
	.sys_init = cvp_create_pkt_cmd_sys_init,
	.sys_pc_prep = cvp_create_pkt_cmd_sys_pc_prep,
	.sys_power_control = cvp_create_pkt_cmd_sys_power_control,
	.sys_set_resource = cvp_create_pkt_cmd_sys_set_resource,
	.sys_debug_config = cvp_create_pkt_cmd_sys_debug_config,
	.sys_coverage_config = cvp_create_pkt_cmd_sys_coverage_config,
	.sys_set_idle_indicator = cvp_create_pkt_cmd_sys_set_idle_indicator,
	.sys_release_resource = cvp_create_pkt_cmd_sys_release_resource,
	.sys_image_version = cvp_create_pkt_cmd_sys_image_version,
	.sys_ubwc_config = create_pkt_cmd_sys_ubwc_config,
	.ssr_cmd = cvp_create_pkt_ssr_cmd,
	.session_init = cvp_create_pkt_cmd_sys_session_init,
	.session_cmd = cvp_create_pkt_cmd_session_cmd,
	.session_set_buffers =
		cvp_create_pkt_cmd_session_set_buffers,
	.session_release_buffers =
		cvp_create_pkt_cmd_session_release_buffers,
	.session_send = cvp_create_pkt_cmd_session_send,
};

struct cvp_hfi_packetization_ops *cvp_hfi_get_pkt_ops_handle(
			enum hfi_packetization_type type)
{
	dprintk(CVP_HFI, "%s selected\n",
		type == HFI_PACKETIZATION_4XX ?
		"4xx packetization" : "Unknown hfi");

	switch (type) {
	case HFI_PACKETIZATION_4XX:
		return &hfi_default;
	}

	return NULL;
}