/* 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_mtcec.c,v $
 * $Revision: #1 $
 * $Date: 2015/11/10 $
 * $Author: dtvbm11 $
 *
 * Description: video, such as hdmi (and others), are centralized in
 *                   this file
 *---------------------------------------------------------------------------*/

//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------
#include "mtmhl.h"
#include <linux/mtal_ioctl.h>
#include "mhl_if.h"
#include "cb_low.h"  // For callback function
#include "drv_hdmi.h"
#ifdef SYS_MHL_SUPPORT
#if defined (CC_MT5881) || defined(CC_MT5399) || defined(CC_MT5890)
#include "drv_cbus_if.h"
#endif
#endif
//-----------------------------------------------------------------------------
// Constant definitions
//-----------------------------------------------------------------------------

#define USR_SPACE_ACCESS_VALIDATE_2ARG(arg)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   sizeof(MTAL_IOCTL_2ARG_T)))              \
    {                                                       \
        printk("mtal_ioctl_cec argument error1.1\n");     \
        return MTR_ERR_INV;                                          \
    }                                                       \

#define USR_SPACE_ACCESS_VALIDATE_3ARG(arg)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   sizeof(MTAL_IOCTL_3ARG_T)))              \
    {                                                       \
        printk("mtal_ioctl_cec argument error1.2\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_cec argument error2.1\n");      \
        return MTR_ERR_INV;                                          \
    }     

#define COPY_FROM_USER_3ARG(ArgUsr, ArgKernel)              \
    if (copy_from_user(&ArgKernel, (void __user *)ArgUsr,   \
                       sizeof(MTAL_IOCTL_3ARG_T)))          \
    {                                                       \
        printk("mtal_ioctl_ntsc argument error2.2\n");      \
        return -1;                                          \
    }
	
#define USR_SPACE_ACCESS_VALIDATE_1ARG(arg)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg, 		\
					   sizeof(int)))							\
    {														\
	printk("mtal_ioctl_cec argument error 2.3\n"); 	\
	return -1;											\
    }
	
#define COPY_FROM_USER_1ARG(ArgUsr, ArgKernel)              \
if (copy_from_user(&ArgKernel, (void __user *)ArgUsr,	\
						   sizeof(int)))						\
    {														\
	printk("mtal_ioctl_cec argument error 2.4 \n"); 	 \
	return -1;											\
    }
		

#define USR_SPACE_ACCESS_VALIDATE_ARG(arg,type)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   sizeof(type)))              \
    {                                                       \
        printk("mtal_ioctl_cec argument error3\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_cec argument error4.1\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_cec argument error4.2\n");      \
        return MTR_ERR_INV;                                          \
    } 
    
#define USR_SPACE_ACCESS_VALIDATE_ARG_K(arg,size)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   size))              \
    {                                                       \
        printk("mtal_ioctl_cec argument error6\n");     \
        return MTR_ERR_INV;                                          \
    }                                                       \

#define COPY_TO_USER_ARG_K(ArgUsr, ArgKernel,size)              \
    if (copy_to_user((void __user *)ArgUsr, &ArgKernel,   \
                       size))          \
    {                                                       \
        printk("mtal_ioctl_cec argument error7\n");      \
        return MTR_ERR_INV;                                          \
    }     
#define COPY_FROM_USER_ARG_K(ArgUsr, ArgKernel,size)              \
    if (copy_from_user(&ArgKernel, (void __user *)ArgUsr,    \
                       size))          \
    {                                                       \
        printk("mtal_ioctl_cec argument error8\n");      \
        return MTR_ERR_INV;                                          \
    } 


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

/** MTCEC_Init()  Common Interface Initialization Fucntion
 *  @retval MTR_OK successful,otherwise MTR_NOT_OK.
 */
//-----------------------------------------------------------------------------
#ifdef SYS_MHL_SUPPORT
extern x_vbus_drv_nfy_ocp_status rMhlOcpCbHandler;
MTMHL_CB_T  rMhlCbT;
extern void _MTMHL_MWNfyOcp(UINT8 port,MHL_OC_STATUS result);

