/* 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_mtfe_tuner.c,v $
 * $Revision: #1 $
 *---------------------------------------------------------------------------*/

/** @file io_mtfe_tuner.c
 */

//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------
#include "tuner_if.h"
#include "io_mtfe_pd_def.h"
#include "tuner_interface_if.h"
#include "io_mtfe_tuner.h"
#include <linux/mtal_ioctl.h>


//-----------------------------------------------------------------------------
// debug switch
//-----------------------------------------------------------------------------
//#ifdef DEBUG_TUNER_PERFORMANCE

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

//-----------------------------------------------------------------------------
// Type definitions
//-----------------------------------------------------------------------------


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

//-----------------------------------------------------------------------------
// Macro definitions
//-----------------------------------------------------------------------------

#define USR_SPACE_ACCESS_VALIDATE_1ARG(arg)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   sizeof(int)))                            \
    {                                                       \
        printk("mtal_ioctl_tuner argument error\n");     \
        return -1;                                          \
    }

#define COPY_FROM_USER_1ARG(ArgUsr, ArgKernel)              \
    if (copy_from_user(&ArgKernel, (void __user *)ArgUsr,   \
                       sizeof(int)))                        \
    {                                                       \
        printk("mtal_ioctl_tuner argument error\n");      \
        return -1;                                          \
    }

#define USR_SPACE_ACCESS_VALIDATE_2ARG(arg)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   sizeof(MTAL_IOCTL_2ARG_T)))              \
    {                                                       \
        printk("mtal_ioctl_tuner argument error\n");     \
        return -1;                                          \
    }

#define COPY_FROM_USER_2ARG(ArgUsr, ArgKernel)              \
    if (copy_from_user(&ArgKernel, (void __user *)ArgUsr,   \
                       sizeof(MTAL_IOCTL_2ARG_T)))          \
    {                                                       \
        printk("mtal_ioctl_tuner argument error\n");      \
        return -1;                                          \
    }
#define USR_SPACE_ACCESS_VALIDATE_3ARG(arg)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   sizeof(MTAL_IOCTL_3ARG_T)))              \
    {                                                       \
        printk("mtal_ioctl_tuner argument error\n");     \
        return -1;                                          \
    }

#define COPY_FROM_USER_3ARG(ArgUsr, ArgKernel)              \
    if (copy_from_user(&ArgKernel, (void __user *)ArgUsr,   \
                       sizeof(MTAL_IOCTL_3ARG_T)))          \
    {                                                       \
        printk("mtal_ioctl_tuner argument error\n");      \
        return -1;                                          \
    }
#define USR_SPACE_ACCESS_VALIDATE_ARG(arg,type)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   sizeof(type)))              \
    {                                                       \
        printk("mtal_ioctl_tuner 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_tuner argument error\n");      \
        return MTR_ERR_INV;                                          \
    }
#define COPY_FROM_USER_ARG(ArgUsr, ArgKernel,type)              \
    if (copy_from_user(&ArgKernel,(void __user *)ArgUsr,    \
                       sizeof(type)))          \
    {                                                       \
        printk("mtal_ioctl_tuner argument error\n");      \
        return MTR_ERR_INV;                                          \
    }

//-----------------------------------------------------------------------------
// Static variables
//-----------------------------------------------------------------------------
static MTFE_TUNER_PD_CTX_T *pMTPdCtx;
static BOOL _fgTunerInit=FALSE;
static HANDLE_T    Exclusive_DigiAnaSema=0;
static UINT8       u1ExclSemaInit=0;


//-----------------------------------------------------------------------------
// Static Functions
//-----------------------------------------------------------------------------
extern ITUNER_CTX_T *ITunerGetCtx(VOID);
extern void DigTunerBypassI2C(BOOL bSwitchOn);

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


//-----------------------------------------------------------------------------
// Type definitions
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Private functions
//-----------------------------------------------------------------------------
static MT_RESULT_T _MTFE_TUNER_Init(VOID)
{
	MTFE_TUNER_PRINT("In kernel %s\n",__FUNCTION__);
	if(TRUE==_fgTunerInit)
	{
		MTFE_TUNER_PRINT("_fgTunerInit == TRUE, Init done!\n");
		return (MTR_TUNER_ALREADY_INIT);
	}
	pMTPdCtx = (MTFE_TUNER_PD_CTX_T *)x_mem_calloc(1,sizeof(MTFE_TUNER_PD_CTX_T));
	if(NULL == pMTPdCtx)
	{
		MTFE_TUNER_PRINT("MTFE_TUNER_PD_CTX_T malloc fail!\n");
		return (MTR_NOT_OK);
	}
	if (x_sema_create(&(pMTPdCtx->hHalLock), X_SEMA_TYPE_BINARY, X_SEMA_STATE_LOCK) != OSR_OK)
    {
        MTFE_TUNER_PRINT(("Tuner x_sema_create(pMTPdCtx->hHalLock) Fail!\n"));
        return (MTR_NOT_OK);
    }
    x_sema_unlock(pMTPdCtx->hHalLock);
	if (x_sema_create(&Exclusive_DigiAnaSema, X_SEMA_TYPE_BINARY, X_SEMA_STATE_LOCK) != OSR_OK)
	{
		MTFE_TUNER_PRINT(("Tuner x_sema_create(Exclusive_DigiAnaSema) Fail!\n"));
		return (MTR_NOT_OK);
	}
	x_sema_unlock(Exclusive_DigiAnaSema);
	u1ExclSemaInit = 1;

	_fgTunerInit = TRUE;

	return MTR_OK;
}

