// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * drivers/amlogic/media/deinterlace/film_mode_fmw/flm_mod_xx.c
 *
 * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include "film_vof_soft.h"

/* dif02 < (size >> sft) => static */
int flm2224_stl_sft = 7; /*10*/
module_param(flm2224_stl_sft, int, 0644);
MODULE_PARM_DESC(flm2224_stl_sft, "flm2224_stl_sft");

int aMax02[15]; /* maximum 4 */
int aXId02[15];
int aMin02[15]; /* minimum 4 */
int aNId02[15];
int aXMI02[15];

int GetMaxNIdx(int *nMax4, int *nXId4, int N, int *nQt01, int nLen)
{
	int nT1 = 0;
	int nT3 = 0;
	int nT4 = 0;
	int nTmp = 0;

	for (nT1 = 0; nT1 <	N; nT1++) {
		nMax4[nT1] = 0;
		nXId4[nT1] = 0;
	}

	for (nT1 = 0; nT1 <	nLen; nT1++) {
		nTmp = nQt01[nT1];

		/* maximum */
		for (nT3 = 0; nT3 < N; nT3++) {
			if (nTmp > nMax4[nT3]) {
				for (nT4 = 3; nT4 >= nT3+1; nT4--) {
					nMax4[nT4] = nMax4[nT4-1];
					nXId4[nT4] = nXId4[nT4-1];
				}
				nMax4[nT3] = nTmp;
				nXId4[nT3] = nT1;
				break;
			}
		}
	}
	return 0;
}

int GetMinNIdx(int *nMax4, int *nXId4, int N, int *nQt01, int nLen)
{
	int nT1 = 0;
	int nT3 = 0;
	int nT4 = 0;
	int nTmp = 0;

	for (nT1 = 0; nT1 <	N; nT1++) {
		nMax4[nT1] = 17;
		nXId4[nT1] = 0;
	}

	for (nT1 = 0; nT1 <	nLen; nT1++) {
		nTmp = nQt01[nT1];

		/* minimum */
		for (nT3 = 0; nT3 < N; nT3++) {
			if (nTmp < nMax4[nT3]) {
				for (nT4 = 3; nT4 >= nT3+1; nT4--) {
					nMax4[nT4] = nMax4[nT4-1];
					nXId4[nT4] = nXId4[nT4-1];
				}
				nMax4[nT3] = nTmp;
				nXId4[nT3] = nT1;
				break;
			}
		}
	}
	return 0;
}