MT_RESULT_T  _MTMHL_Init (unsigned long arg) 
{
    MTMHL_PRINT("_MTMHL_Init\n");
#if defined (CC_MT5881) || defined(CC_MT5399) || defined(CC_MT5890)
    vCbusInit();
#endif
    MHL_SetOCPCallback(_MTMHL_MWNfyOcp);
    return MTR_OK;
}

void _MTMHL_MWNfyOcp(UINT8 port,MHL_OC_STATUS result)
{
    //MTCEC_CB_T  rCecCbT;
	rMhlCbT.u4Arg1 =(UINT32)port;
	rMhlCbT.u4Arg2 =(UINT32)result.MhlOCStatus;
	rMhlCbT.u4Arg3 =(UINT32)result.MhlDeviceConnect;
    MTMHL_PRINT("port%d:MhlOCStatus = %d,MhlDeviceConnect = %d\n",port,rMhlCbT.u4Arg2,rMhlCbT.u4Arg3);
}
MT_RESULT_T _MTMHL_GetOCPStatus(unsigned long arg)
{
   MHL_OC_STATUS* UserStatus;
   MHL_OC_STATUS KernelStatus;

   UserStatus = (MHL_OC_STATUS *)arg;    //ai4Arg[0]
   KernelStatus.MhlOCStatus = rMhlCbT.u4Arg2;
   KernelStatus.MhlDeviceConnect = rMhlCbT.u4Arg3;
  
   USR_SPACE_ACCESS_VALIDATE_ARG(UserStatus,MHL_OC_STATUS);
   COPY_TO_USER_ARG(UserStatus, KernelStatus,MHL_OC_STATUS);

   return MTR_OK;
   
}

#ifdef SYS_ANDROID_TV_TIF_SUPPORT
static MT_RESULT_T _MTMHL_SendMscSubCommand(unsigned long arg)
{  
    CBUS_MSC_MSG_COMMAND_T msg;
	BOOL uCbusStatus;
    x_memset((void *)&msg, 0, sizeof(CBUS_MSC_MSG_COMMAND_T));
    USR_SPACE_ACCESS_VALIDATE_ARG(arg, CBUS_MSC_MSG_COMMAND_T);
    COPY_FROM_USER_ARG(arg, msg, CBUS_MSC_MSG_COMMAND_T);    
#ifdef CC_COMBINE_4K_2K	
	if(u1GetHDMI2KModel())
	{
#endif
	uCbusStatus = fgCBUSGetDiscoveryOK();
	if(!uCbusStatus)
	{
		MTMHL_PRINT("_MTMHL_SendMscSubCommand FAIL! CBUS DISCONNECTED!\n");
		return MTR_NOT_OK;
		}
#ifdef CC_COMBINE_4K_2K	
	}
#endif
	vSendMscSubMsg(&msg);
    MTMHL_PRINT("_MTMHL_SendMscSubCommand\n");
    return MTR_OK;
}

static MT_RESULT_T _MTMHL_SendScratchPadData(unsigned long arg)
{  
    CBUS_MSG_DESCRIPTION_T msg;
	BOOL uCbusStatus;
    x_memset((void *)&msg, 0, sizeof(CBUS_MSG_DESCRIPTION_T));
    USR_SPACE_ACCESS_VALIDATE_ARG(arg, CBUS_MSG_DESCRIPTION_T);
    COPY_FROM_USER_ARG(arg, msg, CBUS_MSG_DESCRIPTION_T); 
#ifdef CC_COMBINE_4K_2K	
	if(u1GetHDMI2KModel())
	{
#endif
	uCbusStatus = fgCBUSGetDiscoveryOK();
	if(!uCbusStatus)
	{
		MTMHL_PRINT("_MTMHL_SendMscSubCommand FAIL! CBUS DISCONNECTED!\n");
		return MTR_NOT_OK;
		}
#ifdef CC_COMBINE_4K_2K	
	}
#endif
	vSendScratchPadData(&msg);
    MTMHL_PRINT("_MTMHL_SendScratchPadData\n");
    return MTR_OK;
}

