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

#include "alp_desc.h"
#include "ring_buf.h"

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

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

//-------------------------------------------------------------------------------------------------
//  Constants
//-------------------------------------------------------------------------------------------------

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

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

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

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

//-------------------------------------------------------------------------------------------------
//  Internal Functions
//-------------------------------------------------------------------------------------------------

/***
 * FW notify descriptor format
 * DescWord0
 * +--------------------+---------------------+-------------------------+----------------------+
 * |seg_info(seg alp)   |  max_cid (comp pkt) |	    plp_id              |         fw_pkt_type  |
 * +--------------------+---------------------+-------------------------+----------------------+
 * DescWord1
 * +--------------------+---------------------+-------------------------+----------------------+
 * |                 Reserved                 |     pud_type            |          stream_idx  |
 * +--------------------+---------------------+-------------------------+----------------------+
 * DescWord2
 * +--------------------+---------------------+-------------------------+----------------------+
 * |                 PacketEnd + 1 Physical Address in Payload Buffer                          |
 * +--------------------+---------------------+-------------------------+----------------------+
 * DescWord3
 * +--------------------+---------------------+-------------------------+----------------------+
 * |                   PacketStart Physical Address in Payload Buffer                          |
 * +--------------------+---------------------+-------------------------+----------------------+
 * DescWord4
 * +--------------------+---------------------+-------------------------+----------------------+
 * |                                      timestamp                                            |
 * +--------------------+---------------------+-------------------------+----------------------+
 */

/***
 * Sony ATSC 3.0 demodulator defines two priavte alp pacekt types
 * +-------------------------------------------------------------------------------+
 * +------------Alp Packet Header ------+-------------Additional Header -----------+
 * 0 -----------------------------------15-----------------------------------------23
 * |  3 Bit |	1 Bit |  1 Bit |  11 bit |  5 Bit     |  1 Bit	|  1 Bit  |  1 Bit |
 * |  Type  |	 PC   |   HM   |  Length | Length_MSB |   RSV	|   SIF   |   HEF  |
 * |  000b  |	 0b   |   1b   |   0x00  |  0x00      |    1b	|    1b   |    1b  |
 * +------------------------------------+------------------------------------------+
 * *********************** Case 1 : PLP_ID Packet **********************************
 * +-------------------------------------------------------------------------------+
 * +--------------------------Header Extension(Private User Defined)---------------+
 * 0-----------------------------------15------------------------------------------23
 * |      8 Bit      |      8 Bit       |        6 Bit          |      2 Bit       |
 * | Extension_Type  | Extension_Length |       PLP_ID          |      Dummy       |
 * |      0xF1       |      0x01        |                       |                  |
 * +------------------------------------+------------------------------------------+
 * **************************** Case 2 : PTP Packet ************************************
 * +-----------------------------------------------------------------------------------+
 * +--------------------------Header Extension(Private User Defined)-------------------+
 * 0-----------------------------------15----------------------------------------------79
 * |       8 Bit     |        8 Bit     | 2 Bit    |  32 Bit | 10 Bit | 10 Bit | 10 Bit |
 * | Extension_Type  | Extension_Length |Time_Info |   Time  |  Time  |  Time  |  Time  |
 * |                 |                  |    Flag  |    sec  |  msec  |  usec  |  nsec  |
 * |      0xF0       |         0x08     |          |         |        |        |        |
 * +------------------------------------+-----------------------------------------------+
 *
 * seg_info format
 * +--------------+----------------+---------------+
 * | seq_no[7:3]  | last indi[2:2] | reserved[1:0] |
 * +--------------+----------------+---------------+
 */

static inline alp_fw_notify_packet_type_t _extract_fw_notify_packet_type(
		const u32 *data)
{
	return (alp_fw_notify_packet_type_t)
		ALP_DESC_GET_FW_NOTIFY_PACKET_TYPE(*data);
}

static inline u8 _extract_plp_id(const u32 *data)
{
	return  (u8)ALP_DESC_GET_PLP_ID(*data);
}

static inline u8 _extract_comp_rohc_max_cid(const u32 *data)
{
	return  (u8)ALP_DESC_GET_MAX_CID(*data);
}

static inline u8 _extract_segment_seg_seq_no(const u32 *data)
{
	return  (u8)ALP_DESC_GET_SEQ_NO(*data);
}

static inline bool _extract_segment_last_indicator_flag(const u32 *data)
{
	return  (bool)ALP_DESC_GET_LAST_INDICATOR_FLAG(*data);
}

static inline u8 _extract_stream_idx(const u32 *data)
{
	return  (u8)ALP_DESC_GET_STREAM_IDX(*data);
}

static inline u8 _extract_pud_type(const u32 *data)
{
	return  (u8)ALP_DESC_GET_PUD_TYPE(*data);
}

