/*
* Copyright (c) 2007-2021, Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
//!
//! \file      cm_device_rt_os.cpp
//! \brief     Contains Linux-dependent CmDeviceRT member functions.
//!

#include "cm_device_rt.h"

#include "cm_hal.h"
#include "cm_surface_manager.h"
#include "cm_mem.h"
#include "cm_surface_2d_rt.h"

extern int32_t CmFillMosResource(VASurfaceID, VADriverContext*, PMOS_RESOURCE);

namespace CMRT_UMD
{
//*-----------------------------------------------------------------------------
//| Purpose:    Constructor of CmDevice
//| Returns:    None.
//*-----------------------------------------------------------------------------
CmDeviceRT::CmDeviceRT(uint32_t options) : CmDeviceRTBase(options)
{
    ConstructOSSpecific(options);
}

//*-----------------------------------------------------------------------------
//| Purpose:    Destructor of CmDevice
//| Returns:    None.
//*-----------------------------------------------------------------------------
CmDeviceRT::~CmDeviceRT()
{
    m_mosContext->m_skuTable.reset();
    m_mosContext->m_waTable.reset();

    DestroyAuxDevice();
};

//*-----------------------------------------------------------------------------
//| Purpose:    Create Cm Device
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
int32_t CmDeviceRT::Create(MOS_CONTEXT *umdContext,
                           CmDeviceRT* &device,
                           uint32_t options)
{
    int32_t result = CM_FAILURE;

    if (device != nullptr)
    {
        // if the Cm Device exists
        device->Acquire();
        return CM_SUCCESS;
    }

    device = new (std::nothrow) CmDeviceRT(options);
    if (device)
    {
        device->Acquire(); // increase ref count
        result = device->Initialize(umdContext);
        if (result != CM_SUCCESS)
        {
            CM_ASSERTMESSAGE("Error: Failed to initialzie CmDevice.");
            CmDeviceRT::Destroy(device);
            device = nullptr;
        }
    }
    else
    {
        CM_ASSERTMESSAGE("Error: Failed to create CmDevice due to out of system memory.");
        result = CM_OUT_OF_HOST_MEMORY;
    }

    return result;
}

//*-----------------------------------------------------------------------------
//! Destroy the CmDevice_RT and kernels, samplers and the queue it created.
//! Also destroy all surfaces it created if the surface hasn't been explicitly destroyed.
//! Input :
//!     Reference to the pointer to the CmDevice_RT .
//! OUTPUT :
//!     CM_SUCCESS if CmDevice_RT is successfully destroyed.
//*-----------------------------------------------------------------------------
int32_t CmDeviceRT::Destroy(CmDeviceRT*& device)
{
    INSERT_API_CALL_LOG(device->GetHalState());

    int32_t result = CM_SUCCESS;
    int32_t refCount = device->Release();

    if (refCount == 0)
    {
        //check CmDevice remaining memory objects count and return 4bit encoded value
        result = device->CheckObjectCount();
        //destory remaining CM objects
        device->DestructCommon();

        CmSafeDelete(device);
    }

    //if return value is non-zero, return memory object counters 4 bit encoded value
    return result;
}



//*-----------------------------------------------------------------------------
//| Purpose:    Initialize the OS-Specific part in the Initialize() function
//| Returns:    None.
//*-----------------------------------------------------------------------------
int32_t CmDeviceRT::InitializeOSSpecific(MOS_CONTEXT *mosContext)
{
    return CreateAuxDevice( mosContext);
}

void CmDeviceRT::ConstructOSSpecific(uint32_t devCreateOption)
{
    m_pfnReleaseVaSurface = nullptr;

    // If use dynamic states.
    m_cmHalCreateOption.dynamicStateHeap = (devCreateOption & CM_DEVICE_CONFIG_DSH_DISABLE_MASK) ? false : true;
    if (m_cmHalCreateOption.dynamicStateHeap)
    {
        m_cmHalCreateOption.maxTaskNumber = 64;
    }
    return;
}

//*-----------------------------------------------------------------------------
//| Purpose:    Create Intel CM Device and Get maxValues and version of CM device
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
int32_t CmDeviceRT::CreateAuxDevice(MOS_CONTEXT *mosContext)  //VADriverContextP
{
    int32_t                     hr = CM_SUCCESS;
    PDDI_MEDIA_CONTEXT          mediaContext = nullptr;
    VAContextID                 vaCtxID;
    PCM_HAL_STATE               cmHalState;
    PCM_CONTEXT                 cmContext;

    m_mosContext = mosContext;

    CM_CHK_MOSSTATUS_GOTOFINISH_CMERROR(HalCm_Create(mosContext, &m_cmHalCreateOption, &cmHalState ));

    CM_CHK_MOSSTATUS_GOTOFINISH_CMERROR(cmHalState->pfnCmAllocate(cmHalState));

    // allocate cmContext
    cmContext = (PCM_CONTEXT)MOS_New(CM_CONTEXT);
    CM_CHK_NULL_GOTOFINISH_CMERROR(cmContext);
    cmContext->mosCtx     = *mosContext; // mos context
    cmContext->cmHalState = cmHalState;

    m_accelData =  (void *)cmContext;

    CM_CHK_CMSTATUS_GOTOFINISH_WITH_MSG(GetMaxValueFromCaps(m_halMaxValues, m_halMaxValuesEx), "Failed to get Max values.");
    CM_CHK_CMSTATUS_GOTOFINISH_WITH_MSG(GetGenPlatform(m_platform), "Failed to get GPU type.");

    //  Get version from Driver
    m_ddiVersion = CM_VERSION;

finish:
    return hr;
}

//*-----------------------------------------------------------------------------
//| Purpose:    Destory Intel Aux Device : CM device
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
int32_t CmDeviceRT::DestroyAuxDevice()
{
    PCM_CONTEXT_DATA  cmData = (PCM_CONTEXT_DATA)m_accelData;

    // Delete VPHAL State
    if (cmData && cmData->cmHalState)
    {
        cmData->mosCtx.m_skuTable.reset();
        cmData->mosCtx.m_waTable.reset();
        HalCm_Destroy(cmData->cmHalState);
        // Delete CM Data itself
        MOS_Delete(cmData);

    }

    return CM_SUCCESS;
}

//*-----------------------------------------------------------------------------
//| Purpose:    Create Surface 2D
//| Arguments :
//|               VASurfaceID :     [in]     index to MEDIASURFACE[], same as LIBVA SurfaceID
//|               umdContext           [in]     Va driver context
//|               surface          [out]    Reference to Pointer to CmSurface2D
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
CM_RT_API int32_t CmDeviceRT::CreateSurface2D(VASurfaceID vaSurface,
                                              VADriverContext *vaDriverCtx,
                                              CmSurface2D* & surface)
{
    INSERT_API_CALL_LOG(GetHalState());

    MOS_RESOURCE mosResource;
    int32_t hr = CmFillMosResource( vaSurface, vaDriverCtx, &mosResource);
    if( hr != CM_SUCCESS)
    {
        CM_ASSERTMESSAGE("Error: Failed to fill MOS resource.");
        return hr;
    }

    CmSurface2DRT *surfaceRT = nullptr;
    hr = m_surfaceMgr->CreateSurface2DFromMosResource(&mosResource, false, surfaceRT);
    surface = surfaceRT;
    return hr;
}

//*----------------------------------------------------------------------------
//| Purpose:    Get JIT Compiler function from igfxcmjit64/32.dll
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
int32_t CmDeviceRT::GetJITCompileFnt(pJITCompile &jitCompile)
{
    if (m_fJITCompile)
    {
        jitCompile = m_fJITCompile;
    }
    else
    {
        int ret = LoadJITDll();
        if (ret != CM_SUCCESS)
        {
            return ret;
        }
        jitCompile = m_fJITCompile;
    }
    return CM_SUCCESS;
}

int32_t CmDeviceRT::GetJITCompileFntV2(pJITCompile_v2 &jitCompile_v2)
{
    if (m_fJITCompile_v2)
    {
        jitCompile_v2 = m_fJITCompile_v2;
    }
    else
    {
        int ret = LoadJITDll();
        if (ret != CM_SUCCESS)
        {
            return ret;
        }
        jitCompile_v2 = m_fJITCompile_v2;
    }
    return CM_SUCCESS;
}


//*----------------------------------------------------------------------------
//| Purpose:    Get JIT Free Block function from igfxcmjit64/32.dll
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
int32_t CmDeviceRT::GetFreeBlockFnt(pFreeBlock &freeBlock)
{
    if (m_fFreeBlock)
    {
        freeBlock = m_fFreeBlock;
    }
    else
    {
        int ret = LoadJITDll();
        if (ret != CM_SUCCESS)
        {
            return ret;
        }
        freeBlock = m_fFreeBlock;
    }
    return CM_SUCCESS;
}

//*----------------------------------------------------------------------------
//| Purpose:    Get JIT Version function from igfxcmjit64/32.dll, It used to get
//|             version from common isa
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
int32_t CmDeviceRT::GetJITVersionFnt(pJITVersion &jitVersion)
{
    if (m_fJITVersion)
    {
        jitVersion = m_fJITVersion;
    }
    else
    {
        int ret = LoadJITDll();
        if (ret != CM_SUCCESS)
        {
            return ret;
        }
        jitVersion = m_fJITVersion;
    }
    return CM_SUCCESS;
}

//*----------------------------------------------------------------------------
//| Purpose:    Get all JIT  functions from igfxcmjit64/32.dll.
//|
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
int32_t CmDeviceRT::LoadJITDll()
{
    int result = 0;

    if (nullptr == m_hJITDll)
    {
        m_hJITDll = dlopen( "libigc.so", RTLD_LAZY );
        if (nullptr == m_hJITDll)
        {
            CM_NORMALMESSAGE("Warning: Failed to load IGC library, will try JIT library.");
            if (sizeof(void *) == 4)  //32-bit
            {
                m_hJITDll = dlopen( "igfxcmjit32.so", RTLD_LAZY );
            }
            else  //64-bit
            {
                m_hJITDll = dlopen( "igfxcmjit64.so", RTLD_LAZY );
            }
        }
        if (nullptr == m_hJITDll)
        {
            result = CM_JITDLL_LOAD_FAILURE;
            CM_ASSERTMESSAGE("Error: Failed to load either IGC or JIT library.");
            return result;
        }
        if ((nullptr == m_fJITCompile && nullptr == m_fJITCompile_v2) || nullptr == m_fFreeBlock || nullptr == m_fJITVersion)
        {
            m_fJITCompile = (pJITCompile)MosUtilities::MosGetProcAddress(m_hJITDll, JITCOMPILE_FUNCTION_STR);
            m_fJITCompile_v2 = (pJITCompile_v2)MosUtilities::MosGetProcAddress(m_hJITDll, JITCOMPILEV2_FUNCTION_STR);
            m_fFreeBlock = (pFreeBlock)MosUtilities::MosGetProcAddress(m_hJITDll, FREEBLOCK_FUNCTION_STR);
            m_fJITVersion = (pJITVersion)MosUtilities::MosGetProcAddress(m_hJITDll, JITVERSION_FUNCTION_STR);
        }

        if ((nullptr == m_fJITCompile && nullptr == m_fJITCompile_v2) || (nullptr == m_fFreeBlock) || (nullptr == m_fJITVersion))
        {
            result = CM_JITDLL_LOAD_FAILURE;
            CM_ASSERTMESSAGE("Error: Failed to get JIT functions.");
            return result;
        }
    }

    return result;
}

//*-----------------------------------------------------------------------------
//| Purpose:    Get the GPU Infomations from Internal
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
CM_RETURN_CODE CmDeviceRT::QueryGPUInfoInternal(PCM_QUERY_CAPS queryCaps)
{
    PCM_CONTEXT_DATA        cmData;
    PCM_HAL_STATE           cmHalState;
    CM_RETURN_CODE          hr = CM_SUCCESS;

    cmData = (PCM_CONTEXT_DATA)GetAccelData();
    CM_CHK_NULL_GOTOFINISH_CMERROR(cmData);

    cmHalState = cmData->cmHalState;
    CM_CHK_NULL_GOTOFINISH_CMERROR(cmHalState);

    switch(queryCaps->type)
    {
        case CM_QUERY_GPU:
            queryCaps->genCore = cmHalState->platform.eRenderCoreFamily;
            break;

        case CM_QUERY_GT:
            cmHalState->cmHalInterface->GetGenPlatformInfo(nullptr, &queryCaps->genGT, nullptr);
            break;

        case CM_QUERY_MIN_RENDER_FREQ:
            queryCaps->minRenderFreq = 0;
            break;

        case CM_QUERY_MAX_RENDER_FREQ:
            queryCaps->maxRenderFreq = 0;
            break;

        case CM_QUERY_STEP:
            queryCaps->genStepId = cmHalState->platform.usRevId;
            break;

        case CM_QUERY_GPU_FREQ:
            CM_CHK_MOSSTATUS_GOTOFINISH_CMERROR(cmHalState->pfnGetGPUCurrentFrequency(cmHalState, &queryCaps->gpuCurrentFreq));
            break;

        default:
            hr = CM_FAILURE;
            goto finish;
        }
finish:
    return hr;
}

//*-----------------------------------------------------------------------------
//| Purpose:    Get the supported formats in Surface2D from Internal
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
CM_RETURN_CODE
CmDeviceRT::QuerySurface2DFormatsInternal(PCM_QUERY_CAPS queryCaps)
{
    if (queryCaps->surface2DFormats)
    {
        CM_SURFACE_FORMAT formats[ CM_MAX_SURFACE2D_FORMAT_COUNT_INTERNAL ] =
        {
            CM_SURFACE_FORMAT_X8R8G8B8,
            CM_SURFACE_FORMAT_A8R8G8B8,
            CM_SURFACE_FORMAT_A8B8G8R8,
            CM_SURFACE_FORMAT_R32F,
            CM_SURFACE_FORMAT_V8U8,
            CM_SURFACE_FORMAT_P8,
            CM_SURFACE_FORMAT_YUY2,
            CM_SURFACE_FORMAT_A8,
            CM_SURFACE_FORMAT_NV12,
            CM_SURFACE_FORMAT_P010,
            CM_SURFACE_FORMAT_P016,
            CM_SURFACE_FORMAT_Y216,
            CM_SURFACE_FORMAT_Y416,
            CM_SURFACE_FORMAT_UYVY,
            CM_SURFACE_FORMAT_V8U8,
            CM_SURFACE_FORMAT_Y8_UNORM,
            CM_SURFACE_FORMAT_YV12,
            CM_SURFACE_FORMAT_R8_UINT,
            CM_SURFACE_FORMAT_R16_UINT,
            CM_SURFACE_FORMAT_P208,
            CM_SURFACE_FORMAT_AYUV,
            CM_SURFACE_FORMAT_Y210,
            CM_SURFACE_FORMAT_Y410,
        };
        CmSafeMemCopy( queryCaps->surface2DFormats, formats, CM_MAX_SURFACE2D_FORMAT_COUNT_INTERNAL  * sizeof( GMM_RESOURCE_FORMAT ) );
    }
    else
        return CM_FAILURE;

    return CM_SUCCESS;
}

//*-----------------------------------------------------------------------------
//| Purpose:   Report all the supported formats for surface2D
//| Returns:    No
//*-----------------------------------------------------------------------------
int32_t CmDeviceRT::QuerySurface2DFormats(void *capValue,
                                          uint32_t & capValueSize)
{
    if( capValueSize >= CM_MAX_SURFACE2D_FORMAT_COUNT  * sizeof( GMM_RESOURCE_FORMAT ) )
    {
        capValueSize = CM_MAX_SURFACE2D_FORMAT_COUNT  * sizeof( GMM_RESOURCE_FORMAT ) ;
        CM_SURFACE_FORMAT formats[ CM_MAX_SURFACE2D_FORMAT_COUNT ] =
        {
            CM_SURFACE_FORMAT_X8R8G8B8,
            CM_SURFACE_FORMAT_A8R8G8B8,
            CM_SURFACE_FORMAT_A8B8G8R8,
            CM_SURFACE_FORMAT_R32F,
            CM_SURFACE_FORMAT_V8U8,
            CM_SURFACE_FORMAT_P8,
            CM_SURFACE_FORMAT_YUY2,
            CM_SURFACE_FORMAT_A8,
            CM_SURFACE_FORMAT_NV12,
            CM_SURFACE_FORMAT_P010,
            CM_SURFACE_FORMAT_P016,
            CM_SURFACE_FORMAT_Y216,
            CM_SURFACE_FORMAT_Y416,
            CM_SURFACE_FORMAT_UYVY,
            CM_SURFACE_FORMAT_IMC3,
            CM_SURFACE_FORMAT_411P,
            CM_SURFACE_FORMAT_411R,
            CM_SURFACE_FORMAT_422H,
            CM_SURFACE_FORMAT_422V,
            CM_SURFACE_FORMAT_444P,
            CM_SURFACE_FORMAT_RGBP,
            CM_SURFACE_FORMAT_BGRP,
            CM_SURFACE_FORMAT_YV12,
            CM_SURFACE_FORMAT_R8_UINT,
            CM_SURFACE_FORMAT_R16_UINT,
            CM_SURFACE_FORMAT_P208,
            CM_SURFACE_FORMAT_AYUV,
            CM_SURFACE_FORMAT_Y210,
            CM_SURFACE_FORMAT_Y410,
        };
        CmSafeMemCopy( capValue, formats, capValueSize );
        return CM_SUCCESS;
    }
    else
    {
        return CM_FAILURE;
    }
}

//*-----------------------------------------------------------------------------
//| Purpose:    Set CM Context ID in MediaContext
//| Arguments : Context ID
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
int32_t CmDeviceRT::SetVaCtxID(uint32_t vaCtxID)
{
    m_vaCtxID = vaCtxID;
    return CM_SUCCESS;
}

//*-----------------------------------------------------------------------------
//| Purpose:    Get CM Context ID in MediaContext
//| Arguments : Context ID
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
int32_t CmDeviceRT::GetVaCtxID(uint32_t &vaCtxID)
{
    vaCtxID = m_vaCtxID;
    return CM_SUCCESS;
}

int32_t CmDeviceRT::RegisterCallBack(pCallBackReleaseVaSurface callBack)
{
    m_pfnReleaseVaSurface = callBack;
    return CM_SUCCESS;
}

int32_t CmDeviceRT::ReleaseVASurface(void *vaDisplay, void *vaSurfaceID)
{
    if(m_pfnReleaseVaSurface)
    {
        m_pfnReleaseVaSurface( vaDisplay, vaSurfaceID);
    }

    return CM_SUCCESS;
}

int32_t CmDeviceRT::ReadVtuneProfilingFlag()
{
    //Aggrement with Vtune: <user home dir>/.mdf_trace
    //if .mdf_trace does not exist, vtune log is off
    //if .mdf_trace exists, read string "Output=<hexmask>"
    //hexmask = 1 : enable; hexmask = 0: disable
    m_vtuneOn = false;

    char *homeStr = getenv("HOME");
    if (homeStr == nullptr)
    {
       //Even homeStr is not found, this function returns success.
       //m_vtuneOn is still false.
       return CM_SUCCESS;
    }

    char traceFile[256];
    int offset = snprintf(traceFile, 256, "%s", homeStr);
    snprintf(traceFile+offset, 256-offset, "%s", "/.mdf_trace");

    FILE *traceFd = fopen(traceFile, "r");
    int flag = 0;
    if(traceFd )
    {
      //read data from file
      int ret = fscanf(traceFd, "Output=%d", &flag);
      if(ret >=0 && flag == 1)
      {
         m_vtuneOn = true;
      }
      fclose(traceFd);
    }

    //Set flag in cm hal layer
    PCM_CONTEXT_DATA cmData = (PCM_CONTEXT_DATA)this->GetAccelData();
    PCM_HAL_STATE cmHalState = cmData->cmHalState;
    cmHalState->pfnSetVtuneProfilingFlag(cmHalState, m_vtuneOn);

    return CM_SUCCESS;
}

//*-----------------------------------------------------------------------------
//| Purpose:    Create shared Surface 2D (OS agnostic)
//| Arguments :
//|               mosResource      [in]     Pointer to Mos resource
//|               surface          [out]    Reference to Pointer to CmSurface2D
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
CM_RT_API int32_t CmDeviceRT::CreateSurface2D(PMOS_RESOURCE mosResource,
                                              CmSurface2D* & surface)
{
    INSERT_API_CALL_LOG(GetHalState());

    if (mosResource == nullptr)
    {
        return CM_INVALID_MOS_RESOURCE_HANDLE;
    }

    CLock locker(m_criticalSectionSurface);

    CmSurface2DRT *surfaceRT = nullptr;
    int ret = m_surfaceMgr->CreateSurface2DFromMosResource(mosResource, false, surfaceRT);
    surface = surfaceRT;
    return ret;
}

//*-----------------------------------------------------------------------------
//| Purpose:    Create Surface 2D
//| Arguments :   width             [in]     width of the  CmSurface2D
//|               height            [in]     height of the CmSurface2D
//|               format            [in]     format of the CmSurface2D
//|               surface          [in/out]    Reference to Pointer to CmSurface2D
//| Returns:    Result of the operation.
//*-----------------------------------------------------------------------------
CM_RT_API int32_t CmDeviceRT::CreateSurface2D(uint32_t width,
                                              uint32_t height,
                                              CM_SURFACE_FORMAT format,
                                              CmSurface2D* & surface)
{
    INSERT_API_CALL_LOG(GetHalState());

    CLock locker(m_criticalSectionSurface);

    CmSurface2DRT *surfaceRT = nullptr;
    int ret = m_surfaceMgr->CreateSurface2D(width, height, 0, true, format, surfaceRT);
    surface = surfaceRT;
    return ret;
}

//*-----------------------------------------------------------------------------
//| Purpose:    Create Surface 2D
//| NOTE: Called by CM Wrapper, from CMRT Thin
//*-----------------------------------------------------------------------------
int32_t CmDeviceRT::CreateSurface2D(PMOS_RESOURCE mosResource,
                                    bool isCmCreated,
                                    CmSurface2D* & surface)
{
    INSERT_API_CALL_LOG(GetHalState());

    if (mosResource == nullptr)
    {
        return CM_INVALID_MOS_RESOURCE_HANDLE;
    }

    CLock locker(m_criticalSectionSurface);

    CmSurface2DRT *surfaceRT = nullptr;
    int ret = m_surfaceMgr->CreateSurface2DFromMosResource(mosResource, isCmCreated, surfaceRT);
    surface = surfaceRT;
    return ret;
}
}  // namespace
