/* 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.
 *
 *****************************************************************************/


#if defined(LINUX_TURNKEY_SOLUTION) && defined(CC_ENABLE_VDECOMX)

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

#include <linux/cdev.h>
#include <linux/fs.h>          //For file_operations
#include "x_typedef.h"
#include "x_os.h"
#include "x_printf.h"
#include "x_assert.h"

#include "vdpomx_mod.h"
#include "vdpomx_drv_if.h"

#include <linux/cb_data.h>
#include <linux/version.h>

#include <asm/uaccess.h>
/////////////////////////////////////
//driver include file
#include "vdp_if.h"
#include "mute_if.h"
#include "vdo_if.h"
#include "b2r_drvif.h"
#include "vdec_drvif.h"
#include "srm_drvif.h"

//#define  CC_HWCOMP_UT
#ifdef CC_HWCOMP_UT
#include "fbm_drvif.h"
#endif
#define DEFINE_IS_LOG	VDP_IsLog
#include "x_debug.h"
/////////////////////////////////////

LINT_EXT_HEADER_BEGIN
#define DEFINE_IS_LOG    VDP_IsLog
#include "x_debug.h"
LINT_EXT_HEADER_END


#ifdef CC_HWCOMP_UT
static UCHAR _aucFbgId[VDP_NS] = {FBM_FBG_ID_UNKNOWN, FBM_FBG_ID_UNKNOWN};
static INT32 _ai4TemporalRef[VDP_NS] = {0, 0};
static BOOL _afgForceSequenceChange[VDP_NS] = {FALSE,FALSE};
#endif

static BOOL _afgVdpConnected[VDP_NS] = {FALSE,FALSE};

int vdp_set_out_region(VDPOMX_SET_REGION_PARM_T *prParm)
{
    INT32 i4Ret = VDPOMX_OK;
    VDPOMX_SET_REGION_PARM_T rParam ;

    USR_SPACE_ACCESS_VALIDATE_ARG(prParm, VDPOMX_SET_REGION_PARM_T);
    COPY_FROM_USER_ARG(prParm, rParam, VDPOMX_SET_REGION_PARM_T);

    VDP_SetOutRegion((UCHAR)(rParam.ucChId), (UCHAR)0, *(VDP_REGION_T*)&(rParam.rRegion));
    
    LOG(2, " - eVdpId = %d\n"
        "rSrcRegion.u4Y = %d\n"
        "rSrcRegion.u4Y = %d\n"
        "rSrcRegion.u4Width = %d\n"
        "rSrcRegion.u4Height = %d\n",
        rParam.ucChId,
        (int)rParam.rRegion.u4X,
        (int)rParam.rRegion.u4Y,
        (int)rParam.rRegion.u4Width,
        (int)rParam.rRegion.u4Height);
    return i4Ret;
}

int vdp_set_src_region(VDPOMX_SET_REGION_PARM_T *prParm)
{
    INT32 i4Ret = VDPOMX_OK;
    VDPOMX_SET_REGION_PARM_T rParam ;

    USR_SPACE_ACCESS_VALIDATE_ARG(prParm, VDPOMX_SET_REGION_PARM_T);
    COPY_FROM_USER_ARG(prParm, rParam, VDPOMX_SET_REGION_PARM_T);

    VDP_SetSrcRegion((UCHAR)(rParam.ucChId), (UCHAR)0, *(VDP_REGION_T*)&(rParam.rRegion));
    
    LOG(2, " - eVdpId = %d\n"
        "rSrcRegion.u4Y = %d\n"
        "rSrcRegion.u4Y = %d\n"
        "rSrcRegion.u4Width = %d\n"
        "rSrcRegion.u4Height = %d\n",
        rParam.ucChId,
        (int)rParam.rRegion.u4X,
        (int)rParam.rRegion.u4Y,
        (int)rParam.rRegion.u4Width,
        (int)rParam.rRegion.u4Height);
    return i4Ret;
}

