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

#include <linux/types.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>

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

#include "mdrv_atsc30_if.h"
#include "mdrv_atsc30_internal.h"
#include "mdrv_atsc30_ptp.h"

#include "mtk_dmx_dev.h"
#include "mtk_dmx_common.h"
#include "mtk_dmx_drv.h"
#include "mtk_dmx_filter.h"
#include "mtk_dmx_atsc3.h"
#include "mtk_dmx_pvr.h"

//-------------------------------------------------------------------------------------------------
//  Debug Constants and Macros
//-------------------------------------------------------------------------------------------------
//#define MDRV_ATSC30_PTP_MUTEX_DEBUG_ENABLE
//#define MDRV_ATSC30_PTP_STAGE_2_TESTING_ENABLE

#ifdef MDRV_ATSC30_PTP_MUTEX_DEBUG_ENABLE
#define MDRV_ATSC30_PTP_MUTEX_LOG(...) \
	ATSC30_DRVIF_LOG_DEBUG(##__VA_ARGS__)
#else
#define MDRV_ATSC30_PTP_MUTEX_LOG(...)
#endif

#define MDRV_ATSC30_PTP_PROFILE_LOG_US_MIN (500)

#ifdef MDRV_ATSC30_PTP_STAGE_2_TESTING_ENABLE
#define MDRV_ATSC30_PTP_STAGE_2_PTP_TESTING_PACKET_NO (6)
#define MDRV_ATSC30_PTP_STAGE_2_PTP_TESTING_PACKET_TIMESTAMP_DIFF \
	(FREQ_90KHZ / 10) /* 100 ms */
#endif //#ifdef MDRV_ATSC30_PTP_STAGE_2_TESTING_ENABLE

//-------------------------------------------------------------------------------------------------
//  Constants
//-------------------------------------------------------------------------------------------------
#define PCR_RESET_COUNT (2)
#define STC_GAP_IN_90KHZ (0x4000)
#define STC_DIFF_CONDITION1_IN_90KHZ (0x2)
#define STC_DIFF_CONDITION2_IN_90KHZ (0x1000)
#define PLL_DIV_SCALE1 (3)
#define PLL_DIV_SCALE2 (6)

#define TIMESTAMP_WIDTH (32)
#define TIMESTAMP_UP_BOUND (((u64)1) << TIMESTAMP_WIDTH)

#define FREQ_INVALID (-1)

#define PTP_WRAP_CONDITION (2)

//-------------------------------------------------------------------------------------------------
//  Macros
//-------------------------------------------------------------------------------------------------
#define MDRV_ATSC30_PTP_LOCK(framer_idx) \
	do { \
		MDRV_ATSC30_PTP_MUTEX_LOG( \
			"MDRV_ATSC30_PTP_LOCK %d\n", \
			framer_idx); \
		mutex_lock(&_locks[framer_idx]); \
	} while (0)

#define MDRV_ATSC30_PTP_UNLOCK(framer_idx) \
	do { \
		mutex_unlock(&_locks[framer_idx]); \
		MDRV_ATSC30_PTP_MUTEX_LOG( \
			"MDRV_ATSC30_PTP_UNLOCK %d\n", \
			framer_idx); \
	} while (0)

#define MDRV_ATSC30_PTP_STC_LOCK(framer_idx) \
	do { \
		MDRV_ATSC30_PTP_MUTEX_LOG( \
			"MDRV_ATSC30_PTP_STC_LOCK %d\n", \
			framer_idx); \
		spin_lock(&_stc_locks[framer_idx]); \
	} while (0)

#define MDRV_ATSC30_PTP_STC_UNLOCK(framer_idx) \
	do { \
		spin_unlock(&_stc_locks[framer_idx]); \
		MDRV_ATSC30_PTP_MUTEX_LOG( \
			"MDRV_ATSC30_PTP_STC_UNLOCK %d\n", \
			framer_idx); \
	} while (0)

#define TIMESTAMP_2_STC(stc_frequency, timestamp) \
	ptp_freq_convert(timestamp, FREQ_90KHZ, stc_frequency)

#define ROUNDED_DIFF(up_bound, minuend, subtraction) \
	({ \
		u64 __ret = 0; \
		if ((minuend) > (subtraction)) \
			__ret = (minuend) - (subtraction); \
		else \
			__ret = (up_bound) - (subtraction) + (minuend); \
		__ret; \
	})

#define ROUNDED_ADD(up_bound, augend, addend) \
	ptp_modulo(augend + addend, up_bound)

//-------------------------------------------------------------------------------------------------
//  Structs and Enums
//-------------------------------------------------------------------------------------------------
enum ptp_adjustment_timestamp_type {
	PTP_ADJ_TIMESTAMP_MIN = 0,

	PTP_ADJ_TIMESTAMP_0 = PTP_ADJ_TIMESTAMP_MIN,
	PTP_ADJ_TIMESTAMP_1,
	PTP_ADJ_TIMESTAMP_LAST,

	PTP_ADJ_TIMESTAMP_MAX,
};

struct ptp_params {
	u64 new_stcs[PTP_ADJ_TIMESTAMP_MAX];
	u64 timestamps[PTP_ADJ_TIMESTAMP_MAX];

	bool is_first_event;
};

//-------------------------------------------------------------------------------------------------
//  Functions Prototype
//-------------------------------------------------------------------------------------------------
static int ptp_get_context(
		u8 framer_idx,
		struct mdrv_atsc30_internal_context **context);

//-------------------------------------------------------------------------------------------------
//  Variables
//-------------------------------------------------------------------------------------------------
static struct mdrv_atsc30_internal_context _contexts[eATSC30_SUPPORT_FRAMER_NUM] = {
	{
		.inited = false,
		.ref_count = 0,
		.framer_idx = eATSC30_ALP_FRAMER_0,
		.pkt_id_counter = 0,
	},
	{
		.inited = false,
		.ref_count = 0,
		.framer_idx = eATSC30_ALP_FRAMER_1,
		.pkt_id_counter = 0,
	},
};

static struct mutex _locks[eATSC30_SUPPORT_FRAMER_NUM] = {
	__MUTEX_INITIALIZER(_locks[eATSC30_ALP_FRAMER_0]),
	__MUTEX_INITIALIZER(_locks[eATSC30_ALP_FRAMER_1]),
};

static struct ptp_params _ptp_params[eATSC30_SUPPORT_FRAMER_NUM] = {
	{
		.new_stcs = { 0 },
		.timestamps = { 0 },
		.is_first_event = true,
	},
};

static spinlock_t _stc_locks[eATSC30_SUPPORT_FRAMER_NUM] = {
	__SPIN_LOCK_INITIALIZER(_stc_locks[eATSC30_ALP_FRAMER_0]),
	__SPIN_LOCK_INITIALIZER(_stc_locks[eATSC30_ALP_FRAMER_1]),
};

//-------------------------------------------------------------------------------------------------
//  Internal Functions
//-------------------------------------------------------------------------------------------------
static u64 ptp_freq_convert(u64 val, u64 from_frequency, u64 to_frequency)
{
	u64 ret = 0;
	ALP_RET_CODE_T tmp_ret = 0;
	struct u128_t result = { 0 };

	if ((to_frequency > from_frequency)
		&& ((to_frequency % from_frequency) == 0))
		return div_u64(to_frequency, from_frequency) * val;

	tmp_ret =
		atsc30_utils_mult_64_to_128(
			val,
			to_frequency,
			&result);
	if (tmp_ret != ALP_SUCCESS) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"atsc30_utils_mult_64_to_128 is failed\n");

		return FREQ_INVALID;
	}
	tmp_ret =
		atsc30_utils_div_128_by_64(
			result.h,
			result.l,
			from_frequency,
			&ret,
			NULL);
	if (tmp_ret != ALP_SUCCESS) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"atsc30_utils_div_128_by_64 is failed\n");

		return FREQ_INVALID;
	}

	return ret;
}