static inline u32 _extract_timestamp(const u32 *data)
{
	return  (u32)ALP_DESC_GET_TIMESTAMP(*data);
}

static inline const char *_get_nfy_pkt_type_str(
		alp_fw_notify_packet_type_t nfy_pkt_type)
{
	switch (nfy_pkt_type) {
	case  FW_NFY_PKT_TYPE_RESERVED:
		return "FW_NFY_PKT_TYPE_RESERVED";

	case  FW_NFY_PKT_TYPE_IP:
		return "FW_NFY_IP";

	case  FW_NFY_PKT_TYPE_COMP_IP:
		return "FW_NFY_COMP_IP";

	case  FW_NFY_PKT_TYPE_ALP_LLS:
		return "FW_NFY_ALP_LLS";

	case  FW_NFY_PKT_TYPE_SEG_IP:
		return "FW_NFY_SEG_IP";

	case  FW_NFY_PKT_TYPE_NET_PKT_ALP:
		return "FW_NFY_NET_ALP";

	case  FW_NFY_PKT_TYPE_PUD_ALP:
		return "FW_NFY_PUD_ALP";

	case  FW_NFY_PKT_TYPE_SEG_COMP_IP:
		return "FW_NFY_SEG_COMP_IP";

	case  FW_NFY_PKT_TYPE_MAX:
		return "FW_NFY_PKT_TYPE_MAX";

	default:
		break;
	}

	return "BAD UN-RECOGNIZED FW_PKT_TYPE";
}

//-------------------------------------------------------------------------------------------------
//  Public Functions
//-------------------------------------------------------------------------------------------------
alp_fw_notify_packet_type_t alp_desc_extract_fw_notify_packet_type(
		const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return FW_NFY_PKT_TYPE_NULL;

	return _extract_fw_notify_packet_type(data);
}

u8 alp_desc_extract_plp_id(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return FW_NFY_PKT_TYPE_NULL;

	return _extract_plp_id(data);
}

u8 alp_desc_extract_comp_rohc_max_cid(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return FW_NFY_PKT_TYPE_NULL;

	return _extract_comp_rohc_max_cid(data);
}

u8 alp_desc_extract_segment_seg_seq_no(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return FW_NFY_PKT_TYPE_NULL;

	return _extract_segment_seg_seq_no(data);
}

bool alp_desc_extract_segment_last_indicator_flag(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return FW_NFY_PKT_TYPE_NULL;

	return _extract_segment_last_indicator_flag(data);
}

u8 alp_desc_extract_stream_idx(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return FW_NFY_PKT_TYPE_NULL;

	return _extract_stream_idx(data);
}

u8 alp_desc_extract_pud_type(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return FW_NFY_PKT_TYPE_NULL;

	return _extract_pud_type(data);
}

u32 alp_desc_extract_timestamp(const u32 *data)
{
	if (ALP_HAL_VERIFY(data == NULL))
		return FW_NFY_PKT_TYPE_NULL;

	return _extract_timestamp(data);
}

