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

#include "hdcp_main.h"
#include "hdcp_qseecom.h"
#include "hdcp_smcinvoke.h"

struct hdcp_ta_interface ta_interface;
static DEFINE_MUTEX(hdcp1_mutex_g);
static DEFINE_MUTEX(hdcp2_mutex_g);

void select_interface(bool use_smcinvoke)
{
	if (use_smcinvoke) {
		ta_interface.trusted_app_hdcp1_init = &hdcp1_init_smcinvoke;
		ta_interface.trusted_app_hdcp1_feature_supported = &hdcp1_feature_supported_smcinvoke;
		ta_interface.trusted_app_hdcp1_set_enc = &hdcp1_set_enc_smcinvoke;
		ta_interface.trusted_app_hdcp1_ops_notify = &hdcp1_ops_notify_smcinvoke;
		ta_interface.trusted_app_hdcp1_start = &hdcp1_start_smcinvoke;
		ta_interface.trusted_app_hdcp1_stop = &hdcp1_stop_smcinvoke;
		ta_interface.trusted_app_hdcp2_init = &hdcp2_init_smcinvoke;
		ta_interface.trusted_app_hdcp2_deinit = &hdcp2_deinit_smcinvoke;
		ta_interface.trusted_app_hdcp2_app_start = &hdcp2_app_start_smcinvoke;
		ta_interface.trusted_app_hdcp2_app_start_auth = &hdcp2_app_start_auth_smcinvoke;
		ta_interface.trusted_app_hdcp2_app_process_msg = &hdcp2_app_process_msg_smcinvoke;
		ta_interface.trusted_app_hdcp2_app_enable_encryption = &hdcp2_app_enable_encryption_smcinvoke;
		ta_interface.trusted_app_hdcp2_app_timeout = &hdcp2_app_timeout_smcinvoke;
		ta_interface.trusted_app_hdcp2_app_query_stream = &hdcp2_app_query_stream_smcinvoke;
		ta_interface.trusted_app_hdcp2_app_stop = &hdcp2_app_stop_smcinvoke;
		ta_interface.trusted_app_hdcp2_feature_supported = &hdcp2_feature_supported_smcinvoke;
		ta_interface.trusted_app_hdcp2_force_encryption = &hdcp2_force_encryption_smcinvoke;
		ta_interface.trusted_app_hdcp2_open_stream = &hdcp2_open_stream_smcinvoke;
		ta_interface.trusted_app_hdcp2_close_stream = &hdcp2_close_stream_smcinvoke;
		ta_interface.trusted_app_hdcp2_update_app_data = &hdcp2_update_app_data_smcinvoke;
	} else {
		ta_interface.trusted_app_hdcp1_init = &hdcp1_init_qseecom;
		ta_interface.trusted_app_hdcp1_feature_supported = &hdcp1_feature_supported_qseecom;
		ta_interface.trusted_app_hdcp1_set_enc = &hdcp1_set_enc_qseecom;
		ta_interface.trusted_app_hdcp1_ops_notify = &hdcp1_ops_notify_qseecom;
		ta_interface.trusted_app_hdcp1_start = &hdcp1_start_qseecom;
		ta_interface.trusted_app_hdcp1_stop = &hdcp1_stop_qseecom;
		ta_interface.trusted_app_hdcp2_init = &hdcp2_init_qseecom;
		ta_interface.trusted_app_hdcp2_deinit = &hdcp2_deinit_qseecom;
		ta_interface.trusted_app_hdcp2_app_start = &hdcp2_app_start_qseecom;
		ta_interface.trusted_app_hdcp2_app_start_auth = &hdcp2_app_start_auth_qseecom;
		ta_interface.trusted_app_hdcp2_app_process_msg = &hdcp2_app_process_msg_qseecom;
		ta_interface.trusted_app_hdcp2_app_timeout = &hdcp2_app_timeout_qseecom;
		ta_interface.trusted_app_hdcp2_app_enable_encryption = &hdcp2_app_enable_encryption_qseecom;
		ta_interface.trusted_app_hdcp2_app_query_stream = &hdcp2_app_query_stream_qseecom;
		ta_interface.trusted_app_hdcp2_app_stop = &hdcp2_app_stop_qseecom;
		ta_interface.trusted_app_hdcp2_feature_supported = &hdcp2_feature_supported_qseecom;
		ta_interface.trusted_app_hdcp2_force_encryption = &hdcp2_force_encryption_qseecom;
		ta_interface.trusted_app_hdcp2_open_stream = &hdcp2_open_stream_qseecom;
		ta_interface.trusted_app_hdcp2_close_stream = &hdcp2_close_stream_qseecom;
		ta_interface.trusted_app_hdcp2_update_app_data = &hdcp2_update_app_data_qseecom;
	}
}