// Due to kernel 32 bits limitation, 64-bits modulo is not supported.
static u64 ptp_modulo(u64 val, u64 modulo)
{
	while (val >= modulo)
		val -= modulo;

	return val;
}

static int ptp_get_context(
		u8 framer_idx,
		struct mdrv_atsc30_internal_context **context)
{
	if (ATSC30_DRVIF_VERIFY(framer_idx >= eATSC30_SUPPORT_FRAMER_NUM))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;

	*context = &_contexts[framer_idx];

	return 0;
}

static struct ptp_params *ptp_get_ptp_params(u8 framer_idx)
{
	if (ATSC30_DRVIF_VERIFY(framer_idx >= eATSC30_SUPPORT_FRAMER_NUM))
		return NULL;

	return &_ptp_params[framer_idx];
}

static int ptp_reset_ptp_params(u8 framer_idx)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	struct ptp_params *ptp_params = ptp_get_ptp_params(framer_idx);

	if (ATSC30_DRVIF_VERIFY(ptp_params == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;

	memset(ptp_params, 0, sizeof(*ptp_params));
	ptp_params->is_first_event = true;

	return ret;
}

static void ptp_dump_ptp_params(
		struct ptp_params *ptp_params,
		const char *title)
{
	int i = 0;

	if (ATSC30_DRVIF_VERIFY(ptp_params == NULL))
		return;

	ATSC30_DRVIF_LOG_FTRACE(
		"%s\n", (title? title: ""));
	ATSC30_DRVIF_LOG_FTRACE(
		"ptp_params->is_first_event %s\n",
		ATSC30_BOOL_2_STR(ptp_params->is_first_event));
	for (i = 0; i < PTP_ADJ_TIMESTAMP_MAX; i++) {
		ATSC30_DRVIF_LOG_FTRACE(
			"new_stcs[%d] 0x%llx timestamps[%d] 0x%llx\n",
			i,
			ptp_params->new_stcs[i],
			i,
			ptp_params->timestamps[i]);
	}
}

static int ptp_set_ptp_params(
	struct ptp_params *ptp_params,
	u8 index,
	u64 new_stc,
	u64 timestamp)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;

	if (ATSC30_DRVIF_VERIFY(ptp_params == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;
	if (ATSC30_DRVIF_VERIFY(index >= PTP_ADJ_TIMESTAMP_MAX))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;

	ptp_params->new_stcs[index] = new_stc;
	ptp_params->timestamps[index] = timestamp;

	ATSC30_DRVIF_LOG_FTRACE(
		"ptp_params->new_stcs[%d] 0x%llx timestamps[%d] 0x%llx\n",
		index,
		ptp_params->new_stcs[index],
		index,
		ptp_params->timestamps[index]);

	return ret;
}

static int ptp_init(
		u8 framer_idx)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	struct mdrv_atsc30_internal_context *context = NULL;

	ret = ptp_get_context(framer_idx, &context);
	if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"ptp_get_context(%d, ...) is failed\n",
			framer_idx);

		return ret;
	}

	do {
		ret = mdrv_atsc30_priv_context_init(context);
		if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"mdrv_atsc30_priv_context_init of %d is failed\n",
				framer_idx);

			break;
		}

		ret = ptp_reset_ptp_params(framer_idx);
		if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"ptp_reset_ptp_params of %d is failed\n",
				framer_idx);


			break;
		}
	} while (0);

	return ret;
}

