/*
* Copyright (c) 2017-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        hwinfo_linux.c 
//! \brief     The purpose of the file is to get the sku/wa table according to platform information. 
//!

#include "hwinfo_linux.h"
#include "mos_utilities.h"
#include "mos_util_debug.h"
#include "skuwa_factory.h"
#include "linux_system_info.h"
#include "linux_shadow_skuwa.h"
#include "mos_solo_generic.h"


typedef DeviceInfoFactory<struct GfxDeviceInfo> DeviceInfoFact;
typedef DeviceInfoFactory<struct LinuxDeviceInit> DeviceInitFact;

static GfxDeviceInfo *getDeviceInfo(uint32_t devId)
{
    GfxDeviceInfo *devInfo = DeviceInfoFact::LookupDevice(devId);

    return devInfo;
}

static LinuxDeviceInit *getDeviceInit(uint32_t platKey)
{
    LinuxDeviceInit *devInfo = DeviceInitFact::LookupDevice(platKey);

    return devInfo;
}

static bool MediaGetParam(int fd, int32_t param, uint32_t *retValue)
{
    struct drm_i915_getparam gp;

    gp.param = param;
    gp.value = (int32_t *)retValue;
    return drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp) == 0;
}

#ifdef _MEDIA_RESERVED
/*****************************************************************************\
Description:
    Get IpGmdID for separate IP products according to input engine_class

Input:
    fd              - file descriptor to the /dev/dri/cardX
    engine_class    - engine class
Output:
    ipVerInfo       - GmdID info describing current IP version info
\*****************************************************************************/
static MOS_STATUS MediaGetIpGmdID(int32_t       fd,
                          MOS_BUFMGR            *pDrmBufMgr,
                          uint16_t              engineClass,
                          unsigned int          maxNengine,
                          GFX_GMD_ID            *ipVerInfo)
{
    unsigned int nengine = maxNengine;
    struct i915_engine_class_instance *uengines = nullptr;

    if ((fd < 0) || (pDrmBufMgr == nullptr) || (maxNengine == 0) || (ipVerInfo == nullptr))
    {
        MOS_OS_ASSERTMESSAGE("Invalid parameter \n");
        return MOS_STATUS_INVALID_PARAMETER;
    }

    uengines = (struct i915_engine_class_instance *)MOS_AllocAndZeroMemory(nengine * sizeof(struct i915_engine_class_instance));
    MOS_OS_CHK_NULL_RETURN(uengines);
    if (mos_query_engines(pDrmBufMgr, engineClass, 0, &nengine, uengines) || (nengine == 0))
    {
        MOS_OS_ASSERTMESSAGE("Failed to query engine\n");
        MOS_SafeFreeMemory(uengines);
        return MOS_STATUS_PLATFORM_NOT_SUPPORTED;
    }

    if (mos_query_hw_ip_version(fd, uengines[0], (void *)ipVerInfo))
    {
        MOS_OS_ASSERTMESSAGE("Failed to query hw_ip_version\n");
        MOS_SafeFreeMemory(uengines);
        return MOS_STATUS_PLATFORM_NOT_SUPPORTED;
    }

    MOS_SafeFreeMemory(uengines);

    return MOS_STATUS_SUCCESS;
}
#endif
/*****************************************************************************\
Description:
    Get ProductFamily according to input device FD

Input:
    fd              - file descriptor to the /dev/dri/cardX
Output:
    eProductFamily  - describing current platform product family
\*****************************************************************************/
MOS_STATUS HWInfo_GetGfxProductFamily(int32_t fd, PRODUCT_FAMILY &eProductFamily)
{
    if (fd < 0)
    {
        MOS_OS_ASSERTMESSAGE("Invalid parameter \n");
        return MOS_STATUS_INVALID_PARAMETER;
    }
    LinuxDriverInfo drvInfo = {18, 3, 0, 23172, 3, 1, 0, 1, 0, 0, 1, 0};
    if (HWInfoGetLinuxDrvInfo(fd, &drvInfo) != MOS_STATUS_SUCCESS)
    {
        MOS_OS_ASSERTMESSAGE("Failed to get the chipset id\n");
        return MOS_STATUS_INVALID_HANDLE;
    }
    GfxDeviceInfo *devInfo = getDeviceInfo(drvInfo.devId);
    if (devInfo == nullptr)
    {
        MOS_OS_ASSERTMESSAGE("Failed to get the device info for Device id: %x\n", drvInfo.devId);
        return MOS_STATUS_PLATFORM_NOT_SUPPORTED;
    }
    eProductFamily = (PRODUCT_FAMILY)devInfo->productFamily;
    return MOS_STATUS_SUCCESS;
}


