/* SPDX-License-Identifier: LGPL-2.1-only OR BSD-3-Clause */
/******************************************************************************
 *
 * This file is provided under a dual license.  When you use or
 * distribute this software, you may choose to be licensed under
 * version 2.1 of the GNU Lesser General Public License
 * ("LGPLv2.1 License") or BSD License.
 *
 * LGPLv2.1 License
 *
 * Copyright(C) 2019 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2.1 of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * 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 https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html for more
 * details.
 *
 * BSD LICENSE
 *
 * Copyright(C) 2019 MediaTek Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *****************************************************************************/

/*-----------------------------------------------------------------------------
 * $RCSfile: io_mtvfe.c,v $
 * $Revision: #1 $
 *---------------------------------------------------------------------------*/

/** @file io_mtvfe.c
 */


//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------

#include "mtvfe.h"
#include <linux/mtal_ioctl.h>
#include "drv_adcset.h"
#include "drv_autocolor.h"
#include "nptv_if.h"
#include "x_linux.h"
#include <linux/wait.h>
#include "hw_vdoin.h"
#include "hw_hdtv.h"
#include "api_notify.h"
#include <linux/delay.h>

#define  EEPROM_SHADOW_ISSUPPORTED 0
//-----------------------------------------------------------------------------
// Configurations
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Constant definitions
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Type definitions
//-----------------------------------------------------------------------------
#define USR_SPACE_ACCESS_VALIDATE_2ARG(arg)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   sizeof(MTAL_IOCTL_2ARG_T)))              \
    {                                                       \
        printk("mtal_ioctl_mtvfe argument error\n");     \
        return MTR_ERR_INV;                                          \
    }                                                           

#define COPY_FROM_USER_2ARG(ArgUsr, ArgKernel)              \
    if (copy_from_user(&ArgKernel, (void __user *)ArgUsr,   \
                       sizeof(MTAL_IOCTL_2ARG_T)))          \
    {                                                       \
        printk("mtal_ioctl_mtvfe argument error\n");      \
        return MTR_ERR_INV;                                          \
    }  

#define USR_SPACE_ACCESS_VALIDATE_ARG(arg,type)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   sizeof(type)))              \
    {                                                       \
        printk("mtal_ioctl_pal argument error\n");     \
        return MTR_ERR_INV;                                          \
    }                                                           

#define COPY_TO_USER_ARG(ArgUsr, ArgKernel,type)              \
    if (copy_to_user((void __user *)ArgUsr, &ArgKernel,   \
                       sizeof(type)))          \
    {                                                       \
        printk("mtal_ioctl_pal argument error\n");      \
        return MTR_ERR_INV;                                          \
    }
//-----------------------------------------------------------------------------
// Macro definitions
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Static variables
//-----------------------------------------------------------------------------

//static DECLARE_WAIT_QUEUE_HEAD(mtvfel_autocolor_wq);
static BOOL MTVFE_AutoColorResult;
static BOOL MTVFE_AutoColorDone;

static void MTVFE_AutoColorNotify(UINT16 ui2_id, BOOL fgSuccess)
{
	MTVFE_AutoColorResult=fgSuccess;
	MTVFE_AutoColorDone=1;
//	wake_up_interruptible(&mtvfel_autocolor_wq);
}

//-----------------------------------------------------------------------------
// Public functions
//-----------------------------------------------------------------------------

/**Reset ADC module to default.
*@param  void
*@retval void
*/
static MT_RESULT_T _MTVFE_ADCSettingReset(void)
{
    UINT8 i;
    MTVFE_PRINT("- void\n");

    /* skip reset TV adc currently*/
    // vDrvTVADCDefaultSetting();

     /* this cause screen crash */
    //vDrvHDTVADCDefaultSetting(); 
    for(i=HDADC1_CHANNEL_B_Y;i<=HDADC3_CHANNEL_R_PR;i++)
    {
    	bDrvSetHDADCOffset(i,0x80);
    	bDrvSetHDADCGain(i,0);
    }
    return MTR_OK;
}