static MT_RESULT_T _MTMHL_GetPeerMhlVersion(unsigned long arg)
{
	int peerMhlVersion;
	USR_SPACE_ACCESS_VALIDATE_1ARG(arg);
	u1CbusGetPeerMhlVersion(&peerMhlVersion);
	COPY_TO_USER_ARG(arg,peerMhlVersion,sizeof(peerMhlVersion));
	
    return MTR_OK;
}
static MT_RESULT_T _MTMHL_SetOption(unsigned long arg)
{
	MHL_SET_OPTION_T eMhlSetOption;
    USR_SPACE_ACCESS_VALIDATE_ARG(arg, MHL_SET_OPTION_T);
	COPY_FROM_USER_ARG(arg, eMhlSetOption, MHL_SET_OPTION_T);
	u1MhlSetOption(eMhlSetOption);
	
    return MTR_OK;
}
static MT_RESULT_T _MTMHL_GetCbusMode(unsigned long arg)
{
	int cbusMode;
	USR_SPACE_ACCESS_VALIDATE_1ARG(arg);
	u1CbusGetMhlcbusMode(&cbusMode);
	COPY_TO_USER_ARG(arg,cbusMode,sizeof(cbusMode));
	
    return MTR_OK;
}

static MT_RESULT_T _MTMHL_GetSupportFeautures(unsigned long arg)
{
	int feature;
	USR_SPACE_ACCESS_VALIDATE_1ARG(arg);
	u1CbusGetSupportFeature(&feature);
	COPY_TO_USER_ARG(arg,feature,sizeof(feature));
	
    return MTR_OK;
}

static MT_RESULT_T _MTMHL_EcbusDeviceRoles(unsigned long arg)
{
	int devRole;
	USR_SPACE_ACCESS_VALIDATE_1ARG(arg);
	u1CbusGetEcbusDevRoles(&devRole);
	COPY_TO_USER_ARG(arg,devRole,sizeof(devRole));
	
    return MTR_OK;
}

static void  _MTMHL_ScratchPadDataNfy(CBUS_MSG_DESCRIPTION_T *arMsg)
{
    _CB_PutEvent(CB_MTAL_MHL_SCRATCHPAD_DATA_NFY, sizeof(CBUS_MSG_DESCRIPTION_T), (void*)arMsg);    
    MTMHL_PRINT(" -_MTMHL_ScratchPadDataNfy is done! length: %d, arg: %d\n", arMsg->Size, arMsg->arg);
}

static void _MTMHL_MscSubCommandNfy(UINT8 u1Port,UINT8 u1Opcode,UINT8 data,void *arg)
{
	MTMHL_CB_T  rMhlCbT;

    rMhlCbT.u4Arg1 = (UINT32)u1Port;
    rMhlCbT.u4Arg2 = (UINT32)u1Opcode;
	rMhlCbT.u4Arg3 = (UINT32)data;
    rMhlCbT.u4Arg4 = (UINT32)arg;
	_CB_PutEvent(CB_MTAL_MHL_MSC_SUBCOMMAND_NFY, sizeof(MTMHL_CB_T), (void*)&rMhlCbT);	
	MTMHL_PRINT(" -_MTMHL_MscSubCommandNfy is done! Port = 0x%x, opcode = 0x%x, data = 0x%x, arg = 0x%x\n", 
	rMhlCbT.u4Arg1, rMhlCbT.u4Arg2, rMhlCbT.u4Arg3,rMhlCbT.u4Arg4);
}

static void _MTMHL_HotPlugNfy(UINT8 u1Port,UINT8 connect, void *arg)
{
    MTMHL_CB_T  rMhlCbT;

    rMhlCbT.u4Arg1 = (UINT32)u1Port;
    rMhlCbT.u4Arg2 = (UINT32)connect;
    rMhlCbT.u4Arg3 = (UINT32)arg;
    
    _CB_PutEvent(CB_MTAL_MHL_HOT_PLUG_NFY, sizeof(MTMHL_CB_T), (void*)&rMhlCbT);    
    MTMHL_PRINT(" -_MTMHL_HotPlugNfy is done! u1Switch: %d, b5VStatus : %d,arg =%d\n", rMhlCbT.u4Arg1, rMhlCbT.u4Arg2,rMhlCbT.u4Arg3);
}