static int ptp_exit(
		u8 framer_idx)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	struct mdrv_atsc30_internal_context *context = NULL;

	ret = ptp_get_context(framer_idx, &context);
	if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"ptp_get_context(%d, ...) is failed\n",
			framer_idx);

		return ret;
	}

	ret = mdrv_atsc30_priv_context_exit(context);
	if (ret != MDRV_ATSC30_RETURN_CODE_OK)
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"mdrv_atsc30_priv_context_exit of %d is failed\n",
			framer_idx);

	return ret;
}

static int ptp_add_filter(
		u8 framer_idx,
		struct dmx2_filter *demux_filter)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	struct mdrv_atsc30_internal_context *context = NULL;

	ret = ptp_get_context(framer_idx, &context);
	if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"ptp_get_context(%d, ...) is failed\n",
			framer_idx);

		return ret;
	}

	ret = mdrv_atsc30_priv_filter_add(
			context,
			demux_filter);
	if (ret != MDRV_ATSC30_RETURN_CODE_OK)
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"mdrv_atsc30_priv_filter_add of %d is failed\n",
			framer_idx);

	return ret;
}

static int ptp_del_filter(
		u8 framer_idx,
		struct dmx2_filter *demux_filter)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	struct mdrv_atsc30_internal_context *context = NULL;

	ret = ptp_get_context(framer_idx, &context);
	if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"ptp_get_context(%d, ...) is failed\n",
			framer_idx);

		return ret;
	}

	ret = mdrv_atsc30_priv_filter_del(
			context,
			demux_filter);
	if (ret != MDRV_ATSC30_RETURN_CODE_OK)
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"mdrv_atsc30_priv_filter_del of %d is failed\n",
			framer_idx);

	return ret;
}