/** Do ADC calibration(autocolor)
*@param  void
*@retval void
*/
static MT_RESULT_T _MTVFE_ADCCalibration(void)
{
    INT32 i;
    MTVFE_PRINT("- void\n");
    //set notify function
    NPTV_SetVgaAutocolorNfy(MTVFE_AutoColorNotify);

    //issue auto color
    vDrvIntAutoColorStart();
    MTVFE_AutoColorDone=0;

    /*
	if(interruptible_sleep_on_timeout(&mtvfel_autocolor_wq,15*HZ)==0)
	{
		return MTR_NOT_OK;
	}
     */
     for(i=0;i<750;i++)
     {
     	msleep(50);
     	if(MTVFE_AutoColorDone) break;
     }

	return MTVFE_AutoColorResult?MTR_OK:MTR_NOT_OK;
}


/**Read ADC_Offset
*@param eChannelSel	HDADC Channel
*@retval Offset Value
*/
static MT_RESULT_T _MTVFE_GetHDADCOffset(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T rArg;
    MTVFE_HDADC_CHANNEL_T eChannelSel;
    UINT8 u1AdcOffset;
    MT_RESULT_T eRet=MTR_OK;
    
    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg,rArg);

    eChannelSel = (MTVFE_HDADC_CHANNEL_T)rArg.ai4Arg[0];
	
    switch(eChannelSel)
    {
    	case HDADC1_CHANNEL_B_Y:
    		u1AdcOffset=vCust_B_Offset_Read();
    		break;
    	case HDADC2_CHANNEL_G_PB:
  		u1AdcOffset=vCust_G_Offset_Read();
    		break;
    	case HDADC3_CHANNEL_R_PR:
    		u1AdcOffset=vCust_R_Offset_Read();
    		break;
    	default:
    		u1AdcOffset=0;
    		eRet=MTR_ERR_INV;
    		break;
    }

    MTVFE_PRINT("- eChannelSel = %d, ADCOffset = %d\n",
		(int)eChannelSel,(int)u1AdcOffset);    
    rArg.ai4Arg[0] = (INT32)u1AdcOffset;
    COPY_TO_USER_ARG(arg, rArg, MTAL_IOCTL_2ARG_T);
    return eRet;
}

/**Read ADC_Gain
*@param eChannelSel	HDADC Channel
*@retval Gain Value
*/
static MT_RESULT_T _MTVFE_GetHDADCGain(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T rArg;
    MTVFE_HDADC_CHANNEL_T eChannelSel;
    UINT8 u1AdcGain;
    MT_RESULT_T eRet=MTR_OK;
	
    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg,rArg);
	
    eChannelSel = (MTVFE_HDADC_CHANNEL_T)rArg.ai4Arg[0];
	
    switch(eChannelSel)
    {
    	case HDADC1_CHANNEL_B_Y:
    		u1AdcGain=vCust_B_Gain_Read();
    		break;
    	case HDADC2_CHANNEL_G_PB:
		u1AdcGain=vCust_G_Gain_Read();
    		break;
    	case HDADC3_CHANNEL_R_PR:
    		u1AdcGain=vCust_R_Gain_Read();
    		break;
    	default:
    		u1AdcGain=0;
    		eRet=MTR_ERR_INV;
    		break;
    }	

    MTVFE_PRINT("- eChannelSel = %d, ADCGain = %d\n",
		(int)eChannelSel,(int)u1AdcGain);    
    rArg.ai4Arg[0] = (INT32)u1AdcGain;
    COPY_TO_USER_ARG(arg, rArg, MTAL_IOCTL_2ARG_T);
    return eRet;
}