int hdcp1_count_ones(u8 *array, u8 len)
{
	int i, j, count = 0;

	for (i = 0; i < len; i++)
		for (j = 0; j < 8; j++)
			count += (((array[i] >> j) & 0x1) ? 1 : 0);

	return count;
}



int hdcp1_validate_aksv(u32 aksv_msb, u32 aksv_lsb)
{
	int const number_of_ones = 20;
	u8 aksv[5] = {0};

	pr_debug("AKSV=%02x%08x\n", aksv_msb, aksv_lsb);

	aksv[0] = aksv_lsb & 0xFF;
	aksv[1] = (aksv_lsb >> 8) & 0xFF;
	aksv[2] = (aksv_lsb >> 16) & 0xFF;
	aksv[3] = (aksv_lsb >> 24) & 0xFF;
	aksv[4] = aksv_msb & 0xFF;

	/* check there are 20 ones in AKSV */
	if (hdcp1_count_ones(aksv, 5) != number_of_ones) {
		pr_err("AKSV bit count failed\n");
		return -EINVAL;
	}

	return 0;
}



bool hdcp2_feature_supported(void *data)
{
	int ret = 0;

	mutex_lock(&hdcp2_mutex_g);
	ret = ta_interface.trusted_app_hdcp2_feature_supported(data);
	mutex_unlock(&hdcp2_mutex_g);

	return ret;
}
EXPORT_SYMBOL_GPL(hdcp2_feature_supported);

int hdcp2_force_encryption(void *ctx, uint32_t enable)
{
	int ret = 0;

	mutex_lock(&hdcp2_mutex_g);
	ret = ta_interface.trusted_app_hdcp2_force_encryption(ctx, enable);
	mutex_unlock(&hdcp2_mutex_g);

	return ret;
}
EXPORT_SYMBOL_GPL(hdcp2_force_encryption);

int hdcp2_app_comm(void *ctx, enum hdcp2_app_cmd cmd,
				   struct hdcp2_app_data *app_data)
{
	int ret = 0;
	uint32_t req_len = 0;

	if (!ctx || !app_data) {
		pr_err("invalid input\n");
		return -EINVAL;
	}

	req_len = app_data->request.length;

	mutex_lock(&hdcp2_mutex_g);
	switch (cmd) {
	case HDCP2_CMD_START:
		ret = ta_interface.trusted_app_hdcp2_app_start(ctx, req_len);
		break;
	case HDCP2_CMD_START_AUTH:
		ret = ta_interface.trusted_app_hdcp2_app_start_auth(ctx, req_len);
		break;
	case HDCP2_CMD_PROCESS_MSG:
		ret = ta_interface.trusted_app_hdcp2_app_process_msg(ctx, req_len);
		break;
	case HDCP2_CMD_TIMEOUT:
		ret = ta_interface.trusted_app_hdcp2_app_timeout(ctx, req_len);
		break;
	case HDCP2_CMD_EN_ENCRYPTION:
		ret = ta_interface.trusted_app_hdcp2_app_enable_encryption(ctx, req_len);
		break;
	case HDCP2_CMD_QUERY_STREAM:
		ret = ta_interface.trusted_app_hdcp2_app_query_stream(ctx, req_len);
		break;
	case HDCP2_CMD_STOP:
		ret = ta_interface.trusted_app_hdcp2_app_stop(ctx);
		break;
	default:
		goto error;
	}

	if (ret)
		goto error;

	ret = ta_interface.trusted_app_hdcp2_update_app_data(ctx, app_data);

error:
	mutex_unlock(&hdcp2_mutex_g);
	return ret;
}
EXPORT_SYMBOL_GPL(hdcp2_app_comm);

int hdcp2_open_stream(void *ctx, uint8_t vc_payload_id, uint8_t stream_number,
		  uint32_t *stream_id)
{
	int ret = 0;

	mutex_lock(&hdcp2_mutex_g);
	ret = ta_interface.trusted_app_hdcp2_open_stream(ctx, vc_payload_id, stream_number,
		   stream_id);
	mutex_unlock(&hdcp2_mutex_g);

	return ret;
}
EXPORT_SYMBOL_GPL(hdcp2_open_stream);

int hdcp2_close_stream(void *ctx, uint32_t stream_id)
{
	int ret = 0;

	mutex_lock(&hdcp2_mutex_g);
	ret = ta_interface.trusted_app_hdcp2_close_stream(ctx, stream_id);
	mutex_unlock(&hdcp2_mutex_g);

	return ret;
}
EXPORT_SYMBOL_GPL(hdcp2_close_stream);

