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

#include <linux/kernel.h>

#include "alp_sideband.h"
#include "ring_buf.h"

#include "atsc30_log.h"
#include "atsc30_utils.h"

//-------------------------------------------------------------------------------------------------
//  Debug Constants and Macros
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Constants
//-------------------------------------------------------------------------------------------------
#define ALP_SIDEBAND_BUS_0_MAX (3)

//-------------------------------------------------------------------------------------------------
//  Macros
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Structs and Enums
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Functions Prototype
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Variables
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Internal Functions
//-------------------------------------------------------------------------------------------------
static inline u32 _extract_alp_length(const u32 *data)
{
	return (u32)ALP_SIDEBAND_GET_ALP_LENGTH(*data);
}

// alp_bus1
static inline u8 _extract_alp_plp_id(const u32 *data)
{
	return (u8)ALP_SIDEBAND_GET_ALP_PLP_ID(*data);
}

static inline u16 _extract_alp_head_length(const u32 *data)
{
	return (u16)ALP_SIDEBAND_GET_ALP_HEAD_LENGTH(*data);
}

static inline bool _extract_out_alp_valid_pkt_idx(const u32 *data)
{
	return (bool)ALP_SIDEBAND_GET_OUT_ALP_VALID_PKT_IDX(*data);
}

static inline bool _extract_deadp_ip_packet_type_idx(const u32 *data)
{
	return (bool)ALP_SIDEBAND_GET_DEADP_IP_PACKET_TYPE_IDX(*data);
}

static inline u8 _extract_max_cid(const u32 *data)
{
	return  (u8)ALP_SIDEBAND_GET_MAX_CID(*data);
}

static inline u8 _extract_rl_plp_id(const u32 *data)
{
	return  (u8)ALP_SIDEBAND_GET_RL_PLP_ID(*data);
}

// alp_bus2
static inline u32 _extract_ip_total_length(const u32 *data)
{
	return  (u32)ALP_SIDEBAND_GET_IP_TOTAL_LENGTH(*data);
}

static inline u8 _extract_out_filter_id(const u32 *data)
{
	return  (u8)ALP_SIDEBAND_GET_OUT_FILTER_ID(*data);
}

//-------------------------------------------------------------------------------------------------
//  Public Functions
//-------------------------------------------------------------------------------------------------
u32 alp_sideband_extract_alp_length(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return 0;

	return _extract_alp_length(data);
}

u8 alp_sideband_extract_alp_plp_id(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return 0;

	return _extract_alp_plp_id(data);
}

u16 alp_sideband_extract_alp_head_length(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return 0;

	return _extract_alp_head_length(data);
}

bool alp_sideband_extract_out_alp_valid_pkt_idx(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return false;

	return _extract_out_alp_valid_pkt_idx(data);
}

bool alp_sideband_extract_deadp_ip_packet_type_idx(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return false;

	return _extract_deadp_ip_packet_type_idx(data);
}

u8 alp_sideband_extract_max_cid(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return 0;

	return _extract_max_cid(data);
}

u8 alp_sideband_extract_rl_plp_id(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return 0;

	return _extract_rl_plp_id(data);
}

u32 alp_sideband_extract_ip_total_length(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return 0;

	return _extract_ip_total_length(data);
}

u8 alp_sideband_extract_out_filter_id(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return 0;

	return _extract_out_filter_id(data);
}

int alp_sideband_parse(
		const uintptr_t addr_1,
		size_t addr_len_1,
		const uintptr_t addr_2,
		size_t addr_len_2,
		struct alp_sideband *alp_sideband)
{
	int ret_tmp = 0;
	u8 data_bus_0[sizeof(u32)] = { 0 };
	u32 data_bus = 0;
	u8 data[ALP_SIDEBAND_BYTE_MAX] = { 0 };
	ring_buf_t ring_buf = { 0 };

	if (ALP_HAL_VERIFY(alp_sideband == NULL))
		return ALP_NULL_PTR;

	if (addr_1 == UINTPTR_T_NULL)
		return ALP_ERROR;

	if ((addr_len_1 + addr_len_2) < ALP_SIDEBAND_BYTE_MAX)
		return ALP_ERROR;