/**Write ADC_Offset
*@param eChannelSel	HDADC Channel
*@param u2Offset		Offset Value
*@retval void
*/
static MT_RESULT_T _MTVFE_SetHDADCOffset(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T rArg;
    MTVFE_HDADC_CHANNEL_T eChannelSel;
    UINT16 u2Offset;

    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg,rArg);

    eChannelSel = (MTVFE_HDADC_CHANNEL_T)rArg.ai4Arg[0];
    u2Offset = (UINT16)rArg.ai4Arg[1];

    MTVFE_PRINT("- eChannelSel = %d, u2Offset = %d\n",
		(int)eChannelSel, (int)u2Offset);

    u2Offset &= 0xff;// limit 8bit

    switch(eChannelSel)
    {
    	case HDADC1_CHANNEL_B_Y:
    		bDrvSetHDADCOffset(0,u2Offset);
    		vCust_B_Offset_Update(u2Offset);
    		break;
    	case HDADC2_CHANNEL_G_PB:
    		bDrvSetHDADCOffset(1,u2Offset);
		vCust_G_Offset_Update(u2Offset);
    		break;
    	case HDADC3_CHANNEL_R_PR:
	    	bDrvSetHDADCOffset(2,u2Offset);
    		vCust_R_Offset_Update(u2Offset);
    		break;
    	default:
    		return MTR_ERR_INV;
    }		
    vCust_Gain_OFFSet_Update();

    return MTR_OK;
}

/**Write ADC_Gain
*@param eChannelSel	HDADC Channel
*@param u2Gain		Gain Value
*@retval void
*/
static MT_RESULT_T _MTVFE_SetHDADCGain(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T rArg;
    MTVFE_HDADC_CHANNEL_T eChannelSel=HDADC1_CHANNEL_B_Y;
    UINT16 u2Gain=0;
	
    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg,rArg);

    MTVFE_PRINT("- eChannelSel = %d, u2Gain = %d\n",
		(int)eChannelSel,
		(int)u2Gain);
	
    eChannelSel = (MTVFE_HDADC_CHANNEL_T)rArg.ai4Arg[0];
    u2Gain = (UINT16)rArg.ai4Arg[1];

    u2Gain &= 0xff;// limit 8 bit
	
    switch(eChannelSel)
    {
    	case HDADC1_CHANNEL_B_Y:
    		bDrvSetHDADCGain(0,u2Gain);
    		vCust_B_Gain_Update(u2Gain);
    		break;
    	case HDADC2_CHANNEL_G_PB:
		bDrvSetHDADCGain(1,u2Gain);
    		vCust_G_Gain_Update(u2Gain);
    		break;
    	case HDADC3_CHANNEL_R_PR:
	    	bDrvSetHDADCGain(2,u2Gain);
    		vCust_R_Gain_Update(u2Gain);
    		break;
    	default:
    		return MTR_ERR_INV;
    }	


    return MTR_OK;
}

/**Get maximum Value in a frame
*@param eChannelSel	HDADC Channel
*@retval Max value
*/
static MT_RESULT_T _MTVFE_GetADCMaxValue(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T rArg;
    MTVFE_HDADC_CHANNEL_T eChannelSel;
    UINT16 u2AdcMaxVal;
    MT_RESULT_T eRet=MTR_OK;
    
    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg,rArg);
	
    eChannelSel = (MTVFE_HDADC_CHANNEL_T)rArg.ai4Arg[0];
	
    switch(eChannelSel)
    {
    	case HDADC1_CHANNEL_B_Y:
		u2AdcMaxVal=bDrvOnChipGetADCMaxValueRGBYPBPR(2);
    		break;
    	case HDADC2_CHANNEL_G_PB:
		u2AdcMaxVal=bDrvOnChipGetADCMaxValueRGBYPBPR(1);
    		break;
    	case HDADC3_CHANNEL_R_PR:
    		u2AdcMaxVal=bDrvOnChipGetADCMaxValueRGBYPBPR(0);
    		break;
    	default:
    		u2AdcMaxVal=0;
    		eRet=MTR_ERR_INV;
    		break;
    }

    MTVFE_PRINT("- eChannelSel = %d, ADCMaxValue = %d\n",
		(int)eChannelSel,
		(int)u2AdcMaxVal);
	
    rArg.ai4Arg[0] = (INT32)u2AdcMaxVal;
    COPY_TO_USER_ARG(arg, rArg, MTAL_IOCTL_2ARG_T);
    return eRet;
}