/*****************************************************************************\
Description:
    Get Sku/Wa tables and platform information according to input device FD

Input:
    fd         - file descriptor to the /dev/dri/cardX
Output:
    gfxPlatform  - describing current platform. is it mobile, desk,
                   server? Sku/Wa must know.
    skuTable     - describing SKU
    waTable      - the constraints list
    gtSystemInfo - describing current system information
    userSettingPtr - shared pointer to user setting instance
\*****************************************************************************/
MOS_STATUS HWInfo_GetGfxInfo(int32_t           fd,
                          MOS_BUFMGR           *pDrmBufMgr,
                          PLATFORM             *gfxPlatform,
                          MEDIA_FEATURE_TABLE  *skuTable,
                          MEDIA_WA_TABLE       *waTable,
                          MEDIA_SYSTEM_INFO    *gtSystemInfo,
                          MediaUserSettingSharedPtr userSettingPtr)
{
    if ((fd < 0) ||
        (pDrmBufMgr == nullptr) ||
        (gfxPlatform == nullptr) ||
        (skuTable == nullptr) ||
        (waTable == nullptr) ||
        (gtSystemInfo == nullptr))
    {
        MOS_OS_ASSERTMESSAGE("Invalid parameter \n");
        return MOS_STATUS_INVALID_PARAMETER;
    }

#if (_DEBUG || _RELEASE_INTERNAL)
    MOS_USER_FEATURE_VALUE_DATA         UserFeatureData;
#endif

    LinuxDriverInfo drvInfo = {18, 3, 0, 23172, 3, 1, 0, 1, 0, 0, 1, 0, 0};
    if (!Mos_Solo_IsEnabled(nullptr) && HWInfoGetLinuxDrvInfo(fd, &drvInfo) != MOS_STATUS_SUCCESS)
    {
        MOS_OS_ASSERTMESSAGE("Failed to get the chipset id\n");
        return MOS_STATUS_INVALID_HANDLE;
    }

    GfxDeviceInfo *devInfo = getDeviceInfo(drvInfo.devId);
    if (devInfo == nullptr)
    {
        MOS_OS_ASSERTMESSAGE("Failed to get the device info for Device id: %x\n", drvInfo.devId);
        return MOS_STATUS_PLATFORM_NOT_SUPPORTED;
    }
    /* Initialize Platform Info */
    gfxPlatform->ePlatformType      = (PLATFORM_TYPE)devInfo->platformType;
    gfxPlatform->eProductFamily     = (PRODUCT_FAMILY)devInfo->productFamily;
    gfxPlatform->ePCHProductFamily  = PCH_UNKNOWN;
    gfxPlatform->eDisplayCoreFamily = (GFXCORE_FAMILY)devInfo->displayFamily;
    gfxPlatform->eRenderCoreFamily  = (GFXCORE_FAMILY)devInfo->renderFamily;
    gfxPlatform->eGTType            = (GTTYPE)devInfo->eGTType;
    gfxPlatform->usDeviceID         = drvInfo.devId;
    gfxPlatform->usRevId            = drvInfo.devRev;

    if (mos_query_device_blob(fd, gtSystemInfo) == 0)
    {
        gtSystemInfo->EUCount           = gtSystemInfo->SubSliceCount * gtSystemInfo->MaxEuPerSubSlice;
        gtSystemInfo->ThreadCount       = gtSystemInfo->EUCount * gtSystemInfo->NumThreadsPerEu;
        gtSystemInfo->VEBoxInfo.IsValid = true;
        gtSystemInfo->VDBoxInfo.IsValid = true;
    }
    else
    {
        MOS_OS_NORMALMESSAGE("Device blob query is not supported yet.\n");
        gtSystemInfo->SliceCount        = drvInfo.sliceCount;
        gtSystemInfo->SubSliceCount     = drvInfo.subSliceCount;
        gtSystemInfo->EUCount           = drvInfo.euCount;

        if (devInfo->InitMediaSysInfo &&
            devInfo->InitMediaSysInfo(devInfo, gtSystemInfo))
        {
            MOS_OS_NORMALMESSAGE("Init Media SystemInfo\n");
        }
        else
        {
            MOS_OS_ASSERTMESSAGE("Failed to Init Gt System Info\n");
            return MOS_STATUS_PLATFORM_NOT_SUPPORTED;
        }
    }

    unsigned int maxNengine = 0;
    if((gtSystemInfo->VDBoxInfo.NumberOfVDBoxEnabled == 0)
        || (gtSystemInfo->VEBoxInfo.NumberOfVEBoxEnabled == 0))
    {
        if (mos_query_engines_count(pDrmBufMgr, &maxNengine) || (maxNengine == 0))
        {
            MOS_OS_ASSERTMESSAGE("Failed to query engines count.\n");
            return MOS_STATUS_PLATFORM_NOT_SUPPORTED;
        }
    }

    if (gtSystemInfo->VDBoxInfo.NumberOfVDBoxEnabled == 0)
    {
        unsigned int nengine = maxNengine;
        struct i915_engine_class_instance *uengines = nullptr;
        uengines = (struct i915_engine_class_instance *)MOS_AllocAndZeroMemory(nengine * sizeof(struct i915_engine_class_instance));
        MOS_OS_CHK_NULL_RETURN(uengines);
        if (mos_query_engines(pDrmBufMgr,I915_ENGINE_CLASS_VIDEO,0,&nengine,uengines) == 0)
        {
            gtSystemInfo->VDBoxInfo.NumberOfVDBoxEnabled = nengine;
        }
        else
        {
            MOS_OS_ASSERTMESSAGE("Failed to query vdbox engine\n");
            MOS_SafeFreeMemory(uengines);
            return MOS_STATUS_PLATFORM_NOT_SUPPORTED;
        }
        for (int i=0; i<nengine; i++)
        {
            gtSystemInfo->VDBoxInfo.Instances.VDBoxEnableMask |= 1<<uengines[i].engine_instance;
        }
        MOS_SafeFreeMemory(uengines);
    }

    if (gtSystemInfo->VEBoxInfo.NumberOfVEBoxEnabled == 0)
    {
        unsigned int nengine = maxNengine;
        struct i915_engine_class_instance *uengines = nullptr;
        uengines = (struct i915_engine_class_instance *)MOS_AllocAndZeroMemory(nengine * sizeof(struct i915_engine_class_instance));
        MOS_OS_CHK_NULL_RETURN(uengines);
        if (mos_query_engines(pDrmBufMgr,I915_ENGINE_CLASS_VIDEO_ENHANCE,0,&nengine,uengines) == 0)
        {
            MOS_OS_ASSERT(nengine <= maxNengine);
            gtSystemInfo->VEBoxInfo.NumberOfVEBoxEnabled = nengine;
        }
        else
        {
            MOS_OS_ASSERTMESSAGE("Failed to query vebox engine\n");
            MOS_SafeFreeMemory(uengines);
            return MOS_STATUS_PLATFORM_NOT_SUPPORTED;
        }
        MOS_SafeFreeMemory(uengines);
    }

    uint32_t platformKey = devInfo->productFamily;
    LinuxDeviceInit *devInit = getDeviceInit(platformKey);

    if (devInit && devInit->InitMediaFeature &&
        devInit->InitMediaWa &&
        devInit->InitMediaFeature(devInfo, skuTable, &drvInfo, userSettingPtr) &&
        devInit->InitMediaWa(devInfo, waTable, &drvInfo))
    {
#ifdef _MEDIA_RESERVED
        MOS_OS_NORMALMESSAGE("Init Media SKU/WA info successfully\n");
        if (MEDIA_IS_SKU(skuTable, FtrMediaIPSeparation))
        {
            gfxPlatform->eMediaCoreFamily = (GFXCORE_FAMILY)devInfo->mediaFamily;

            if (0 == gfxPlatform->sMediaBlockID.Value)
            {
                if (MediaGetIpGmdID(fd, pDrmBufMgr, I915_ENGINE_CLASS_VIDEO, maxNengine, &(gfxPlatform->sMediaBlockID)))
                {
                    MOS_OS_ASSERTMESSAGE("Failed to query vdbox engine GmdID\n");
                    return MOS_STATUS_PLATFORM_NOT_SUPPORTED;
                }
                else
                {
                    MOS_OS_NORMALMESSAGE("Media mediaRevID arch:%d, release:%d, RevisionID:%d\n", gfxPlatform->sMediaBlockID.GmdID.GMDArch,
                                        gfxPlatform->sMediaBlockID.GmdID.GMDRelease, gfxPlatform->sMediaBlockID.GmdID.RevisionID);
                }
            }

            if (0 == gfxPlatform->sRenderBlockID.Value)
            {
                if (MediaGetIpGmdID(fd, pDrmBufMgr, I915_ENGINE_CLASS_RENDER, maxNengine, &(gfxPlatform->sRenderBlockID)))
                {
                    MOS_OS_ASSERTMESSAGE("Failed to query render GmdID\n");
                    return MOS_STATUS_PLATFORM_NOT_SUPPORTED;
                }

                else
                {
                    MOS_OS_NORMALMESSAGE("Media sRenderBlockID arch:%d, release:%d, RevisionID:%d\n", gfxPlatform->sRenderBlockID.GmdID.GMDArch,
                                        gfxPlatform->sRenderBlockID.GmdID.GMDRelease, gfxPlatform->sRenderBlockID.GmdID.RevisionID);
                }
            }
        }
#endif
    }
    else
    {
        MOS_OS_ASSERTMESSAGE("Failed to Init SKU/WA Info\n");
        return MOS_STATUS_PLATFORM_NOT_SUPPORTED;
    }

    uint32_t devExtKey = platformKey + MEDIA_EXT_FLAG;
    LinuxDeviceInit *devExtInit = getDeviceInit(devExtKey);

    /* The initializationof Ext SKU/WA is optional. So skip the check of return value */
    if (devExtInit && devExtInit->InitMediaFeature &&
        devExtInit->InitMediaWa &&
        devExtInit->InitMediaFeature(devInfo, skuTable, &drvInfo, userSettingPtr) &&
        devExtInit->InitMediaWa(devInfo, waTable, &drvInfo))
    {
        MOS_OS_NORMALMESSAGE("Init Media SystemInfo successfully\n");
    }
    if (drvInfo.isServer)
    {
        mos_set_platform_information(pDrmBufMgr, PLATFORM_INFORMATION_IS_SERVER);
    }

    /* disable it on Linux */
    MEDIA_WR_SKU(skuTable, FtrPerCtxtPreemptionGranularityControl, 0);
    MEDIA_WR_SKU(skuTable, FtrMediaThreadGroupLevelPreempt, 0);
    MEDIA_WR_SKU(skuTable, FtrMediaMidBatchPreempt, 0);
    MEDIA_WR_SKU(skuTable, FtrMediaMidThreadLevelPreempt, 0);
    MEDIA_WR_SKU(skuTable, FtrGpGpuThreadGroupLevelPreempt, 0);
    MEDIA_WR_SKU(skuTable, FtrGpGpuMidBatchPreempt, 0);
    MEDIA_WR_SKU(skuTable, FtrGpGpuMidThreadLevelPreempt, 0);

#if (_DEBUG || _RELEASE_INTERNAL)
    // User feature read to detect if simulation environment (MediaSolo) is enabled
    MOS_ZeroMemory(&UserFeatureData, sizeof(UserFeatureData));
    MOS_UserFeature_ReadValue_ID(
        nullptr,
        __MEDIA_USER_FEATURE_VALUE_MEDIASOLO_ENABLE_ID,
        &UserFeatureData,
        (MOS_CONTEXT_HANDLE)nullptr);
    //Disable plarform checks to support pre-si features (e.g., MMCD) on MediaSolo
    MEDIA_WR_WA(waTable, WaDisregardPlatformChecks, (UserFeatureData.i32Data) ? true : false);
#endif

    return MOS_STATUS_SUCCESS;
}