struct alp_fw_packet_descriptor alp_desc_parse_descriptor(
		const uintptr_t addr_1,
		size_t addr_len_1,
		const uintptr_t addr_2,
		size_t addr_len_2)
{
	int ret_tmp = 0;
	struct alp_fw_packet_descriptor desc = {
		.fw_nfy_pkt_type = FW_NFY_PKT_TYPE_RESERVED,
	};
	u32 data[DESC_WORD_MAX] = {0};
	uintptr_t *packet_end = (uintptr_t *) &data[DESC_WORD_PACKET_END];
	uintptr_t *packet_start = (uintptr_t *) &data[DESC_WORD_PACKET_START];
	ring_buf_t ring_buf = {0};

	if (addr_1 == UINTPTR_T_NULL)
		return desc;

	if ((addr_len_1 + addr_len_2) < ALP_SINGLE_HEADER_BYTE)
		return desc;

	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_ERROR(
			"[%s][%d] ring_buf_set is failed\n",
			__func__,
			__LINE__);

		return desc;
	}
	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,
		(void *)ring_buf.rp,
		min(sizeof(data), (size_t)ring_buf_calculate_range_size(ring_buf,ring_buf.rp,ring_buf.end)),
		sizeof(data[0]),
		"ring_buf");
	atsc30_log_dump_hex_array(
		A30_DBG_LVL_DEBUG,
		ATSC30_LOG_MODULE_HAL,
		data,
		sizeof(data),
		sizeof(data[0]),
		"data");

	desc.fw_nfy_pkt_type =
		_extract_fw_notify_packet_type(data + DESC_WORD_FW_PKT_TYPE);
	desc.plp_id =
		_extract_plp_id(data + DESC_WORD_PLP_ID);
	desc.timestamp =
		_extract_timestamp(data + DESC_WORD_TIMESTAMP);

	desc.pkt_start = *packet_start;
	desc.pkt_end = *packet_end;
	if (ALP_PARSER_VERIFY(desc.pkt_start == UINTPTR_T_NULL))
		return desc;
	if (ALP_PARSER_VERIFY(desc.pkt_end == UINTPTR_T_NULL))
		return desc;
	ALP_HAL_LOG_DEBUG(
		"Descriptor base word[0] 0x%llx: type %s\n",
		(u64)addr_1,
		_get_nfy_pkt_type_str(desc.fw_nfy_pkt_type));
	ALP_HAL_LOG_DEBUG(
		"Descriptor plp_id %d, pkt range[0x%llx, 0x%llx)\n",
		desc.plp_id,
		(u64)desc.pkt_start,
		(u64)desc.pkt_end);

	if ((desc.fw_nfy_pkt_type == FW_NFY_PKT_TYPE_IP) ||
		(desc.fw_nfy_pkt_type == FW_NFY_PKT_TYPE_COMP_IP) ||
		(desc.fw_nfy_pkt_type == FW_NFY_PKT_TYPE_SEG_IP) ||
		(desc.fw_nfy_pkt_type == FW_NFY_PKT_TYPE_SEG_COMP_IP) ||
		(desc.fw_nfy_pkt_type == FW_NFY_PKT_TYPE_NET_PKT_ALP)) {
		desc.strm_idx =
			_extract_stream_idx(
				data + DESC_WORD_STREAM_IDX);
		ALP_HAL_LOG_DEBUG(
			"Descriptor cont: stream idx %d\n",
			desc.strm_idx);
	}

	if ((desc.fw_nfy_pkt_type == FW_NFY_PKT_TYPE_COMP_IP) ||
		(desc.fw_nfy_pkt_type == FW_NFY_PKT_TYPE_SEG_COMP_IP)) {
		desc.max_cid =
			_extract_comp_rohc_max_cid(
				data + DESC_WORD_MAX_CID);
		ALP_FW_LOG_DEBUG(
			"Descriptor cont: max cid %d\n",
			desc.max_cid);
	}

	if ((desc.fw_nfy_pkt_type == FW_NFY_PKT_TYPE_SEG_COMP_IP) ||
		(desc.fw_nfy_pkt_type == FW_NFY_PKT_TYPE_SEG_IP)) {
		desc.seg_num =
			_extract_segment_seg_seq_no(
				data + DESC_WORD_SEG_INFO);
		desc.last_seg_indicator =
			_extract_segment_last_indicator_flag(
				data + DESC_WORD_SEG_INFO);
		ALP_FW_LOG_DEBUG(
			"Descriptor cont: seq No%d, last Seg ind(%d)\n",
			desc.seg_num,
			desc.last_seg_indicator);
	}

	if (desc.fw_nfy_pkt_type == FW_NFY_PKT_TYPE_PUD_ALP) {
		desc.pud_type = _extract_pud_type(data + DESC_WORD_PUD_TYPE);
		ALP_FW_LOG_DEBUG(
			"Descriptor cont: pud type(%d)\n",
			desc.pud_type);
	}

	return desc;
}

void alp_desc_dump_descriptor(
		int log_level,
		struct alp_fw_packet_descriptor *descriptor)
{
	if (ALP_FW_VERIFY(descriptor == NULL))
		return;

	ALP_FW_LOG(
		log_level,
		"plp_id = 0x%x\n",
		descriptor->plp_id);
	ALP_FW_LOG(
		log_level,
		"max_cid = 0x%x\n",
		descriptor->max_cid);
	ALP_FW_LOG(
		log_level,
		"strm_idx = 0x%x\n",
		descriptor->strm_idx);
	ALP_FW_LOG(
		log_level,
		"last_seg_indicator = %s\n",
		ATSC30_BOOL_2_STR(descriptor->last_seg_indicator));
	ALP_FW_LOG(
		log_level,
		"seg_num = 0x%x\n",
		descriptor->seg_num);
	ALP_FW_LOG(
		log_level,
		"pud_type = 0x%x\n",
		descriptor->pud_type);
	ALP_FW_LOG(
		log_level,
		"fw_nfy_pkt_type = %s\n",
		_get_nfy_pkt_type_str(descriptor->fw_nfy_pkt_type));
	ALP_FW_LOG(
		log_level,
		"pkt_start = 0x%llx\n",
		(u64)descriptor->pkt_start);
	ALP_FW_LOG(
		log_level,
		"pkt_end = 0x%llx\n",
		(u64)descriptor->pkt_end);
}
