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

/*-----------------------------------------------------------------------------
 *
 * $Author: shuo.ding $
 * $Date: 2015/08/19 $
 * $RCSfile: osd_region_hw.c,v $
 * $Revision: #7 $
 *
 *---------------------------------------------------------------------------*/

/** @file osd_region_hw.c
 *  This header file includes function definitions of OSD region hardware.
 */

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

#include "osd_hw.h"
#include "osd_drvif.h"
#include "osd_debug.h"

#ifdef CC_MTK_LOADER
#define HalFlushDCache()
#endif /* CC_MTK_LOADER */




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

#define OSD_RGN_ALLOCATED 1

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

#define OSD_REGION_FLUSH(REGION) \
do \
{ \
HalFlushInvalidateDCacheMultipleLine((UPTR)(&_prOsdRegionReg[REGION]), sizeof(OSD_RGN_UNION_T)); \
} \
while (0)

/*lint -save -e960 */
#define OSD_REGION_SETGET_TMPL(NAME, FIELD) \
    INT32 _OSD_RGN_Set##NAME(UINT32 u4Region, UINT32 u4Value) \
    { \
        OSD_VERIFY_REGION(u4Region); \
        _prOsdRegionReg[u4Region].rField.FIELD = u4Value; \
        OSD_REGION_FLUSH(u4Region); \
        return (INT32)OSD_RET_OK; \
    } \
    INT32 _OSD_RGN_Get##NAME(UINT32 u4Region, UINT32 *pu4Value) \
    { \
        OSD_VERIFY_REGION(u4Region); \
        if (pu4Value == NULL) \
        { \
            return -(INT32)OSD_RET_INV_ARG; \
        } \
        *pu4Value = _prOsdRegionReg[u4Region].rField.FIELD; \
        return (INT32)OSD_RET_OK; \
    } \

/* above line is intendedly left blanc */
/*lint -restore */

/*lint -save -e960 */
#if defined(CC_MT5863) && defined(__KERNEL__) && !defined(CC_UBOOT)