#ifndef I915_PARAM_HAS_BSD2
#define LOCAL_I915_PARAM_HAS_BSD2 I915_PARAM_HAS_BSD2
#else
#define LOCAL_I915_PARAM_HAS_BSD2 31
#endif

#ifdef I915_PARAM_HUC_STATUS
#define LOCAL_I915_PARAM_HAS_HUC I915_PARAM_HUC_STATUS
#else
#define LOCAL_I915_PARAM_HAS_HUC 42
#endif

#ifdef I915_PARAM_EU_TOTAL
#define LOCAL_I915_PARAM_EU_TOTAL I915_PARAM_EU_TOTAL
#else
#define LOCAL_I915_PARAM_EU_TOTAL 34
#endif

#ifdef I915_PARAM_SUBSLICE_TOTAL
#define LOCAL_I915_PARAM_SUBSLICE I915_PARAM_SUBSLICE_TOTAL
#else
#define LOCAL_I915_PARAM_SUBSLICE 33
#endif

#ifdef I915_PARAM_REVISION
#define LOCAL_I915_PARAM_REVISION I915_PARAM_REVISION
#else
#define LOCAL_I915_PARAM_REVISION 32
#endif

MOS_STATUS HWInfoGetLinuxDrvInfo(int fd, struct LinuxDriverInfo *drvInfo)
{
    if ((fd < 0) || (drvInfo == nullptr))
    {
        return MOS_STATUS_INVALID_HANDLE;
    }

    uint32_t retValue = 0;

    drvInfo->hasBsd = 0;
    if (MediaGetParam(fd, I915_PARAM_HAS_BSD, &retValue))
    {
        drvInfo->hasBsd = !!retValue;
    }

    drvInfo->hasBsd2 = 0;
    retValue = 0;
    if (MediaGetParam(fd, LOCAL_I915_PARAM_HAS_BSD2, &retValue))
    {
        drvInfo->hasBsd2 = !!retValue;
    }

    drvInfo->hasVebox = 0;
    retValue = 0;
    if (MediaGetParam(fd, I915_PARAM_HAS_VEBOX, &retValue))
    {
        drvInfo->hasVebox = !!retValue;
    }

    drvInfo->hasPpgtt = 1;
    retValue = 0;
    if (MediaGetParam(fd, I915_PARAM_HAS_ALIASING_PPGTT, &retValue))
    {
        drvInfo->hasPpgtt = !!retValue;
    }

    drvInfo->hasHuc = 0;
    retValue = 0;
    if (MediaGetParam(fd, LOCAL_I915_PARAM_HAS_HUC, &retValue))
    {
        drvInfo->hasHuc = !!retValue;
    }

    drvInfo->devId = 0;
    retValue = 0;
    if (MediaGetParam(fd, I915_PARAM_CHIPSET_ID, &retValue))
    {
        drvInfo->devId = retValue;
    }
    drvInfo->devRev = 0;
    retValue = 0;
    if (MediaGetParam(fd, LOCAL_I915_PARAM_REVISION, &retValue))
    {
        drvInfo->devRev = retValue;
    }

    drvInfo->euCount = 0;
    retValue = 0;
    if (MediaGetParam(fd, LOCAL_I915_PARAM_EU_TOTAL, &retValue))
    {
        drvInfo->euCount = retValue;
    }

    drvInfo->subSliceCount = 0;
    retValue = 0;
    if (MediaGetParam(fd, LOCAL_I915_PARAM_SUBSLICE, &retValue))
    {
        drvInfo->subSliceCount = retValue;
    }

    // There is no interface to read total slice count from drm/i915, so we
    // will set the slice count in InitMediaSysInfo accordint to Device ID.
    drvInfo->sliceCount = 0;

    return MOS_STATUS_SUCCESS;
}