static int ptp_get_pcr_filter_cfg(
		struct dmx2_filter *dmx2_filter,
		void *data)
{
	int ret = 0;
	int tmp_ret = MDRV_ATSC30_RETURN_CODE_OK;
	struct ptp_filter_cfg ptp_filter_cfg;

	memset(&ptp_filter_cfg, 0, sizeof(struct ptp_filter_cfg));
	if (ATSC30_DRVIF_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	if (ATSC30_DRVIF_VERIFY(data == NULL))
		return -EINVAL;

	do {
		tmp_ret = mdrv_atsc30_priv_get_filter_info(
				dmx2_filter,
				&ptp_filter_cfg,
				sizeof(struct ptp_filter_cfg));
		if (tmp_ret != MDRV_ATSC30_RETURN_CODE_OK) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"mdrv_atsc30_priv_get_filter_info is failed\n");

			ret = -EIO;
			break;
		}

		memcpy(data,
			&ptp_filter_cfg.pcr_filter_cfg,
			sizeof(ptp_filter_cfg.pcr_filter_cfg));
	} while (0);

	return ret;
}

static int ptp_set_pcr_filter_cfg(
		struct dmx2_filter *dmx2_filter,
		void *data)
{
	int ret = 0;
	int tmp_ret = MDRV_ATSC30_RETURN_CODE_OK;
	struct ptp_filter_cfg ptp_filter_cfg;

	memset(&ptp_filter_cfg, 0, sizeof(struct ptp_filter_cfg));
	if (ATSC30_DRVIF_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	if (ATSC30_DRVIF_VERIFY(data == NULL))
		return -EINVAL;

	do {
		tmp_ret = mdrv_atsc30_priv_get_filter_info(
				dmx2_filter,
				&ptp_filter_cfg,
				sizeof(struct ptp_filter_cfg));
		if (tmp_ret != MDRV_ATSC30_RETURN_CODE_OK) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"mdrv_atsc30_priv_get_filter_info is failed\n");

			ret = -EIO;
			break;
		}
		memcpy(&ptp_filter_cfg.pcr_filter_cfg,
			data,
			sizeof(ptp_filter_cfg.pcr_filter_cfg));
		tmp_ret = mdrv_atsc30_priv_set_filter_info(
				dmx2_filter,
				&ptp_filter_cfg,
				sizeof(struct ptp_filter_cfg));
		if (tmp_ret != MDRV_ATSC30_RETURN_CODE_OK) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"mdrv_atsc30_priv_set_filter_info is failed\n");

			ret = -EIO;
		}
	} while (0);

	return ret;
}