void *hdcp2_init(u32 device_type)
{
	void *data = NULL;

	mutex_lock(&hdcp2_mutex_g);
	data = ta_interface.trusted_app_hdcp2_init(device_type);
	mutex_unlock(&hdcp2_mutex_g);

	return data;
}
EXPORT_SYMBOL_GPL(hdcp2_init);

void hdcp2_set_hdcp_key_verify_retries(void *ctx, u32 max_hdcp_key_verify_retries)
{
	struct hdcp2_qsee_handle *handle = ctx;

	handle->max_hdcp_key_verify_retries = max_hdcp_key_verify_retries;

	pr_debug("hdcp2 max_hdcp_key_verify_retries %d\n", handle->max_hdcp_key_verify_retries);
}
EXPORT_SYMBOL_GPL(hdcp2_set_hdcp_key_verify_retries);

void hdcp2_deinit(void *ctx)
{
	ta_interface.trusted_app_hdcp2_deinit(ctx);
}
EXPORT_SYMBOL_GPL(hdcp2_deinit);

void *hdcp1_init(void)
{
	void *data = NULL;

	mutex_lock(&hdcp1_mutex_g);
	data = ta_interface.trusted_app_hdcp1_init();
	mutex_unlock(&hdcp1_mutex_g);

	return data;
}
EXPORT_SYMBOL_GPL(hdcp1_init);

void hdcp1_set_hdcp_key_verify_retries(void *ctx, u32 max_hdcp_key_verify_retries)
{
	struct hdcp1_qsee_handle *handle = ctx;

	handle->max_hdcp_key_verify_retries = max_hdcp_key_verify_retries;

	pr_debug("hdcp1 max_hdcp_key_verify_retries %d\n", handle->max_hdcp_key_verify_retries);
}
EXPORT_SYMBOL_GPL(hdcp1_set_hdcp_key_verify_retries);

void hdcp1_deinit(void *data)
{
	kfree(data);
}
EXPORT_SYMBOL_GPL(hdcp1_deinit);

bool hdcp1_feature_supported(void *data)
{
	bool supported = false;

	mutex_lock(&hdcp1_mutex_g);
	supported = ta_interface.trusted_app_hdcp1_feature_supported(data);
	mutex_unlock(&hdcp1_mutex_g);

	return supported;
}
EXPORT_SYMBOL_GPL(hdcp1_feature_supported);

int hdcp1_set_enc(void *data, bool enable)
{
	int ret = 0;

	mutex_lock(&hdcp1_mutex_g);
	ret = ta_interface.trusted_app_hdcp1_set_enc(data, enable);
	mutex_unlock(&hdcp1_mutex_g);

	return ret;
}
EXPORT_SYMBOL_GPL(hdcp1_set_enc);

int hdcp1_ops_notify(void *data, void *topo, bool is_authenticated)
{
	int ret = 0;

	ret = ta_interface.trusted_app_hdcp1_ops_notify(data, topo, is_authenticated);

	return ret;
}
EXPORT_SYMBOL_GPL(hdcp1_ops_notify);

int hdcp1_start(void *data, u32 *aksv_msb, u32 *aksv_lsb)
{
	int ret = 0;

	mutex_lock(&hdcp1_mutex_g);
	ret = ta_interface.trusted_app_hdcp1_start(data, aksv_msb, aksv_lsb);
	mutex_unlock(&hdcp1_mutex_g);

	return ret;
}
EXPORT_SYMBOL_GPL(hdcp1_start);

void hdcp1_stop(void *data)
{
	mutex_lock(&hdcp1_mutex_g);
	ta_interface.trusted_app_hdcp1_stop(data);
	mutex_unlock(&hdcp1_mutex_g);
}
EXPORT_SYMBOL_GPL(hdcp1_stop);

static int __init hdcp_module_init(void)
{
	struct device_node *np = NULL;
	bool use_smcinvoke = false;

	np = of_find_compatible_node(NULL, NULL, "qcom,hdcp");
	if (!np) {
		/*select qseecom interface as default if hdcp node
		*is not present in dtsi
		 */
		 select_interface(use_smcinvoke);
		return 0;
		}

	use_smcinvoke = of_property_read_bool(np, "qcom,use-smcinvoke");

	select_interface(use_smcinvoke);

	return 0;
}

static void __exit hdcp_module_exit(void)
{
}

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("HDCP driver");

module_init(hdcp_module_init);
module_exit(hdcp_module_exit);