/*****************************************************************************\
Description:
    Get the required Sku/Wa tables for GMM and platform information  based on device Fd

Input:
    fd         - file descriptor to the /dev/dri/cardX
Output:
     SHADOW_MEDIA_FEATURE_TABLE  *shadowSkuTable
     SHADOW_MEDIA_WA_TABLE      *shadowWaTable,
     MEDIA_SYSTEM_INFO       *systemInfo
\*****************************************************************************/
MOS_STATUS HWInfo_GetGmmInfo(int32_t                 fd,
                          SHADOW_MEDIA_FEATURE_TABLE *shadowSkuTable,
                          SHADOW_MEDIA_WA_TABLE      *shadowWaTable,
                          MEDIA_SYSTEM_INFO          *systemInfo)
{
    if ((fd < 0) ||
        (shadowSkuTable == nullptr) ||
        (shadowWaTable == nullptr) ||
        (systemInfo == nullptr))
    {
        MOS_OS_ASSERTMESSAGE("Invalid parameter \n");
        return MOS_STATUS_INVALID_PARAMETER;
    }

    LinuxDriverInfo drvInfo = {18, 3, 0, 23172, 3, 1, 0, 1, 0, 0, 1, 0};

    if (!Mos_Solo_IsEnabled(nullptr) && HWInfoGetLinuxDrvInfo(fd, &drvInfo) != MOS_STATUS_SUCCESS)
    {
        MOS_OS_ASSERTMESSAGE("Failed to get the chipset id\n");
        return MOS_STATUS_INVALID_HANDLE;
    }

    GfxDeviceInfo *devInfo = getDeviceInfo(drvInfo.devId);
    if (devInfo == nullptr)
    {
        MOS_OS_ASSERTMESSAGE("Failed to get the device info for Device id: %x\n", drvInfo.devId);
        return MOS_STATUS_PLATFORM_NOT_SUPPORTED;
    }

    if (devInfo->InitMediaSysInfo &&
        devInfo->InitShadowSku &&
        devInfo->InitShadowWa &&
        devInfo->InitMediaSysInfo(devInfo, systemInfo) &&
        devInfo->InitShadowSku(devInfo, shadowSkuTable, &drvInfo) &&
        devInfo->InitShadowWa(devInfo, shadowWaTable, &drvInfo))
    {
        MOS_OS_NORMALMESSAGE("Init Media SystemInfo successfully\n");
    }
    else
    {
        MOS_OS_ASSERTMESSAGE("Failed to Init Media System Info\n");
        return MOS_STATUS_PLATFORM_NOT_SUPPORTED;
    }

    return MOS_STATUS_SUCCESS;
}