int vdp_set_out_channel(VDPOMX_SET_CHANNEL_PARM_T *prParm)
{
    INT32 i4Ret = VDPOMX_OK;
    VDPOMX_SET_CHANNEL_PARM_T rParam ;
    UINT8 u1Src ;
    UINT32 u4UnMuteDleay ;

    USR_SPACE_ACCESS_VALIDATE_ARG(prParm, VDPOMX_SET_CHANNEL_PARM_T);
    COPY_FROM_USER_ARG(prParm, rParam, VDPOMX_SET_CHANNEL_PARM_T);

    LOG(1, "[vdpomx] Vdp(%d) Enable(%d)\n", rParam.ucChId, rParam.fgOnOff);
    
    if(rParam.fgOnOff)
    {
        u1Src = (rParam.ucChId == VDP_DISP_CHANNEL_0)?SV_VS_DTV1 :SV_VS_DTV2;
        u4UnMuteDleay = 0 ;

        if(bApiVideoSetVideoSrc(rParam.ucChId, u1Src) == SV_FAIL)
        {
            i4Ret = VDPOMX_ERR;
        }

        if(VDP_SetEnable(rParam.ucChId, rParam.fgOnOff) != VDP_SET_OK)
        {
            i4Ret = VDPOMX_ERR;
        }
        if(VDP_DISP_CHANNEL_0 != rParam.ucChId)
        {
            SRM_SetTvMode(SRM_TV_MODE_TYPE_PIP);
        }
    }
    else
    {
        //VDP_REGION_T rRegion ;
        u1Src = SV_VS_MAX ;
        //memset(&rRegion, 0, sizeof(VDP_REGION_T));
        //VDP_SetOutRegion((UCHAR)(rParam.ucChId), (UCHAR)0, *(VDP_REGION_T*)&(rRegion));
        u4UnMuteDleay = FOREVER_MUTE;

        if(VDP_SetEnable(rParam.ucChId, rParam.fgOnOff) != VDP_SET_OK)
        {
            i4Ret = VDPOMX_ERR;
        }

        if(bApiVideoSetVideoSrc(rParam.ucChId, u1Src) == SV_FAIL)
        {
            i4Ret = VDPOMX_ERR;
        }
        if(VDP_DISP_CHANNEL_0 != rParam.ucChId)
        {
            SRM_SetTvMode(SRM_TV_MODE_TYPE_NORMAL);
        }
        VDP_SetInput(rParam.ucChId, MAX_ES_NS, 0);
    }

    _vDrvVideoSetMute(MUTE_MODULE_API_FORCE, (UINT32) rParam.ucChId, u4UnMuteDleay , FALSE);

#ifdef CC_HWCOMP_UT
    VDP_SetInput(rParam.ucChId, 0, 0);
    _afgForceSequenceChange[rParam.ucChId] = TRUE;
#endif

    if (rParam.ucChId < VDP_NS)
    {
        _afgVdpConnected[rParam.ucChId] = FALSE;
    }

    return i4Ret;
}


int vdp_get_fb_width_height_by_tag(VDPOMX_GET_FB_WIDTH_HEIGHT_PARM_T * prParm)
{
    INT32 i4Ret = VDPOMX_OK;
    VDPOMX_GET_FB_WIDTH_HEIGHT_PARM_T rParam;
    UCHAR ucFbgId = FBM_FBG_ID_UNKNOWN;
    UCHAR ucFbId = FBM_FB_ID_UNKNOWN;
    FBM_PIC_HDR_T * prPicHdr = NULL;

    USR_SPACE_ACCESS_VALIDATE_ARG(prParm, VDPOMX_GET_FB_WIDTH_HEIGHT_PARM_T);
    COPY_FROM_USER_ARG(prParm, rParam, VDPOMX_GET_FB_WIDTH_HEIGHT_PARM_T);

    FBM_QueryFbByDispTag(rParam.u8Addr, &ucFbgId, &ucFbId);

    prPicHdr = FBM_GetFrameBufferPicHdr(ucFbgId, ucFbId);

    if (prPicHdr != NULL)
    {
        rParam.u4Width  = prPicHdr->fgCropping ? prPicHdr->u4CropWidth  : prPicHdr->u4PicWidth;
        rParam.u4Height = prPicHdr->fgCropping ? prPicHdr->u4CropHeight : prPicHdr->u4PicHeight;
        
        COPY_TO_USER_ARG(prParm, rParam, VDPOMX_GET_FB_WIDTH_HEIGHT_PARM_T);
    }
    else
    {
        i4Ret = VDPOMX_ERR;
    }
    
    return i4Ret;
}