	if (addr_len_2 == 0)
		ret_tmp = ring_buf_set(
				&ring_buf,
				(u8 *)addr_1,
				addr_len_1);
	else
		ret_tmp = ring_buf_set(
				&ring_buf,
				(u8 *)addr_2,
				addr_1 + addr_len_1 - addr_2);
	if (ret_tmp != 0) {
		ALP_HAL_LOG_ENHANCED_ERROR(
			"ring_buf_set is failed\n");

		return ALP_ERROR;
	}
	ALP_HAL_LOG_DEBUG("ring_buf\n");
	ring_buf_dump_info(
		A30_DBG_LVL_DEBUG,
		&ring_buf);

	ring_buf_copy_range_by_start_and_size(
		(u8 *)data,
		ring_buf,
		addr_1,
		sizeof(data));

	atsc30_log_dump_hex_array(
		A30_DBG_LVL_DEBUG,
		ATSC30_LOG_MODULE_HAL,
		data,
		sizeof(data),
		sizeof(data[0]),
		"data");

	// alp_bus_0
	memset(data_bus_0, 0, sizeof(data_bus_0));
	memcpy(data_bus_0 + 1, &data[ALP_SIDEBAND_ALP_LENGTH], sizeof(data_bus_0) - 1);
	data_bus =
		be32_to_cpu(*((u32 *) data_bus_0));
	alp_sideband->alp_bus_0.alp_length =
		alp_sideband_extract_alp_length(&data_bus);

	// alp_bus_1
	data_bus =
		be32_to_cpu(*((u32 *) &data[ALP_SIDEBAND_BUS_1]));
	alp_sideband->alp_bus_1.alp_plpid =
		alp_sideband_extract_alp_plp_id(&data_bus);
	alp_sideband->alp_bus_1.alp_head_length =
		alp_sideband_extract_alp_head_length(&data_bus);
	alp_sideband->alp_bus_1.out_alp_valid_pkt_idx =
		alp_sideband_extract_out_alp_valid_pkt_idx(&data_bus);
	alp_sideband->alp_bus_1.deadp_ip_pkt_type_idx =
		alp_sideband_extract_deadp_ip_packet_type_idx(&data_bus);
	alp_sideband->alp_bus_1.max_cid =
		alp_sideband_extract_max_cid(&data_bus);
	alp_sideband->alp_bus_1.rl_plp_id =
		alp_sideband_extract_rl_plp_id(&data_bus);

	// alp_bus_2
	data_bus =
		be32_to_cpu(*((u32 *) &data[ALP_SIDEBAND_BUS_2]));
	alp_sideband->alp_bus_2.ip_total_length =
		alp_sideband_extract_ip_total_length(&data_bus);
	alp_sideband->alp_bus_2.out_filter_id =
		alp_sideband_extract_out_filter_id(&data_bus);

	alp_sideband_dump(
		A30_DBG_LVL_DEBUG,
		alp_sideband);

	return ALP_SUCCESS;
}

void alp_sideband_dump(
		int log_level,
		struct alp_sideband *alp_sideband)
{
	if (ALP_FW_VERIFY(alp_sideband == NULL))
		return;

	ALP_HAL_LOG(
		log_level,
		"alp_bus_0.alp_length = 0x%x\n",
		alp_sideband->alp_bus_0.alp_length);

	ALP_HAL_LOG(
		log_level,
		"alp_bus_1.rl_plp_id = 0x%x\n",
		alp_sideband->alp_bus_1.rl_plp_id);
	ALP_HAL_LOG(
		log_level,
		"          max_cid = 0x%x\n",
		alp_sideband->alp_bus_1.max_cid);
	ALP_HAL_LOG(
		log_level,
		"          deadp_ip_pkt_type_idx = %s\n",
		ATSC30_BOOL_2_STR(alp_sideband->alp_bus_1.deadp_ip_pkt_type_idx));
	ALP_HAL_LOG(
		log_level,
		"          out_alp_valid_pkt_idx = %s\n",
		ATSC30_BOOL_2_STR(alp_sideband->alp_bus_1.out_alp_valid_pkt_idx));
	ALP_HAL_LOG(
		log_level,
		"          alp_head_length = 0x%x\n",
		alp_sideband->alp_bus_1.alp_head_length);
	ALP_HAL_LOG(
		log_level,
		"          alp_plpid = 0x%x\n",
		alp_sideband->alp_bus_1.alp_plpid);

	ALP_HAL_LOG(
		log_level,
		"alp_bus_2.out_filter_id = 0x%x\n",
		alp_sideband->alp_bus_2.out_filter_id);
	ALP_HAL_LOG(
		log_level,
		"          ip_total_length = 0x%x\n",
		alp_sideband->alp_bus_2.ip_total_length);
}