/**Get balnking Value
*@param eChannelSel	HDADC Channel
*@retval Blank value
*/
static MT_RESULT_T _MTVFE_GetBlankVal(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T rArg;
    MTVFE_HDADC_CHANNEL_T eChannelSel;
    UINT16 u2AdcBlankVal;
    MT_RESULT_T eRet=MTR_OK;
    
    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg,rArg);
	
    eChannelSel = (MTVFE_HDADC_CHANNEL_T)rArg.ai4Arg[0];
	
    //add driver implementation code here	
    vRegWriteFldAlign(HDTV_03, AS_BLANK_ALWAYS, HDTV_BLAK_SET);
    msleep(1);
    switch(eChannelSel)
    {
    	case HDADC1_CHANNEL_B_Y:
		u2AdcBlankVal=bDrvGetBlankVal(2);
    		break;
    	case HDADC2_CHANNEL_G_PB:
		u2AdcBlankVal=bDrvGetBlankVal(1);
    		break;
    	case HDADC3_CHANNEL_R_PR:
    		u2AdcBlankVal=bDrvGetBlankVal(0);
    		break;
    	default:
    		u2AdcBlankVal=0;
    		eRet=MTR_ERR_INV;
    		break;
    }
    vRegWriteFldAlign(HDTV_03, AS_BLANK_RESET, HDTV_BLAK_SET);
    MTVFE_PRINT("- eChannelSel = %d, BlankVal = %d\n",
		(int)eChannelSel,
		(int)u2AdcBlankVal);    
    rArg.ai4Arg[0] = (INT32)u2AdcBlankVal;
    COPY_TO_USER_ARG(arg, rArg, MTAL_IOCTL_2ARG_T);
    return eRet;
}


static MT_RESULT_T _MTVFE_SetSlicerLevel(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T rArg;
    UINT8 bVTL;
    UINT8 bVTH;


    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg,rArg);

    bVTL = (UINT8)rArg.ai4Arg[0];
    bVTH = (UINT8)rArg.ai4Arg[1];

    MTVFE_PRINT("- bVTL = 0x%x, bVTH = 0x%d\n",bVTL,bVTH);
    MTAL_SOGY_VTL_Set(bVTL);
    MTAL_SOGY_VTH_Set(bVTH);
    return MTR_OK;
}

static MT_RESULT_T _MTVFE_GetGainStorage(unsigned long arg)
{
    MTVDECEX_SRC_T eType;
    UINT32 *pu4Value;
    UINT32  u4Value;
    MTAL_IOCTL_2ARG_T    rArg;
    UINT8 bSrcType=0xFF;

    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg, rArg);

    eType = (MTVDECEX_SRC_T )rArg.ai4Arg[0];
    pu4Value = (UINT32 *)rArg.ai4Arg[1];

    switch(eType)
    {
        case MTVDECEX_SRC_COMP1:
        case MTVDECEX_SRC_COMP2:
        case MTVDECEX_SRC_COMP3:
        case MTVDECEX_SRC_COMP4:
            bSrcType = 0;
            u4Value = vCust_Current_Use_ADC_Read(bSrcType);
            break;
        case MTVDECEX_SRC_PC1:
            bSrcType = 1;
            u4Value = vCust_Current_Use_ADC_Read(bSrcType);
            break;
        case MTVDECEX_SRC_SCART1:
        case MTVDECEX_SRC_SCART2:
        case MTVDECEX_SRC_SCART3:
        case MTVDECEX_SRC_SCART4:
            bSrcType = 2;
            u4Value = vCust_Current_Use_ADC_Read(bSrcType);
            break;
        default:
            bSrcType = 0xFF;
            u4Value = 0;
            break;
    }    
    
    USR_SPACE_ACCESS_VALIDATE_ARG(pu4Value,UINT32);
    COPY_TO_USER_ARG(pu4Value,u4Value,UINT32);

    MTVDECEX_PRINT(" - eType = %d, u4Value = %d\n",
		(int)eType,
		(int)u4Value);
    if(bSrcType==0xFF)
        return MTR_NOT_OK;
    else
        return MTR_OK;
}