int vdp_set_display_buf(VDPOMX_SET_DISPLAY_BUF_PARM_T *prParm)
{
    INT32 i4Ret = VDPOMX_OK;
    VDPOMX_SET_DISPLAY_BUF_PARM_T rParam;
    VDP_B2R_CHG_FRAME_MSG_T rB2rDispMsg;
    UCHAR ucFbgId = FBM_FBG_ID_UNKNOWN;
    UCHAR ucFbId = FBM_FB_ID_UNKNOWN;

    USR_SPACE_ACCESS_VALIDATE_ARG(prParm, VDPOMX_SET_DISPLAY_BUF_PARM_T);
    COPY_FROM_USER_ARG(prParm, rParam, VDPOMX_SET_DISPLAY_BUF_PARM_T);

    FBM_GetFbByDispTag(rParam.u4Addr, &ucFbgId, &ucFbId);

    if(ucFbgId==FBM_FBG_ID_UNKNOWN || ucFbId==FBM_FB_ID_UNKNOWN)
    {
       return VDPOMX_ERR;
    }

#ifndef CC_HWCOMP_UT
    if (rParam.ucChId < VDP_NS)
    {
        if (_afgVdpConnected[rParam.ucChId] == FALSE)
        {
            // Use buffer address to check real vdec es id.
            // And set es id to b2r input port
            UCHAR ucEsId;

            if (ucFbgId != FBM_FBG_ID_UNKNOWN)
            {
                // Check real decoder es id, and set to B2R input port
                ucEsId = FBM_GetDecoderSrcId(ucFbgId);
                VDP_SetInput(rParam.ucChId, ucEsId, 0);
                _afgVdpConnected[rParam.ucChId] = TRUE;
                VDP_SetB2rNoLock(rParam.ucChId, TRUE);
            }
        }
    }
#else
    if (rParam.ucChId < VDP_NS)
    {
        UCHAR ucVdpId = rParam.ucChId;
        FBM_SEQ_HDR_T *prSeqHdr = NULL;
        FBM_PIC_HDR_T *prPicHdr = NULL;

        if (ucFbgId != FBM_FBG_ID_UNKNOWN && ucFbId != FBM_FB_ID_UNKNOWN)
        {
            if (ucFbgId != _aucFbgId[ucVdpId] || _afgForceSequenceChange[ucVdpId] == TRUE)
            {
                FBM_FbgChgNotify(ucFbgId, VDP_Vdp2Es(ucVdpId));

                // Fbg changed, reset sequence info
                prSeqHdr = FBM_GetFrameBufferSeqHdr(ucFbgId);
                if (prSeqHdr != NULL)
                {
                    prSeqHdr->ucAspRatInf = MPEG_ASPECT_RATIO_1_1;
                    prSeqHdr->ucFrmRatCod = MPEG_FRAME_RATE_30_;
                    prSeqHdr->fgProgressiveSeq = TRUE;
                    prSeqHdr->u2LineSize = 176;
                    prSeqHdr->u2HSize = 176;
                    prSeqHdr->u2VSize = 144;
                    prSeqHdr->u2OrgHSize = 176;
                    prSeqHdr->u2OrgVSize = 144;
                    prSeqHdr->u4ResizeWidth = 176;
                    prSeqHdr->u4ResizeHeight = 144;

                    prSeqHdr->fgRasterOrder = TRUE;
                    prSeqHdr->fgResizeSmallPic = FALSE;
                    prSeqHdr->fgB2R3DEnable= FALSE;
                    prSeqHdr->fgColorDescrip = FALSE;
                    prSeqHdr->fgCropping = FALSE;

                    prSeqHdr->u4CropX = 0;
                    prSeqHdr->u4CropY = 0;
                    prSeqHdr->u4CropWidth = 0;
                    prSeqHdr->u4CropHeight = 0;
                    prSeqHdr->u4FrmCropTop = 0;
                    prSeqHdr->u4FrmCropBtm = 0;

                    prSeqHdr->fgSwDecoder = TRUE;

                    FBM_SetFrameBufferFlag(ucFbgId, FBM_FLAG_SEQ_CHG_SPEEDUP);  // trigger seq chg immediately
                }

                _aucFbgId[ucVdpId] = ucFbgId;
                _ai4TemporalRef[ucVdpId] = 0;
                _afgForceSequenceChange[ucVdpId] = FALSE;
            }

            prPicHdr = FBM_GetFrameBufferPicHdr(ucFbgId, ucFbId);
            if (prPicHdr != NULL)
            {
                prPicHdr->u4PTS = 0;
                prPicHdr->u4PicWidthPitch = 176;
                prPicHdr->u4PicWidth = 176;
                prPicHdr->u4PicHeight = 144;
                prPicHdr->i4TemporalRef = _ai4TemporalRef[ucVdpId]++;
                prPicHdr->fgProgressiveFrm = TRUE;

                prPicHdr->fgSeqHeader = FALSE;
                prPicHdr->fgSeamlessDisp = FALSE;
                prPicHdr->fgProgressive2Interlace = FALSE;
                prPicHdr->fgTrackingMode = FALSE;
                prPicHdr->fgRepFirstFld = FALSE;
                prPicHdr->fgTopFldFirst = FALSE;
                prPicHdr->fgDummyFrame = FALSE;
            }
        }
    }
#endif

    x_memset(&rB2rDispMsg, 0, sizeof(VDP_B2R_CHG_FRAME_MSG_T));

    rB2rDispMsg.u4VdpId = rParam.ucChId;
    rB2rDispMsg.u4Flag = VDP_B2R_CHG_FRAME_MSG_SYNC;
    rB2rDispMsg.u4DispMode = VDP_B2R_CHG_FRAME_DIRECT;
    rB2rDispMsg.ucFbgId = ucFbgId;
    rB2rDispMsg.ucFbId = ucFbId;

    //FBM_GetFrameBufferAddr(ucFbgId, ucFbId, &rB2rDispMsg.u4FrameAddrY,&rB2rDispMsg.u4FrameAddrC);
    if(VDP_SendB2RChgFrameMsg(&rB2rDispMsg) == FALSE)
    {
        printk("!!!HWC send msq failed [%d %d], tag:%x\n", ucFbgId, ucFbId, rParam.u4Addr);
        if(FBM_CheckFrameBufferStatus(ucFbgId, ucFbId, FBM_FB_STATUS_LOCK) && FBM_ChkFbDispTagInB2R(ucFbgId,ucFbId))
        {
            printk("!!!Set empty\n");
        FBM_SetFrameBufferStatus(ucFbgId, ucFbId, FBM_FB_STATUS_EMPTY);
        }
        i4Ret = VDPOMX_ERR;
    }

    return i4Ret;
}