/* 15: 8-7 */
/* 12: 3-2-3-2-2 */
/* 10: 6-4 */
/* 10: 5-5 */
/* 10: 2-2-2-4 */
/* 10: 2-3-3-2 */
/* 10: 3-2-3-2 */
/* pulldown pattern number */
int	FlmModsDet(struct sFlmDatSt *pRDat, int nDif01, int nDif02, int flm2224_cen)
{
	int iWidth  = pRDat->iWidth;
	int iHeight = pRDat->iHeight;
	int iSIZE = (iWidth * iHeight);
	int iMxDif  = (iSIZE >> 6);
	int nPrtLog[PDXX_PT_NUM] = {87, 32322, 64, 55, 2224, 2332, 3232};

	/* HISDETNUM hist */
	UINT8 *pModXx = pRDat->pModXx;
	UINT8 *pFlgXx = pRDat->pFlgXx; /* pre-1, nxt-0 */
	UINT8 *pLvlXx = pRDat->pLvlXx;  /* mode level */

	static unsigned int sModFlg01[PDXX_PT_NUM]; /* flags */
	static unsigned int sModFlg02[PDXX_PT_NUM]; /* flags */
	static int nModCnt[PDXX_PT_NUM]; /* mode counter */

	unsigned int tModFlg01[PDXX_PT_NUM]; /* current flags */
	unsigned int tModFlg02[PDXX_PT_NUM]; /* current flags */

	int nMxMn[PDXX_PT_NUM][2] = { {2, 4}, {-2, -2}, {2, 4},
	{2, 4}, {4, -2}, {4, -2}, {4, -2} };

	int nModLvl[PDXX_PT_NUM] = {0, 0, 0, 0, 0, 0, 0}; /* mode level */
	int nQt01[15];
	int nQt02[15];

	int aMax01[15]; /* maximum 4 */
	int aXId01[15];
	int aMin01[15]; /* minimum 4 */
	int aNId01[15];
	int aXMI01[15];

	int	nT0	= 0;
	int	nT1	= 0;
	int	nT2	= 0;
	int	nT3	= 0;
	int	nT4	= 0;

	int	tT0	= 0;
	int	tT1	= 0;
	int	tT2	= 0;
	int	tT3	= 0;

	unsigned int uT01 = 0;
	unsigned int uT02 = 0;
	unsigned int uT03 = 0;

	int	nS01[PDXX_PT_NUM]	= {0, 0, 0,	0, 0, 0, 0};
	int	nS02[PDXX_PT_NUM]	= {0, 0, 0,	0, 0, 0, 0};
	int	nStp[PDXX_PT_NUM]	= {15, 12, 10, 10, 10, 10};

	static int pDif01[30];
	static int pDif02[30];

	int nLen1 = 0;
	int nLen2 = 0;

	int	nMin01 = 0;
	int	nMax01 = 0;
	int	nMin02 = 0;
	int	nMax02 = 0;
	int tModLvl = 0;
	int ndif021 = 0;
	int ndif022 = 0;
	int idx = 0;

	iMxDif = (iMxDif >> 4) + 1;

	prt_flg = ((pr_pd >> 4) & 0x1);
	if (prt_flg)
		sprintf(debug_str, "#DbgXx:\n");

	for (nT0 = 1; nT0 < HISDETNUM; nT0++) {
		pFlgXx[nT0 - 1] = pFlgXx[nT0];
		pLvlXx[nT0 - 1] = pLvlXx[nT0];
		pModXx[nT0 - 1] = pModXx[nT0];
	}

	for	(nT0 = 0; nT0 <	29;	nT0++) {
		pDif01[nT0]	= pDif01[nT0 + 1];
		pDif02[nT0]	= pDif02[nT0 + 1];
	}
	pDif01[29] = (nDif01 >> 6);
	pDif02[29] = (nDif02 >> 6);

	for	(nT0 = 0; nT0 <	3; nT0++) {
		nT2	= nStp[nT0];

		nT3	= pDif01[29];
		nT4	= pDif02[29];

		nMin01 = nT3;
		nMax01 = nT3;
		nMin02 = nT4;
		nMax02 = nT4;

		nT3 = pDif01[29] - pDif01[29 - nT2];
		nT4 = pDif02[29] - pDif02[29 - nT2];
		if (nT3 < 0)
			nT3 = -nT3;
		if (nT4 < 0)
			nT4 = -nT4;
		nS01[nT0] = nT3;
		nS02[nT0] = nT4;

		/* nS01, nS02: sum of difference */
		for	(nT1 = 1; nT1 <	nT2; nT1++)	{
			nT3	= pDif01[29	- nT1];
			nT4	= pDif02[29	- nT1];

			if (nT3 > nMax01)
				nMax01 = nT3;
			if (nT3 < nMin01)
				nMin01 = nT3;

			if (nT4 > nMax02)
				nMax02 = nT4;
			if (nT4 < nMin02)
				nMin02 = nT4;

			/* diff max */
			nT3 = pDif01[29	- nT1] - pDif01[29 - nT1 - nT2];
			nT4	= pDif02[29	- nT1] - pDif02[29 - nT1 - nT2];
			if (nT3 < 0)
				nT3 = -nT3;
			if (nT4 < 0)
				nT4 = -nT4;
			if (nT3 > nS01[nT0])
				nS01[nT0] = nT3;
			if (nT4 > nS02[nT0])
				nS02[nT0] = nT4;
		}

		for (nT1 = 0; nT1 <	nT2; nT1++)	{
			nT3 = pDif01[29 - nT1] - nMin01;
			nT4 = nMax01 - nMin01 + 32;
			nT3 = (16 * nT3) + (nT4 / 2);
			nQt01[nT1] = (nT3 / nT4);

			nT3 = pDif02[29 - nT1] - nMin02;
			nT4 = nMax02 - nMin02 + 32;
			nT3 = (16 * nT3) + (nT4 / 2);
			nQt02[nT1] = (nT3 / nT4);
		}

		if (nT0 == 2)
			tT2 = PDXX_PT_NUM - 2;
		else
			tT2 = 1;

		for (tT1 = 0; tT1 < tT2; tT1++) {
			tT0	= nT0 + tT1;

			tModLvl = ((nModCnt[tT0] + 2) >> 2);
			if (tModLvl > 64)
				tModLvl = 64;

			if (nS01[nT0] > nMax01)
				nT3 = 8;
			else {
				nT3 = (nS01[nT0] << 3);
				nT3 = nT3 + (nMax01 >> 1);
				nT3 = nT3 / (nMax01 + 1);
				if (nT3 > 8)
					nT3 = 8;
			}
			tModLvl -= nT3;

			if (nS02[nT0] > nMax02)
				nT4 = 8;
			else {
				nT4 = (nS02[nT0] << 3);
				nT4 = nT4 + (nMax02 >> 1);
				nT4 = nT4 / (nMax02 + 1);
				if (nT4 > 8)
					nT4 = 8;
			}
			tModLvl -= nT4;

			if (nMxMn[tT0][0] > 0) {
				nLen1 = nMxMn[tT0][0];
				GetMaxNIdx(aMax01, aXId01, nLen1, nQt01, nT2);

				nT4 = 0;
				for (tT3 = 0; tT3 < nLen1; tT3++) {
					aXMI01[tT3] = aXId01[tT3];
					nT4 += (16 - aMax01[tT3]);
				}
				nT4 /= nLen1;
				tModLvl -= nT4;
			} else {
				nLen1 = -nMxMn[tT0][0];
				GetMinNIdx(aMin01, aNId01, nLen1, nQt01, nT2);

				nT4 = 0;
				for (tT3 = 0; tT3 < nLen1; tT3++) {
					aXMI01[tT3] = aNId01[tT3];
					nT4 += aMin01[tT3];
				}
				nT4 /= nLen1;
				tModLvl -= nT4;
			}

			if (nMxMn[tT0][1] > 0) {
				nLen2 = nMxMn[tT0][1];
				GetMaxNIdx(aMax02, aXId02, nLen2, nQt02, nT2);

				nT4 = 0;
				for (tT3 = 0; tT3 < nLen2; tT3++) {
					aXMI02[tT3] = aXId02[tT3];
					nT4 += (16 - aMax02[tT3]);
				}
				nT4 /= nLen2;
				tModLvl -= nT4;
			} else {
				nLen2 = -nMxMn[tT0][1];
				GetMinNIdx(aMin02, aNId02, nLen2, nQt02, nT2);

				nT3 = 0;
				nT4 = 0;
				for (tT3 = 0; tT3 < nLen2; tT3++) {
					aXMI02[tT3] = aNId02[tT3];
					nT4 += aMin02[tT3];

					nT3 = pDif02[29 - aNId02[tT3]];
					if (nT3 > iMxDif)
						nT4 += 8;
					else {
						nT3 = (nT3 << 3) +
							(iMxDif >> 1);
						nT3 /= (iMxDif + 1);
						nT4 += nT3;
					}
				}
				nT4 /= (2 * nLen2);
				tModLvl -= nT4;
			}

			tModFlg01[tT0] = 0;
			for (tT3 = 0; tT3 < nLen1; tT3++)
				tModFlg01[tT0] |= (1 << aXMI01[tT3]);

			tModFlg02[tT0] = 0;
			for (tT3 = 0; tT3 < nLen2; tT3++)
				tModFlg02[tT0] |= (1 << aXMI02[tT3]);

			uT03 = (1 << nT2) - 1;
			tModFlg01[tT0] &= uT03;
			tModFlg02[tT0] &= uT03;

			uT01 = (sModFlg01[tT0] << 1);
			uT01 |= (uT01 >> nT2);
			uT01 &= uT03;

			uT02 = (sModFlg02[tT0] << 1);
			uT02 |= (uT02 >> nT2);
			uT02 &= uT03;

			/* minimum check */
			nLen2 = 0;
			if (tT0 == 0) { /* 8-7 */
				nLen2 = 11;
			} else if (tT0 == 2) { /* 6-4 */
				nLen2 = 6;
			} else if (tT0 == 3) { /* 5-5 */
				nLen2 = 6;
			}

			if (nLen2 > 0) {
				GetMinNIdx(aMin02, aNId02, nLen2, nQt02, nT2);
				nT4 = 0;
				for (tT3 = 0; tT3 < nLen2; tT3++) {
					nT3 = pDif02[29 - aNId02[tT3]];
					if (nT3 > iMxDif)
						nT4 += 8;
					else {
						nT3 = (nT3 << 3)
							+ (iMxDif >> 1);
						nT3 /= (iMxDif + 1);
						nT4 += nT3;
					}
				}
				nT4 /= nLen2;
				tModLvl -= nT4;
			}

			if (nMin02 > iMxDif) {
				tModLvl -= 16;
					nModCnt[tT0] = 0;
			} else {
				nT4 = (nMin02 << 4);
				nT4 = nT4 / iMxDif;
				tModLvl -= nT4;
			}

			/*
			 * nT4 = (nMin02 << 6) + (iMxDif >> 1);
			 * nT4 = nT4 / iMxDif;
			 * if (nModCnt[tT0] > nT4)
			 *	nModCnt[tT0] -= nT4;
			 * else
			 *	nModCnt[tT0] = 0;
			 */

			/* Distance between maximum-2 dif01*/
			if (aXMI01[1] > aXMI01[0])
				nT3 = aXMI01[1] - aXMI01[0];
			else
				nT3 = aXMI01[0] - aXMI01[1];

			/* Distance between minimum-2 dif02 */
			if (aXMI02[1] > aXMI02[0])
				nT4 = aXMI02[1] - aXMI02[0];
			else
				nT4 = aXMI02[0] - aXMI02[1];

			if ((uT01 == tModFlg01[tT0]) &&
				(uT02 == tModFlg02[tT0]) &&
				(nT3 > 0) && (uT01 > 0) &&
				(uT02 > 0)) {
				if (tT0 == 0) {
					if (nT3 == 7 || nT3 == 8) {
						nModCnt[tT0] += 1;
						tModLvl += 2;
					} else
						nModCnt[tT0] = 0;
				} else if (tT0 == 2) {
					if (nT3 == 4 || nT3 == 6) {
						nModCnt[tT0] += 1;
						tModLvl += 2;
					} else
						nModCnt[tT0] = 0;
				} else if (tT0 == 3) {
					if (nT3 == 5) {
						nModCnt[tT0] += 1;
						tModLvl += 2;
					} else
						nModCnt[tT0] = 0;
				}
			}

			if ((uT02 == tModFlg02[tT0]) &&
				(nT4 > 0) && (uT02 > 0)) {
				if ((uT01 == tModFlg01[tT0]) && (uT01 > 0))
					tModLvl += 1;

				if (tT0 == 1) {
					if (nT4 == 5 || nT4 == 7) {
						nModCnt[tT0] += 1;
						tModLvl += 1;
					} else
						nModCnt[tT0] = 0;
				}  else if (tT0 == 4) {
					if (nT4 == 1 || nT4 == 9) {
						nModCnt[tT0] += 1;
						tModLvl += 1;
					} else
						nModCnt[tT0] = 0;
				} else if (tT0 == 5) {
					if (nT4 == 3 || nT4 == 7) {
						nModCnt[tT0] += 1;
						tModLvl += 1;
					} else
						nModCnt[tT0] = 0;
				} else if (tT0 == 6) {
					if (nT4 == 5) {
						nModCnt[tT0] += 1;
						tModLvl += 1;
					} else
						nModCnt[tT0] = 0;
				}
			}

			if (nModCnt[tT0] > 254)
				nModCnt[tT0] = 254;

			if (tModLvl < 0)
				tModLvl = 0;

			nModLvl[tT0] = tModLvl;

			sModFlg01[tT0] = tModFlg01[tT0];
			sModFlg02[tT0] = tModFlg02[tT0];

			/*bias static similar to pulldown 32322, max02 must bigger than min02*/
			/*suggest from vlsi yanling*/
			if (nMax02 < (1 << 11))
				nModLvl[tT0] = 0;
			if (tT0 == 4) {
				if (nT4 == 9) {
					ndif021 = pDif02[29 - 1];
					ndif022 = pDif02[29 - 8];
				} else {
					idx = (min(aXMI02[1], aXMI02[0]) - 1 + 10) % 10;
					ndif021 = pDif02[29 - idx];
					idx = (max(aXMI02[1], aXMI02[0]) + 1) % 10;
					ndif022 = pDif02[29 - idx];
				}
			}
		} /* 2-3-4-5*/
	}

	tModLvl = nModLvl[0];
	nT1 = 0;
	for	(nT0 = 1; nT0 <	PDXX_PT_NUM; nT0++) {
		if (nModLvl[nT0] > tModLvl) {
			tModLvl = nModLvl[nT0];
			nT1 = nT0;
		}
	}

	if (prt_flg)
		sprintf(debug_str + strlen(debug_str),
			"nMax02(10)=%4d < (%4d)\n",
			nMax02, (iSIZE >> flm2224_stl_sft));

	if ((nT1 == 4) &&
		(nMax02 <= (iSIZE >> flm2224_stl_sft)))
		nModCnt[nT1] = 0;
	if (nT1 == 4 && flm2224_cen) {
		if (abs(ndif021 - ndif022) > (iSIZE >> 6) || (max(ndif021, ndif022) / (max(1, min(ndif021, ndif022))) > 10) ||
			(max(pDif02[29 - aXMI02[0]], pDif02[29 - aXMI02[1]]) / max(1, min(pDif02[29 - aXMI02[0]], pDif02[29 - aXMI02[1]])) > 10 && min(pDif02[29 - aXMI02[0]], pDif02[29 - aXMI02[1]]) > 10))
			nModCnt[nT1] = 0;
	}

	pModXx[HISDETNUM - 1] = nT1;
	pLvlXx[HISDETNUM - 1] = tModLvl;
	pFlgXx[HISDETNUM - 1] = (pDif01[29] < pDif01[28]);

	/* recheck */
	if ((pFlgXx[HISDETNUM - 2] == 0) &&
		(pDif01[29] > pDif01[28]))
		pFlgXx[HISDETNUM - 2] = 1;

	if (prt_flg && tModLvl > 0) {
		sprintf(debug_str + strlen(debug_str),
			"#FM%5d detected ct(%3d) lvl(%2d)\n",
			nPrtLog[nT1], nModCnt[nT1], nModLvl[nT1]);
		if (pDif01[29] < pDif01[28])
			sprintf(debug_str + strlen(debug_str),
				"#Pre: A<-A\n");
		else
			sprintf(debug_str + strlen(debug_str),
				"#Nxt: A B->\n");
	}

	if (prt_flg) {
		sprintf(debug_str + strlen(debug_str),
			"Mod=%5d, Flg=%d, Lvl=%3d\n",
			nT1, pFlgXx[HISDETNUM - 1],
			tModLvl);
		pr_info("%s", debug_str);
	}
	if (pr_pd & 0x1)
		pr_info("ndif021 = %d,ndif022 = %d", ndif021, ndif022);

	return nT1;
}