#define OSD_REGION_SETGET_ADDRESS_TMPL(NAME, FIELD) \
    INT32 _OSD_RGN_Set##NAME(UINT32 u4Region, UPTR upValue) \
    { \
        OSD_VERIFY_REGION(u4Region); \
        _prOsdRegionReg[u4Region].rField.FIELD##High = (UINT32)((UINT64)upValue >> 32) & 0x7f; \
        _prOsdRegionReg[u4Region].rField.FIELD = (UINT32)upValue >> 4; \
        OSD_REGION_FLUSH(u4Region); \
        return (INT32)OSD_RET_OK; \
    } \
    INT32 _OSD_RGN_Get##NAME(UINT32 u4Region, UPTR *pupValue) \
    { \
        BOOL fgEnable = FALSE;\
        OSD_VERIFY_REGION(u4Region); \
        if (pupValue == NULL) \
        { \
            return -(INT32)OSD_RET_INV_ARG; \
        } \
        OSD_MMU_GetEnable(OSD_RGN_TO_PLANE(u4Region), &fgEnable);\
        if (fgEnable) \
            *pupValue = (UPTR)(VIRTUAL_ADDRESS_BASE | (((UINT64)(_prOsdRegionReg[u4Region].rField.FIELD##High) << 32) \
                        | ((UINT64)(_prOsdRegionReg[u4Region].rField.FIELD) << 4))); \
        else \
            *pupValue = (UPTR)(((UINT64)(_prOsdRegionReg[u4Region].rField.FIELD##High) << 32) \
                        | ((UINT64)(_prOsdRegionReg[u4Region].rField.FIELD) << 4)); \
        return (INT32)OSD_RET_OK; \
    } \

#else

#define OSD_REGION_SETGET_ADDRESS_TMPL(NAME, FIELD) \
    INT32 _OSD_RGN_Set##NAME(UINT32 u4Region, UPTR upValue) \
    { \
        OSD_VERIFY_REGION(u4Region); \
        _prOsdRegionReg[u4Region].rField.FIELD = upValue >> 4; \
        OSD_REGION_FLUSH(u4Region); \
        return (INT32)OSD_RET_OK; \
    } \
    INT32 _OSD_RGN_Get##NAME(UINT32 u4Region, UPTR *pupValue) \
    { \
        OSD_VERIFY_REGION(u4Region); \
        if (pupValue == NULL) \
        { \
            return -(INT32)OSD_RET_INV_ARG; \
        } \
        *pupValue = (UPTR)((UINT32)_prOsdRegionReg[u4Region].rField.FIELD << 4); \
        return (INT32)OSD_RET_OK; \
    } \

#endif

/* above line is intendedly left blanc */
/*lint -restore */

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

//static UINT8 _au1OsdRegion[(OSD_MAX_NUM_RGN * sizeof(OSD_RGN_UNION_T)) + 15];
static UINT32 *_au4OsdRegionStatus;
static OSD_RGN_UNION_T *_prOsdRegionReg;

//-----------------------------------------------------------------------------
// Inter-file functions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
/** Brief
 *  @param
 *  @return
 */
//-----------------------------------------------------------------------------
void _OSD_RGN_InitApi(void)
{
    // just init once
    if (_prOsdRegionReg == NULL)
    {
#ifdef CC_MTK_LOADER
        UPTR _au1OsdRegion = (UPTR)x_mem_alloc((OSD_MAX_NUM_RGN * sizeof(OSD_RGN_UNION_T)) + 15);
        _au1OsdRegion = (_au1OsdRegion + 0xf) & ~0xf;
#else
        UPTR _au1OsdRegion = BSP_AllocAlignedDmaMemory(
                                   (OSD_MAX_NUM_RGN * sizeof(OSD_RGN_UNION_T)), 16);
#endif
        _prOsdRegionReg = (OSD_RGN_UNION_T *)(UPTR)VIRTUAL(_au1OsdRegion);
    }

    if (_au4OsdRegionStatus == NULL)
    {
        _au4OsdRegionStatus = x_mem_alloc((OSD_MAX_NUM_RGN + 1) * sizeof(UINT32));
    }

    if (_prOsdRegionReg != NULL)
        UNUSED(x_memset(_prOsdRegionReg, 0, sizeof(OSD_RGN_UNION_T) * OSD_MAX_NUM_RGN));
    if (_au4OsdRegionStatus != NULL)
        UNUSED(x_memset(_au4OsdRegionStatus, 0, sizeof(UINT32) * (OSD_MAX_NUM_RGN + 1)));
}

//-----------------------------------------------------------------------------
/** Brief
 *  @param
 *  @return
 */
//-----------------------------------------------------------------------------
INT32 _OSD_RGN_GetHandle(UPTR u4Addr, UINT32 *pu4Region)
{
    if ((u4Addr < (UPTR) _prOsdRegionReg) || (u4Addr >= ((OSD_MAX_NUM_RGN
                                                          * sizeof(OSD_RGN_UNION_T)) + (UPTR) _prOsdRegionReg)))
    {
        return -(INT32) OSD_RET_UNDEF_ERR;
    }

    if (pu4Region == NULL)
    {
        return -(INT32) OSD_RET_INV_ARG;
    }

    *pu4Region = (u4Addr - (UPTR) _prOsdRegionReg) / sizeof(OSD_RGN_UNION_T);
    return (INT32) OSD_RET_OK;
}

//-----------------------------------------------------------------------------
/** Brief
 *  @param
 *  @return
 */
//-----------------------------------------------------------------------------
INT32 _OSD_RGN_GetAddress(UINT32 u4Region, UPTR *pupAddr)
{
    OSD_VERIFY_REGION(u4Region);

    if (pupAddr == NULL)
    {
        return -(INT32) OSD_RET_INV_ARG;
    }

    *pupAddr = (UPTR) & _prOsdRegionReg[u4Region];
    return (INT32) OSD_RET_OK;
}

//-----------------------------------------------------------------------------
/** Brief
 *  @param
 *  @return
 */
//-----------------------------------------------------------------------------
INT32 _OSD_RGN_GetRegionId(UPTR u4RegionAdress , UINT32 *pu4RegionId)
{
    UINT32 u4_lp = 0x0;

    if (pu4RegionId == NULL)
    {
        return -(INT32) OSD_RET_INV_ARG;
    }

    for (u4_lp = 0x0; u4_lp < OSD_MAX_NUM_RGN; u4_lp++)
    {
        if (u4RegionAdress == PHYSICAL((UPTR) & _prOsdRegionReg[u4_lp]))
        {
            break;
        }
    }

    *pu4RegionId = u4_lp;
    return (INT32) OSD_RET_OK;
}


//-----------------------------------------------------------------------------
/** Brief
 *  @param
 *  @return
 */
//-----------------------------------------------------------------------------
INT32 _OSD_RGN_Alloc(UINT32 *pu4Region)
{
    INT32 i4Rgn;

    if (pu4Region == NULL)
    {
        return -(INT32) OSD_RET_INV_ARG;
    }

    for (i4Rgn = 0; i4Rgn < OSD_MAX_NUM_RGN; i4Rgn++)
    {
        if ((_au4OsdRegionStatus[i4Rgn] & OSD_RGN_ALLOCATED) == 0)
        {
            *pu4Region = (UINT32) i4Rgn;
            _au4OsdRegionStatus[i4Rgn] |= OSD_RGN_ALLOCATED;
            return (INT32) OSD_RET_OK;
        }
    }

    return -(INT32) OSD_RET_OUT_OF_REGION;
}

INT32 _OSD_RGN_Reset(UINT32 u4Region)
{
    _OSD_RGN_SetNextRegion(u4Region, 0);
    _OSD_RGN_SetNextEnable(u4Region, 0);
    _OSD_RGN_SetAlpha(u4Region, (UINT32) 0xff);
    _OSD_RGN_SetColorKeyEnable(u4Region, 0);
    _OSD_RGN_SetColorKey(u4Region, 0);
    _OSD_RGN_SetHClip(u4Region, 0);
    _OSD_RGN_SetVClip(u4Region, 0);
    _OSD_RGN_SetBlendMode(u4Region, (UINT32)OSD_BM_PIXEL);
    _OSD_RGN_SetASel(u4Region, 3);
    _OSD_RGN_SetYrSel(u4Region, 2);
    _OSD_RGN_SetUgSel(u4Region, 1);
    _OSD_RGN_SetVbSel(u4Region, 0);
    _OSD_RGN_SetHMirror(u4Region, 0);
    _OSD_RGN_SetVFlip(u4Region, 0);
    return (INT32) OSD_RET_OK;
}

//-----------------------------------------------------------------------------
/** Brief
 *  @param
 *  @return
 */
//-----------------------------------------------------------------------------
INT32 _OSD_RGN_Free(UINT32 u4Region)
{
    OSD_VERIFY_REGION(u4Region);
    _OSD_RGN_Reset(u4Region);
    _au4OsdRegionStatus[u4Region] &= ~OSD_RGN_ALLOCATED;
    return (INT32) OSD_RET_OK;
}

INT32 _OSD_RGN_AllFlipMirrorMode(void)
{
    INT32 i4Rgn;

    for (i4Rgn = 0; i4Rgn < OSD_MAX_NUM_RGN; i4Rgn++)
    {
        if ((_au4OsdRegionStatus[i4Rgn] & OSD_RGN_ALLOCATED) != 0)
        {
            OSD_RGN_Set(i4Rgn, (INT32)OSD_RGN_V_FLIP, OSD_GetCpblty(E_OSD_CPBLTY_FLIP, 0x0));
            OSD_RGN_Set(i4Rgn, (INT32)OSD_RGN_H_MIRROR, OSD_GetCpblty(E_OSD_CPBLTY_MIRROR, 0x0));
        }
    }

    return (INT32) OSD_RET_OK;
}

//-----------------------------------------------------------------------------
/** Brief
 *  @param
 *  @return
 */
//-----------------------------------------------------------------------------

INT32 _OSD_RGN_Copy(UINT32 u4DstRgn, UINT32 u4SrcRgn)
{
    INT32 i4Ret = OSD_RET_OK;
    OSD_VERIFY_REGION(u4SrcRgn);
    OSD_VERIFY_REGION(u4DstRgn);
    x_memcpy(&_prOsdRegionReg[u4DstRgn], &_prOsdRegionReg[u4SrcRgn], sizeof(OSD_RGN_UNION_T));
    x_memcpy(&_au4OsdRegionStatus[u4DstRgn], &_au4OsdRegionStatus[u4SrcRgn], sizeof(UINT32));
    return i4Ret;
}

INT32 OSD_RGN_Dump(UINT32 u4Region)
{
    UINT32 u4PosX, u4PosY;
    UINT32 u4BmpW, u4BmpH, u4DispW, u4DispH, u4ColorMode;
    UINT32 u4Alpha, u4LineSize, u4ClipV, u4ClipH;
    UINT32 u4PalLoad, u4PalLen, u4StepV, u4StepH;
    UINT32 u4ColorKey, u4ColorKeyEn, u4MixSel;
    UINT32 u4VFlip, u4HMirror;
    UINT32 u4DecEn, u4DecMode, u4DecLine;
    UINT32 u4AlphaSel, u4YrSel, u4UgSel, u4VbSel, u410Bit;
    UPTR upBmpAddr, upPalAddr;
    OSD_VERIFY_REGION(u4Region);

    _OSD_RGN_GetOutputPosX(u4Region, &u4PosX);
    _OSD_RGN_GetOutputPosY(u4Region, &u4PosY);
    _OSD_RGN_GetInputWidth(u4Region, &u4BmpW);
    _OSD_RGN_GetInputHeight(u4Region, &u4BmpH);
    _OSD_RGN_GetOutputWidth(u4Region, &u4DispW);
    _OSD_RGN_GetOutputHeight(u4Region, &u4DispH);
    _OSD_RGN_GetColorMode(u4Region, &u4ColorMode);
    _OSD_RGN_GetAlpha(u4Region, &u4Alpha);
    _OSD_RGN_GetDataAddr(u4Region, &upBmpAddr);
    _OSD_RGN_GetLineSize(u4Region, &u4LineSize);
    _OSD_RGN_GetHClip(u4Region, &u4ClipH);
    _OSD_RGN_GetVClip(u4Region, &u4ClipV);
    _OSD_RGN_GetLoadPalette(u4Region, &u4PalLoad);
    _OSD_RGN_GetPaletteAddr(u4Region, &upPalAddr);
    _OSD_RGN_GetPaletteLen(u4Region, &u4PalLen);
    _OSD_RGN_GetHStep(u4Region, &u4StepH);
    _OSD_RGN_GetVStep(u4Region, &u4StepV);
    _OSD_RGN_GetColorKey(u4Region, &u4ColorKey);
    _OSD_RGN_GetColorKeyEnable(u4Region, &u4ColorKeyEn);
    _OSD_RGN_GetBlendMode(u4Region, &u4MixSel);
    _OSD_RGN_GetVFlip(u4Region, &u4VFlip);
    _OSD_RGN_GetHMirror(u4Region, &u4HMirror);
    _OSD_RGN_GetDecEn(u4Region, &u4DecEn);
    _OSD_RGN_GetDecMode(u4Region, &u4DecMode);
    _OSD_RGN_GetDecLine(u4Region, &u4DecLine);
    _OSD_RGN_GetASel(u4Region, &u4AlphaSel);
    _OSD_RGN_GetYrSel(u4Region, &u4YrSel);
    _OSD_RGN_GetUgSel(u4Region, &u4UgSel);
    _OSD_RGN_GetVbSel(u4Region, &u4VbSel);
    _OSD_RGN_GetOsd10BitEn(u4Region, &u410Bit);
    LOG(0, "REGION[%u]\n", u4Region);
    LOG(0, "    DATA_ADDR=0x%x, PITCH=%d, COLOR_MODE=%d\n",
           upBmpAddr, u4LineSize << 4, u4ColorMode);
    LOG(0, "    SrcSize(IHW:%d, IVW:%d), DstSize(OHW:%d, OVW:%d)\n",
           u4BmpW, u4BmpH, u4DispW, u4DispH);
    LOG(0, "    Pos(OHS:%d, OVS:%d), Clip(HCLIP:%d, VCLIP:%d)\n",
           u4PosX, u4PosY, u4ClipH, u4ClipV);
    LOG(0, "    VFLIP=%d, HMIRROR=%d,Step(H_STEP:0x%x, V_STEP:0x%x)\n",
           u4VFlip, u4HMirror, u4StepH, u4StepV);
    LOG(0, "    MIX_WEIGHT=%d, MIX_SEL=%d, COLOR_KEY_EN:%d, COLOR_KEY:0x%x\n",
           u4Alpha, u4MixSel, u4ColorKeyEn, u4ColorKey);
    LOG(0, "    AlphaSel=%d, YrSel=%d, UgSel=%d, VbSel=%d\n",
           u4AlphaSel, u4YrSel, u4UgSel, u4VbSel);
    LOG(0, "    PALETTE_ADDR=0x%x, PALETTE_LENGTH=%d, NEW_PALETTE=%d\n",
           upPalAddr, u4PalLen, u4PalLoad);
    LOG(0, "    Dec(DEC_EN:%d, DEC_MODE:%d, DEC_LINE:%d) 10BIT_EN:%d\n",
           u4DecEn, u4DecMode, u4DecLine, u410Bit);
    return (INT32) OSD_RET_OK;
}

OSD_REGION_SETGET_ADDRESS_TMPL(NextRegion, u4NextOsdAddr)
OSD_REGION_SETGET_ADDRESS_TMPL(PaletteAddr, u4PaletteAddr)
OSD_REGION_SETGET_ADDRESS_TMPL(DataAddr, u4DataAddr)
OSD_REGION_SETGET_TMPL(NextEnable, fgNextOsdEn)
OSD_REGION_SETGET_TMPL(Alpha, u4MixWeight)
OSD_REGION_SETGET_TMPL(HClip, u4HClip)
OSD_REGION_SETGET_TMPL(VClip, u4VClip)
OSD_REGION_SETGET_TMPL(UgSel, u4UgSel)
OSD_REGION_SETGET_TMPL(YrSel, u4YrSel)
OSD_REGION_SETGET_TMPL(VbSel, u4VbSel)
OSD_REGION_SETGET_TMPL(ASel, u4AlphaSel)
OSD_REGION_SETGET_TMPL(Osd10BitEn, fg10bitOsdEn)
OSD_REGION_SETGET_TMPL(PaletteLen, u4PaletteLen)
OSD_REGION_SETGET_TMPL(LoadPalette, fgNewPalette)
OSD_REGION_SETGET_TMPL(InputWidth, u4Ihw)
OSD_REGION_SETGET_TMPL(InputHeight, u4Ivw)
OSD_REGION_SETGET_TMPL(LineSize, u4LineSize)
OSD_REGION_SETGET_TMPL(HStep, u4HStep)
OSD_REGION_SETGET_TMPL(VStep, u4VStep)
OSD_REGION_SETGET_TMPL(OutputHeight, u4Ovw)
OSD_REGION_SETGET_TMPL(OutputPosY, u4Ovs)
OSD_REGION_SETGET_TMPL(OutputWidth, u4Ohw)
OSD_REGION_SETGET_TMPL(OutputPosX, u4Ohs)
OSD_REGION_SETGET_TMPL(ColorKeyEnable, fgColorKeyEn)
OSD_REGION_SETGET_TMPL(ColorKey, u4ColorKey)
OSD_REGION_SETGET_TMPL(ColorMode, u4ColorMode)
OSD_REGION_SETGET_TMPL(BlendMode, u4MixSel)
OSD_REGION_SETGET_TMPL(HMirror, fgHMirror)
OSD_REGION_SETGET_TMPL(VFlip, fgVFlip)
OSD_REGION_SETGET_TMPL(DecMode, fgDecMode)
OSD_REGION_SETGET_TMPL(DecLine, fgDecLine)
OSD_REGION_SETGET_TMPL(DecEn, fgDecEn)