static void _MTCEC_CbusModeNfy(UINT8 u1Port,UINT8 mode, void *arg)
{
    MTMHL_CB_T  rMhlCbT;

    rMhlCbT.u4Arg1 = (UINT32)u1Port;
    rMhlCbT.u4Arg2 = (UINT32)mode;
    rMhlCbT.u4Arg3 = (UINT32)arg;
    _CB_PutEvent(CB_MTAL_MHL_CBUS_MODE_NFY, sizeof(MTMHL_CB_T), (void*)&rMhlCbT);    
    MTMHL_PRINT(" -_MTCEC_CbusModeNfy is done! u1Switch: %d, cbus mode : %d,arg =%d\n", rMhlCbT.u4Arg1, rMhlCbT.u4Arg2,rMhlCbT.u4Arg3);
}

static void _MTCEC_VbusOverCurrentNfy(UINT8 u1Port,UINT8 ocStatus,void *arg)
{
    MTMHL_CB_T  rMhlCbT;

    rMhlCbT.u4Arg1 = (UINT32)u1Port;
    rMhlCbT.u4Arg2 = (UINT32)ocStatus;
    rMhlCbT.u4Arg3 = (UINT32)arg;
    _CB_PutEvent(CB_MTAL_MHL_VBUS_OVERCURRENT_NFY, sizeof(MTMHL_CB_T), (void*)&rMhlCbT);    
    MTMHL_PRINT(" -_MTCEC_VbusOverCurrentNfy is done! u1Switch: %d, cbus mode : %d,arg =%d\n", rMhlCbT.u4Arg1, rMhlCbT.u4Arg2,rMhlCbT.u4Arg3);
}

static void _MTCEC_DcChangeNfy(UINT8 u1Port,UINT16 adopterID,UINT16 deviceID, void *arg)
{
    MTMHL_CB_T  rMhlCbT;

    rMhlCbT.u4Arg1 = (UINT32)u1Port;
    rMhlCbT.u4Arg2 = (UINT32)adopterID;
    rMhlCbT.u4Arg3 = (UINT32)deviceID;
	rMhlCbT.u4Arg4 = (UINT32)arg;
    _CB_PutEvent(CB_MTAL_MHL_DC_CHANGE_NFY, sizeof(MTMHL_CB_T), (void*)&rMhlCbT);    
    MTMHL_PRINT(" -_MTCEC_DcChangeNfy is done! u1Port: %d, adopterID : %d,deviceID =%d\n", rMhlCbT.u4Arg1, rMhlCbT.u4Arg2,rMhlCbT.u4Arg3);
}
/**
* @brief Register cec callback function
* @param eFuncType call back function type
* @param u4FuncPtr pointer to call back function
* @param u4Arg1
* @retval MTR_OK          Success.
* @retval MTR_NOT_OK  Fail.
*/
static MT_RESULT_T _MTMHL_RegCbFunc(unsigned long arg)
{
    //add driver implementation code here    
    MTAL_IOCTL_3ARG_T rArg;
    MTMHL_CB_FUNC_ENUM_T eFuncType;
    UINT32 u4FuncPtr;
    UINT32 u4Arg1;

    USR_SPACE_ACCESS_VALIDATE_3ARG(arg);
    COPY_FROM_USER_3ARG(arg, rArg);
    
    eFuncType = (MTMHL_CB_FUNC_ENUM_T)rArg.ai4Arg[0];
    u4FuncPtr = (UINT32)rArg.ai4Arg[1];
    u4Arg1 = (UINT32)rArg.ai4Arg[2];

    MTMHL_PRINT(" -_MTMHL_RegCbFunc: eFuncType = %d, u4FuncPtr = %ul, u4Arg1 = %ul\n",
        eFuncType, u4FuncPtr, u4Arg1);

    switch(eFuncType)
    {
        case MTCEC_CB_FUNC_MSC_SUBCOMMAND:
            {
                vCbus_SetMscCallback(_MTMHL_MscSubCommandNfy, (void *)u4Arg1);
            }		
            break;
			
        case MTCEC_CB_FUNC_SCRATCHPAD_DATA:
            { 
                vCbus_SetScratchPadCallback(_MTMHL_ScratchPadDataNfy,(void *)u4Arg1);
            }
            break;
			
        case MTCEC_CB_FUNC_HOT_PLUG:
            {
                vCbus_SetHotPlugCallback(_MTMHL_HotPlugNfy,(void *)u4Arg1);
             }
            break;
			
		case MTCEC_CB_FUNC_CBUS_MODE:
			{
				vCbus_SetCbusModeCallback(_MTCEC_CbusModeNfy,(void *)u4Arg1);
			}
			break;
			
		case MTCEC_CB_FUNC_VBUS_OVERCURRENT:
			{
				vCbus_SetOverCurrentCallback(_MTCEC_VbusOverCurrentNfy,(void *)u4Arg1);
			}
			break;
		case MTCEC_CB_FUNC_DC_CHANGE:
			{
				vCbus_SetPeerDcChangeCallback(_MTCEC_DcChangeNfy,(void *)u4Arg1);
			}
			break;
        default:
            return MTR_NOT_OK;
            break;
    }
    return MTR_OK;

}
#endif