static int ptp_get_timestamp(
		struct dmx2_filter *dmx2_filter,
		struct stc_event *stc_event,
		u64 *timestamp)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	struct dvb_fh *fh = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;
	int pvr_eng;

	if (ATSC30_DRVIF_VERIFY(dmx2_filter == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;
	if (ATSC30_DRVIF_VERIFY(stc_event == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;
	if (ATSC30_DRVIF_VERIFY(timestamp == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;
	fh = dmx2_filter->fh;
	if (ATSC30_DRVIF_VERIFY(fh == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;
	mtk_demux2 = fh_to_demux(fh);
	if (ATSC30_DRVIF_VERIFY(mtk_demux2 == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;

	do {
		pvr_eng = mtk_dmx_rm_pvr_find_engine_by_path(
			mtk_demux2->rm,
			mtk_demux2->path_id);
		if (pvr_eng < 0) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"mtk_dmx_rm_pvr_find_engine_by_path is failed\n");

			ret = MDRV_ATSC30_RETURN_CODE_FAILURE;
			break;
		}

		*timestamp = mtk_pvr_get_timestamp(pvr_eng);
		ATSC30_DRVIF_LOG_ENHANCED_FTRACE(
			"timestamp = 0x%llx\n",
			*timestamp);
	} while (0);

	return ret;
}

static int ptp_preset_ptp_params(
		struct ptp_params *ptp_params,
		struct stc_event *stc_event)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;

	if (ATSC30_DRVIF_VERIFY(ptp_params == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;
	if (ATSC30_DRVIF_VERIFY(stc_event == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;

	do {
		if (ptp_params->is_first_event) {
			ret = ptp_set_ptp_params(
					ptp_params,
					PTP_ADJ_TIMESTAMP_1,
					stc_event->new_stc,
					stc_event->timestamp);
			if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
				ATSC30_DRVIF_LOG_ENHANCED_ERROR(
					"ptp_set_ptp_params is failed\n");

				break;
			}
		} else {
			ret = ptp_set_ptp_params(
					ptp_params,
					PTP_ADJ_TIMESTAMP_0,
					ptp_params->new_stcs[PTP_ADJ_TIMESTAMP_1],
					ptp_params->timestamps[PTP_ADJ_TIMESTAMP_1]);
			if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
				ATSC30_DRVIF_LOG_ENHANCED_ERROR(
					"ptp_set_ptp_params is failed\n");

				break;
			}
			ret = ptp_set_ptp_params(
					ptp_params,
					PTP_ADJ_TIMESTAMP_1,
					stc_event->new_stc,
					stc_event->timestamp);
			if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
				ATSC30_DRVIF_LOG_ENHANCED_ERROR(
					"ptp_set_ptp_params is failed\n");

				break;
			}
		}
	} while (0);

	return ret;
}

static int ptp_adjust_ptp_params(
		struct ptp_params *ptp_params,
		u64 timestamp_up_bound,
		u64 current_timestamp,
		u64 stc_up_bound,
		u64 stc_frequency)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	ALP_RET_CODE_T tmp_ret = 0;
	struct u128_t result = { 0 };
	u64 stc_diff = 0;

	if (ATSC30_DRVIF_VERIFY(ptp_params == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;

	if (ptp_params->is_first_event) {
		ptp_params->new_stcs[PTP_ADJ_TIMESTAMP_LAST] =
			ROUNDED_ADD(
				stc_up_bound,
				ptp_params->new_stcs[PTP_ADJ_TIMESTAMP_1],
				TIMESTAMP_2_STC(
					stc_frequency,
					ROUNDED_DIFF(
						timestamp_up_bound,
						current_timestamp,
						ptp_params->timestamps[PTP_ADJ_TIMESTAMP_1])));
	} else {
		tmp_ret = atsc30_utils_mult_64_to_128(
			ROUNDED_DIFF(
				timestamp_up_bound,
				current_timestamp,
				ptp_params->timestamps[PTP_ADJ_TIMESTAMP_LAST]),
			ROUNDED_DIFF(
				stc_up_bound,
				ptp_params->new_stcs[PTP_ADJ_TIMESTAMP_1],
				ptp_params->new_stcs[PTP_ADJ_TIMESTAMP_0]),
			&result);
		if (tmp_ret != ALP_SUCCESS) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"atsc30_utils_mult_64_to_128 is failed\n");

			return MDRV_ATSC30_RETURN_CODE_FAILURE;
		}
		// TODO: Should q be contained in u64?
		tmp_ret = atsc30_utils_div_128_by_64(
				result.h,
				result.l,
				ROUNDED_DIFF(
					timestamp_up_bound,
					ptp_params->timestamps[PTP_ADJ_TIMESTAMP_1],
					ptp_params->timestamps[PTP_ADJ_TIMESTAMP_0]),
				&stc_diff,
				NULL);
		if (tmp_ret != ALP_SUCCESS) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"atsc30_utils_div_128_by_64 is failed\n");

			return MDRV_ATSC30_RETURN_CODE_FAILURE;
		}

		ptp_params->new_stcs[PTP_ADJ_TIMESTAMP_LAST] =
			ROUNDED_ADD(
				stc_up_bound,
				ptp_params->new_stcs[PTP_ADJ_TIMESTAMP_LAST],
				stc_diff);
	}
	ptp_params->timestamps[PTP_ADJ_TIMESTAMP_LAST] = current_timestamp;

	return ret;
}

static int ptp_check_wrap(
		struct ptp_params *ptp_params,
		u64 timestamp_up_bound,
		u64 stc_up_bound,
		u64 stc_frequency,
		bool *is_wrapped)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	u64 diff_from_timestamp = 0;
	u64 diff_stc = 0;
	u64 q = 0;

	if (ATSC30_DRVIF_VERIFY(ptp_params == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;
	if (ATSC30_DRVIF_VERIFY(is_wrapped == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;

	if (ptp_params->is_first_event) {
		ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
			"ptp_params->is_first_event %s\n",
			ATSC30_BOOL_2_STR(ptp_params->is_first_event));
		*is_wrapped = false;

		return MDRV_ATSC30_RETURN_CODE_OK;
	}

	diff_from_timestamp =
		TIMESTAMP_2_STC(
			stc_frequency,
			ROUNDED_DIFF(
				timestamp_up_bound,
				ptp_params->timestamps[PTP_ADJ_TIMESTAMP_1],
				ptp_params->timestamps[PTP_ADJ_TIMESTAMP_0]));
	if (diff_from_timestamp == 0) {
		ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
			"diff_from_timestamp is zero\n");
		ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
			"Treat it as being wrapped\n");
		*is_wrapped = true;

		return MDRV_ATSC30_RETURN_CODE_OK;
	}
	diff_stc =
		ROUNDED_DIFF(
			stc_up_bound,
			ptp_params->new_stcs[PTP_ADJ_TIMESTAMP_1],
			ptp_params->new_stcs[PTP_ADJ_TIMESTAMP_0]);
	if (diff_stc == 0) {
		ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
			"diff_stc is zero\n");
		ATSC30_DRVIF_LOG_ENHANCED_DEBUG(
			"Treat it as being wrapped\n");
		*is_wrapped = true;


		return MDRV_ATSC30_RETURN_CODE_OK;
	}
	if (diff_stc > diff_from_timestamp)
		q = div_u64(diff_stc, diff_from_timestamp);
	else
		q = div_u64(diff_from_timestamp, diff_stc);

	*is_wrapped = (q >= PTP_WRAP_CONDITION);

	return ret;
}

static int ptp_adjust_stc_event(
	u8 framer_idx,
	struct stc_event *stc_event,
	u64 current_timestamp,
	u64 stc_up_bound,
	u64 stc_frequency)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	struct ptp_params *ptp_params = ptp_get_ptp_params(framer_idx);
	bool is_wrapped = false;

	if (ATSC30_DRVIF_VERIFY(ptp_params == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;
	if (ATSC30_DRVIF_VERIFY(stc_event == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;

	ptp_dump_ptp_params(ptp_params, "before");

	do {
		ret = ptp_preset_ptp_params(
				ptp_params,
				stc_event);
		if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"ptp_preset_ptp_params is failed\n");

			break;
		}

		ret = ptp_check_wrap(
				ptp_params,
				TIMESTAMP_UP_BOUND,
				stc_up_bound,
				stc_frequency,
				&is_wrapped);
		if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"ptp_check_wrap is failed\n");

			break;
		}
		if (is_wrapped) {
			ret = ptp_reset_ptp_params(framer_idx);
			if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
				ATSC30_DRVIF_LOG_ENHANCED_ERROR(
					"ptp_reset_ptp_params is failed\n");

				break;
			}

			ret = ptp_preset_ptp_params(
					ptp_params,
					stc_event);
			if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
				ATSC30_DRVIF_LOG_ENHANCED_ERROR(
					"ptp_preset_ptp_params is failed\n");

				break;
			}
		}

		ret = ptp_adjust_ptp_params(
				ptp_params,
				TIMESTAMP_UP_BOUND,
				current_timestamp,
				stc_up_bound,
				stc_frequency);
		if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"ptp_adjust_ptp_params is failed\n");

			break;
		}
		stc_event->new_stc = ptp_params->new_stcs[PTP_ADJ_TIMESTAMP_LAST];

		ptp_params->is_first_event = false;
	} while (0);

	ptp_dump_ptp_params(ptp_params, "after");

	return ret;
}