static MT_RESULT_T _MTFE_TUNER_SetFreq(unsigned long arg)
{
	MTFE_TUNER_TUNE_PARAMETER_T *ptTunePara;
	MTFE_TUNER_TUNE_PARAMETER_T tTunePara;
	PARAM_SETFREQ_T SetFreqPara;

    ptTunePara = (MTFE_TUNER_TUNE_PARAMETER_T *)arg;
	MTFE_TUNER_PRINT("In kernel %s\n",__FUNCTION__);
	USR_SPACE_ACCESS_VALIDATE_ARG(ptTunePara,MTFE_TUNER_TUNE_PARAMETER_T);
    COPY_FROM_USER_ARG(ptTunePara, tTunePara,MTFE_TUNER_TUNE_PARAMETER_T);
	MTFE_TUNER_PRINT(" u4FrequencyInKhz = %d\n"
        "eBandWidth = %d\n"
        "e_mod = %d\n"
        "fgAutoSearch = %d\n",
        (INT32)tTunePara.u4FrequencyInKhz,
        (INT32)tTunePara.eBandWidth,
        tTunePara.eMod,
        tTunePara.fgAutoSearch
        );
	pMTPdCtx->tCurTunePara.u4FrequencyInKhz = tTunePara.u4FrequencyInKhz;
	pMTPdCtx->tCurTunePara.eBandWidth = tTunePara.eBandWidth;
	pMTPdCtx->tCurTunePara.eMod = tTunePara.eMod;
	pMTPdCtx->tCurTunePara.fgAutoSearch = tTunePara.fgAutoSearch;

	mcSEMA_LOCK_MTFE(pMTPdCtx->hHalLock); //when access PI Primitive API, must use this lock for protection
	
	ITuner_OP(ITunerGetCtx(), itSetSawBw, tTunePara.eBandWidth, NULL);
	SetFreqPara.Freq = tTunePara.u4FrequencyInKhz;
	SetFreqPara.Modulation = tTunePara.eMod;
	SetFreqPara.fgAutoSearch = tTunePara.fgAutoSearch;
	DigTunerBypassI2C(TRUE); //Need mutex with digital
	if((ITuner_SetFreq(ITunerGetCtx(), &SetFreqPara)) != 0)
	{
		MTFE_TUNER_PRINT("Fail at ITuner_SetFreq!\n");
		return (MTR_NOT_OK);
	}
	DigTunerBypassI2C(FALSE);
	
	mcSEMA_UNLOCK_MTFE(pMTPdCtx->hHalLock);
	
	return MTR_OK;
}

static MT_RESULT_T _MTFE_TUNER_OP(unsigned long arg)
{
	MTFE_OP_PAREMETER_T  tOPPara;
	INT32 *pi4TunerGetValue;
	INT32 i4TunerGetValue;
	MT_RESULT_T  eRet = MTR_OK;
	INT16 i1Ret;
	USR_SPACE_ACCESS_VALIDATE_ARG(arg,MTFE_OP_PAREMETER_T);
    COPY_FROM_USER_ARG(arg, tOPPara,MTFE_OP_PAREMETER_T);
	pi4TunerGetValue=(INT32*)tOPPara.i4GetValue;
	MTFE_TUNER_PRINT(" OPType = %d\n""SetValue = %d\n",(INT32)tOPPara.eOP,(INT32)tOPPara.u1SetValue);
	mcSEMA_LOCK_MTFE(pMTPdCtx->hHalLock);
	i1Ret=ITuner_OP(ITunerGetCtx(), tOPPara.eOP,tOPPara.u1SetValue,&i4TunerGetValue);
	MTFE_TUNER_PRINT("GetValue = %d\n",i4TunerGetValue);
	mcSEMA_UNLOCK_MTFE(pMTPdCtx->hHalLock);
	if(i1Ret==ITUNER_NOT_SUPPORTED)
    {
        eRet=MTR_NOT_SUPPORTED;
    }
    else if(i1Ret==ITUNER_NOT_OK)
    {
        eRet=MTR_NOT_OK;
    }
    else//Ok
    {
		USR_SPACE_ACCESS_VALIDATE_ARG(pi4TunerGetValue,INT32);
		COPY_TO_USER_ARG(pi4TunerGetValue,i4TunerGetValue,INT32);
		eRet=MTR_OK;
	}
	return eRet;
}

int mtal_ioctl_mtfe_tuner(struct inode *inode, struct file *file,
    unsigned int cmd, unsigned long arg)
{
    MT_RESULT_T eRet = MTR_OK;
    MTFE_TUNER_PRINT("-------tuner ioctl cmd-------\r\n");
    if(u1ExclSemaInit==1){
        mcSEMA_LOCK_MTFE_S(Exclusive_DigiAnaSema,"Ex_DiAna");
    }
	switch (cmd)
	{
		case MTAL_IO_TUNER_INIT:
			eRet=_MTFE_TUNER_Init();
			break;
		case MTAL_IO_TUNER_SETFREQ:
			eRet=_MTFE_TUNER_SetFreq(arg);
            break;
		case MTAL_IO_TUNER_OP:
			eRet=_MTFE_TUNER_OP(arg);
			break;
		default:
		    break;
	}
    if(u1ExclSemaInit==1){
        mcSEMA_UNLOCK_MTFE_S(Exclusive_DigiAnaSema, "Ex_DiAna");
    }
	return eRet;
}