static MT_RESULT_T _MTVFE_EBGainOverRule(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T    rArg;
    UINT8 bOnOff;

    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg, rArg);

    bOnOff = (UINT8)rArg.ai4Arg[0];

    if(bOnOff)
    	vCust_EBGain_OverRule_Update(0x00);//use OTP
    else
	vCust_EBGain_OverRule_Update(0xAA);//use external ADC
    return MTR_OK;

}
static MT_RESULT_T _MTVFE_Embedded_Gain(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T    rArg;
    UINT8 Type;

    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg, rArg);

    Type = (UINT8)rArg.ai4Arg[0];

    	vCust_Embedded_Gain(Type);//0:YPbPr  1:VGA  2:SCART 
    return MTR_OK;

}
static MT_RESULT_T _MTVFE_GET_Embedded_Gain(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T    rArg;
    UINT8 Type,Ch,Gain;

    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg, rArg);

    Type = (UINT8)rArg.ai4Arg[0];
	Ch = (UINT8)rArg.ai4Arg[1];
	
    Gain=vCust_Get_Embedded_Gain(Type,Ch);//0:YPbPr  1:VGA  2:SCART 
   
	MTVFE_PRINT("- Type = %d,  ch %d EBGain = %d\n",
			(int)Type,(int)Ch,(int)Gain);	 
	rArg.ai4Arg[0] = (INT32)Gain;
	COPY_TO_USER_ARG(arg, rArg, MTAL_IOCTL_2ARG_T);
	return MTR_OK;

}


static MT_RESULT_T _MTVFE_SET_AllHDADCPow(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T    rArg;
    UINT8 bPow;

    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg, rArg);

    bPow = (UINT8)rArg.ai4Arg[0];
	MTVFE_PRINT("- bPow = %d\n",(int)bPow);	 
	
	if(bPow==0 ||bPow==1)
	{
		vDrvAllHDADCPow(bPow);
		return MTR_OK;
	}
	else
		return MTR_NOT_OK;
   
	

}
int mtal_ioctl_mtvfe(struct inode *inode, struct file *file, unsigned int cmd,
              unsigned long arg)
{
       int i4Ret = MTR_OK;
	   
	switch (cmd) 
	{				
		case MTAL_IO_VFE_ADCSETTINGRESET:
			return _MTVFE_ADCSettingReset();
		case MTAL_IO_VFE_ADCCALIBRATION:
			return _MTVFE_ADCCalibration();
		case MTAL_IO_VFE_GETADCMAXVALUE:
			return _MTVFE_GetADCMaxValue(arg);
		case MTAL_IO_VFE_GETBLANKVAL:
			return _MTVFE_GetBlankVal(arg);
		case MTAL_IO_VFE_GETHDADCGAIN:
			return _MTVFE_GetHDADCGain(arg);
		case MTAL_IO_VFE_GETHDADCOFFSET:
			return _MTVFE_GetHDADCOffset(arg);
		case MTAL_IO_VFE_SETHDADCGAIN:
			return _MTVFE_SetHDADCGain(arg);
		case MTAL_IO_VFE_SETHDADCOFFSET:
			return _MTVFE_SetHDADCOffset(arg);
		case MTAL_IO_VFE_SETSYNCSLICELEVEL:
		       return _MTVFE_SetSlicerLevel(arg);
		case MTAL_IO_VFE_GET_GAIN_STORAGE:
        	       return _MTVFE_GetGainStorage(arg);
 		case MTAL_IO_VFE_EBGAIN_GAIN_OVERRULE:
        	       return _MTVFE_EBGainOverRule(arg);
 		case MTAL_IO_VFE_Embedded_GAIN:
        	       return _MTVFE_Embedded_Gain(arg);
		case MTAL_IO_VFE_GETEBGAIN:
			return _MTVFE_GET_Embedded_Gain(arg);
			break; 
		case MTAL_IO_VFE_SETHDADCPOW:
			return _MTVFE_SET_AllHDADCPow(arg);
			break; 
		default:
			return MTR_NOT_SUPPORTED;
	}
	return i4Ret;
}