static int ptp_handle_stc_value_once(
		u8 framer_idx,
		struct dmx2_filter *dmx2_filter,
		struct stc_event *stc_event,
		struct stc_params *stc_params)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	int tmp_ret = 0;
	u64 current_timestamp = 0;
	struct atsc30_profile profile = {
		.log_level = A30_DBG_LVL_INFO,
		.log_module = ATSC30_LOG_MODULE_DRVIF,
		.log_prefix = __func__,
		.log_us = MDRV_ATSC30_PTP_PROFILE_LOG_US_MIN,
	};

	MDRV_ATSC30_PTP_STC_LOCK(framer_idx);
	atsc30_profile_get_begin_time(&profile);

	do {
		ret = ptp_get_timestamp(
				dmx2_filter,
				stc_event,
				&current_timestamp);
		if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"ptp_get_timestamp is failed\n");

			break;
		}

		ret = ptp_adjust_stc_event(
				framer_idx,
				stc_event,
				current_timestamp,
				stc_params->stc_up_bound,
				stc_params->stc_frequency);
		if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"ptp_adjust_stc_event is failed\n");

			break;
		}

		tmp_ret = mtk_dmx_tm_handle_stc_event(
			dmx2_filter,
			stc_event,
			stc_params);
		if (tmp_ret != 0) {
			ATSC30_DRVIF_LOG_ENHANCED_ERROR(
				"mtk_dmx_tm_handle_stc_event is failed\n");

			ret = MDRV_ATSC30_RETURN_CODE_FAILURE;
			break;
		}
	} while (0);

	ATSC30_PROFILE_GET_END_TIME(&profile);
	MDRV_ATSC30_PTP_STC_UNLOCK(framer_idx);

	return ret;
}