int vdp_get_channel_free(VDPOMX_GET_CHANNEL_FREE_PARM_T *prParm)
{
    INT32 i4Ret = VDPOMX_OK;
    VDPOMX_GET_CHANNEL_FREE_PARM_T rParam;
    UINT8 u1CurSrc;

    USR_SPACE_ACCESS_VALIDATE_ARG(prParm, VDPOMX_GET_CHANNEL_FREE_PARM_T);
    COPY_FROM_USER_ARG(prParm, rParam, VDPOMX_GET_CHANNEL_FREE_PARM_T);

     u1CurSrc=(rParam.ucChId ==0)?_bSrcMainNew: _bSrcSubNew;

    rParam.fgFree = (u1CurSrc == SV_VS_MAX)? 1 : 0 ;

    COPY_TO_USER_ARG(prParm, rParam, VDPOMX_GET_CHANNEL_FREE_PARM_T);
    LOG(2, " - eVdpId = %d free = 0x%x\n", rParam.ucChId, rParam.fgFree);

    return i4Ret;
}

int vdp_get_buf_locked(VDPOMX_GET_BUF_LOCKED_PARM_T *prParm)
{
    INT32 i4Ret = VDPOMX_OK;
    VDPOMX_GET_BUF_LOCKED_PARM_T rParam;
//    UINT8 u1CurSrc;

    USR_SPACE_ACCESS_VALIDATE_ARG(prParm, VDPOMX_GET_BUF_LOCKED_PARM_T);
    COPY_FROM_USER_ARG(prParm, rParam, VDPOMX_GET_BUF_LOCKED_PARM_T);

    if (VDP_GetDispBufLocked(rParam.ucChId, rParam.u4Buf, &(rParam.fgLocked)) == 0)
    {
        i4Ret = VDPOMX_ERR;
    }

    COPY_TO_USER_ARG(prParm, rParam, VDPOMX_GET_BUF_LOCKED_PARM_T);

    return i4Ret;
}

int vdp_connect_vdec(VDPOMX_CONNECT_VDEC_PARM_T *prParm)
{
    INT32 i4Ret = VDPOMX_OK;
    VDPOMX_CONNECT_VDEC_PARM_T rParam;

    USR_SPACE_ACCESS_VALIDATE_ARG(prParm, VDPOMX_CONNECT_VDEC_PARM_T);
    COPY_FROM_USER_ARG(prParm, rParam, VDPOMX_CONNECT_VDEC_PARM_T);

    VDP_SetInput(rParam.ucChId,rParam.ucVdecId,rParam.ucPort);

    LOG(2, " - ucChId = %d ucVdecId = %d ucPort = %d\n", rParam.ucChId, rParam.ucVdecId, rParam.ucPort);

    return i4Ret;
}

int vdp_srm_settvmode(unsigned long arg)
{
    INT32 u4mode;

    USR_SPACE_ACCESS_VALIDATE_ARG(arg,INT32);
    COPY_FROM_USER_ARG(arg,u4mode,INT32);
    SRM_SetTvMode((SRM_TV_MODE_T)u4mode);
    return 0;
}

#endif