int mtal_ioctl_mtmhl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
    MT_RESULT_T eRet = MTR_OK;
    MTMHL_PRINT("-------mtal_ioctl_mtmhl-------\r\n");
    switch (cmd)
    {
        case MTAL_IO_MHL_INIT:
            MTMHL_PRINT("-------mhl ioctl cmd: MTAL_IO_MHL_INIT-------\r\n");
            eRet = _MTMHL_Init(arg);
            break;
		case MHL_IOCTL_GET_OVER_CURRENT_STA:
            MTMHL_PRINT("-------mhl ioctl cmd: MHL_IOCTL_GET_OVER_CURRENT_STA-------\r\n");
			eRet = _MTMHL_GetOCPStatus(arg);
			break;
#ifdef SYS_ANDROID_TV_TIF_SUPPORT
        case MTAL_IO_MHL_REGCB:
            MTMHL_PRINT("-------mhl ioctl cmd: MTAL_IO_MHL_REGCB-------\r\n");
            eRet = _MTMHL_RegCbFunc(arg);
            break;
		case MTAL_IO_MHL_SENDMSCCOMMAND:
			MTMHL_PRINT("-------mhl ioctl cmd: MTAL_IO_MHL_SENDMSCCOMMAND-------\r\n");
			eRet = _MTMHL_SendMscSubCommand(arg);
			break;
		case MTAL_IO_MHL_SENDMSCRATCHPAD:
			MTMHL_PRINT("-------mhl ioctl cmd: MTAL_IO_MHL_SENDMSCRATCHPAD-------\r\n");
			eRet = _MTMHL_SendScratchPadData(arg);
			break;
		case MTAL_IO_MHL_GET_PEER_MHL_VERSION:
			MTMHL_PRINT("-------mhl ioctl cmd: MTAL_IO_MHL_GET_PEER_MHL_VERSION-------\r\n");
			eRet = _MTMHL_GetPeerMhlVersion(arg);
			break;
		case MTAL_IO_MHL_SET_OPTION:
			MTMHL_PRINT("-------mhl ioctl cmd: MTAL_IO_MHL_SET_OPTION-------\r\n");	
			eRet = _MTMHL_SetOption(arg);
			break;
		case MTAL_IO_MHL_GET_CBUS_MODE:
			MTMHL_PRINT("-------mhl ioctl cmd: MTAL_IO_MHL_GET_CBUS_MODE-------\r\n");
			eRet = _MTMHL_GetCbusMode(arg);
			break;
		case MTAL_IO_MHL_GET_SUPPORT_FEATURE:
			MTMHL_PRINT("-------mhl ioctl cmd: MTAL_IO_MHL_GET_SUPPORT_FEATURE-------\r\n");
			eRet = _MTMHL_GetSupportFeautures(arg);
			break;
		case MTAL_IO_MHL_GET_ECBUS_DEV_ROLES:
			MTMHL_PRINT("-------mhl ioctl cmd: MTAL_IO_MHL_GET_ECBUS_DEV_ROLES-------\r\n");
			eRet = _MTMHL_EcbusDeviceRoles(arg);
			break;

#endif
        default: 
            break;
    }
    return eRet;
}
#endif