static void ptp_handle_ptp_event(
		ptp_packet_descriptor_t *desc,
		u64 stc_val,
		struct mdrv_atsc30_filter *pos)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	struct dmx2_filter *dmx2_filter = NULL;
	struct stc_event stc_event = {
		.new_stc = stc_val,

		.get_stc_info = mdrv_atsc30_priv_get_stc_info,

		.get_pcr_filter_cfg = ptp_get_pcr_filter_cfg,
		.set_pcr_filter_cfg = ptp_set_pcr_filter_cfg,
	};
	struct stc_params stc_params = {
			.count_to_reset_stc = PCR_RESET_COUNT,
			.count_to_reset_cw = PCR_RESET_COUNT + 1,

			.stc_gap =
				ptp_freq_convert(
					STC_GAP_IN_90KHZ,
					FREQ_90KHZ,
					desc->stc_frequency),
			.stc_up_bound = desc->stc_up_bound,
			.stc_frequency = desc->stc_frequency,

			.diff_to_reset_count =
				ptp_freq_convert(
					STC_DIFF_CONDITION1_IN_90KHZ,
					FREQ_90KHZ,
					desc->stc_frequency),
			.diff_to_increase_count =
				ptp_freq_convert(
					STC_DIFF_CONDITION2_IN_90KHZ,
					FREQ_90KHZ,
					desc->stc_frequency),

			.pll_div_scale_alpha = PLL_DIV_SCALE1,
			.pll_div_scale_beta = PLL_DIV_SCALE2,
	};
	struct mtk_atsc3_dmx_private *priv = NULL;

	if (ATSC30_DRVIF_VERIFY(desc == NULL))
		return;
	if (ATSC30_DRVIF_VERIFY(pos == NULL))
		return;
	dmx2_filter = pos->demux_filter;
	if (ATSC30_DRVIF_VERIFY(dmx2_filter == NULL))
		return;
	if (ATSC30_DRVIF_VERIFY(
		dmx2_filter->params.type != DMX2_FILTER_TYPE_ALP))
		return;
	if (ATSC30_DRVIF_VERIFY(
		dmx2_filter->params.alp.data_type != DMX2_ALP_PRIV_DATA_PTP))
		return;

	priv = pos->demux_filter->parent->priv;
	stc_params.stc_cw = (priv->stc_cw) ? priv->stc_cw : STC_SYNTH_DEFAULT_CW;
	stc_params.stc_bypass_3_2 = priv->stc_bypass_3_2;
	stc_event.timestamp = desc->timestamp;
	ret = ptp_handle_stc_value_once(
			desc->framer_idx,
			dmx2_filter,
			&stc_event,
			&stc_params);
	if (ret != MDRV_ATSC30_RETURN_CODE_OK)
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"ptp_handle_stc_value_once is failed\n");
}

static bool ptp_update_stc_value(
		u64 stc_val,
		ptp_packet_descriptor_t *desc)
{
	int ret = true;
	int tmp_ret = MDRV_ATSC30_RETURN_CODE_OK;
	struct mdrv_atsc30_internal_context *context = NULL;
	struct list_head *head = NULL;
	struct mdrv_atsc30_filter *pos = NULL;

	tmp_ret = ptp_get_context(desc->framer_idx, &context);
	if (tmp_ret != MDRV_ATSC30_RETURN_CODE_OK) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"ptp_get_context(%d, ...) is failed\n",
			desc->framer_idx);

		return false;
	}
	if (!context->inited) {
		ATSC30_DRVIF_LOG_ENHANCED_VERBOS(
			"context of %d is not initialized\n",
			desc->framer_idx);

		return false;
	}
	head = &context->head;

	context->pkt_id_counter++;
	ATSC30_DRVIF_LOG_ENHANCED_FTRACE(
		"context of %d\n",
		context->framer_idx);
	ATSC30_DRVIF_LOG_ENHANCED_FTRACE(
		"stc_val 0x%llx\n",
		stc_val);
	ATSC30_DRVIF_LOG_ENHANCED_FTRACE(
		"pkt_id_counter %d\n",
		context->pkt_id_counter);

	list_for_each_entry(pos, head, list)
		ptp_handle_ptp_event(
			desc,
			stc_val, pos);

	return ret;
}

//-------------------------------------------------------------------------------------------------
//  Public Functions
//-------------------------------------------------------------------------------------------------
int mdrv_atsc30_ptp_init(
		u8 framer_idx)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;
	int tmp_ret = MDRV_ATSC30_RETURN_CODE_OK;

	if (ATSC30_DRVIF_VERIFY(framer_idx >= eATSC30_SUPPORT_FRAMER_NUM))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;

	MDRV_ATSC30_PTP_LOCK(framer_idx);

	ret = ptp_init(framer_idx);
	if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
		tmp_ret = ptp_exit(framer_idx);
		if (tmp_ret != MDRV_ATSC30_RETURN_CODE_OK) {
		// DO nothing
		}
	}

	MDRV_ATSC30_PTP_UNLOCK(framer_idx);

	return ret;
}

int mdrv_atsc30_ptp_exit(
		u8 framer_idx)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;

	if (ATSC30_DRVIF_VERIFY(framer_idx >= eATSC30_SUPPORT_FRAMER_NUM))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;

	MDRV_ATSC30_PTP_LOCK(framer_idx);

	ret = ptp_exit(framer_idx);

	MDRV_ATSC30_PTP_UNLOCK(framer_idx);

	return ret;
}

int mdrv_atsc30_ptp_add_filter(
		u8 framer_idx,
		struct dmx2_filter *demux_filter)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;

	if (ATSC30_DRVIF_VERIFY(framer_idx >= eATSC30_SUPPORT_FRAMER_NUM))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;
	if (ATSC30_DRVIF_VERIFY(demux_filter == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;

	MDRV_ATSC30_PTP_LOCK(framer_idx);

	ret = ptp_add_filter(
			framer_idx,
			demux_filter);

	MDRV_ATSC30_PTP_UNLOCK(framer_idx);

	return ret;
}

int mdrv_atsc30_ptp_del_filter(
		u8 framer_idx,
		struct dmx2_filter *demux_filter)
{
	int ret = MDRV_ATSC30_RETURN_CODE_OK;

	if (ATSC30_DRVIF_VERIFY(framer_idx >= eATSC30_SUPPORT_FRAMER_NUM))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;
	if (ATSC30_DRVIF_VERIFY(demux_filter == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;

	MDRV_ATSC30_PTP_LOCK(framer_idx);

	ret = ptp_del_filter(
			framer_idx,
			demux_filter);

	MDRV_ATSC30_PTP_UNLOCK(framer_idx);

	return ret;
}

bool mdrv_atsc30_ptp_update_stc_value(
		u64 stc_val,
		ptp_packet_descriptor_t *desc)
{
	int ret = true;
#ifdef MDRV_ATSC30_PTP_STAGE_2_TESTING_ENABLE
	static int count = 0;
#endif //#ifdef MDRV_ATSC30_PTP_STAGE_2_TESTING_ENABLE

	if (ATSC30_DRVIF_VERIFY(desc == NULL))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;
	if (ATSC30_DRVIF_VERIFY(desc->framer_idx >= eATSC30_SUPPORT_FRAMER_NUM))
		return MDRV_ATSC30_RETURN_CODE_INVALID_PARAMETER;

	MDRV_ATSC30_PTP_LOCK(desc->framer_idx);

	ATSC30_DRVIF_LOG_ENHANCED_FTRACE(
		"timestamp 0x%x\n",
		desc->timestamp);

#ifdef MDRV_ATSC30_PTP_STAGE_2_TESTING_ENABLE
	if ((count % MDRV_ATSC30_PTP_STAGE_2_PTP_TESTING_PACKET_NO) != 0) {
		// PVR's time domain is always 90KHZ
		desc->timestamp +=
			MDRV_ATSC30_PTP_STAGE_2_PTP_TESTING_PACKET_TIMESTAMP_DIFF
				* (count % MDRV_ATSC30_PTP_STAGE_2_PTP_TESTING_PACKET_NO);

		ATSC30_DRVIF_LOG_ENHANCED_FTRACE(
			"desc->stc_frequency 0x%llx desc->timestamp 0x%llx stc_val 0x%llx\n",
			desc->stc_frequency,
			desc->timestamp,
			stc_val);
	}
	count++;
#endif //#ifdef MDRV_ATSC30_PTP_STAGE_2_TESTING_ENABLE

	ret = ptp_update_stc_value(
				stc_val,
				desc);

	MDRV_ATSC30_PTP_UNLOCK(desc->framer_idx);

	return ret;
}
