/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Project HWC 2.0 Design
 */
#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
#include <utils/Errors.h>
#include <sync/sync.h>
#include <sys/mman.h>
#include <cutils/properties.h>
#include "ExynosMPP.h"
#include "ExynosResourceRestriction.h"
#include <hardware/hwcomposer_defs.h>
#include <math.h>
#include "VendorGraphicBuffer.h"
#include "ExynosHWCDebug.h"
#include "ExynosDisplay.h"
#include "ExynosVirtualDisplay.h"
#include "ExynosLayer.h"
#include "ExynosHWCHelper.h"
#include "exynos_sync.h"
#include "ExynosResourceManager.h"

/**
 * ExynosMPP implementation
 *
 * Abstraction class for HW Resource
 */

using namespace android;
using namespace vendor::graphics;
using namespace SOC_VERSION;

int ExynosMPP::mainDisplayWidth = 0;
int ExynosMPP::mainDisplayHeight = 0;
extern struct exynos_hwc_control exynosHWCControl;

std::unordered_map<tdm_attr_t, TDMInfo_t> HWAttrs = {
    {TDM_ATTR_SRAM_AMOUNT, {String8("SRAM"),  LS_DPUF}},
    {TDM_ATTR_AFBC,        {String8("AFBC"),  LS_DPUF}},
    {TDM_ATTR_SBWC,        {String8("SBWC"),  LS_DPUF}},
    {TDM_ATTR_ITP,         {String8("CSC"),   LS_DPUF}},
    {TDM_ATTR_ROT_90,      {String8("ROT"),   LS_DPUF}},
    {TDM_ATTR_SCALE,       {String8("SCALE"), LS_DPUF}},
    {TDM_ATTR_WCG,         {String8("WCG"),   LS_DPUF_AXI}},
};

void dumpExynosMPPImgInfo(uint32_t type, exynos_mpp_img_info &imgInfo)
{
    HDEBUGLOGD(type, "\tbuffer: %p, bufferType: %d",
            imgInfo.bufferHandle, imgInfo.bufferType);
}

bool exynosMPPSourceComp(const ExynosMPPSource* l, const ExynosMPPSource* r)
{
    if (l == NULL || r == NULL) {
        HWC_LOGE(NULL,"exynosMPP compare error");
        return 0;
    }
    return (l->mSrcImg.zOrder < r->mSrcImg.zOrder);
}

void dump(const restriction_size_t &restrictionSize, String8 &result) {
    result.appendFormat("    maxDownScale = %u, maxUpscale = %u\n", restrictionSize.maxDownScale,
                        restrictionSize.maxUpScale);
    result.appendFormat("    maxFullWidth = %u, maxFullHeight = %u\n", restrictionSize.maxFullWidth,
                        restrictionSize.maxFullHeight);
    result.appendFormat("    minFullWidth = %u, minFullHeight = %u\n", restrictionSize.minFullWidth,
                        restrictionSize.minFullHeight);
    result.appendFormat("    fullWidthAlign = %u, fullHeightAlign = %u\n",
                        restrictionSize.fullWidthAlign, restrictionSize.fullHeightAlign);
    result.appendFormat("    maxCropWidth = %u, maxCropHeight = %u\n", restrictionSize.maxCropWidth,
                        restrictionSize.maxCropHeight);
    result.appendFormat("    minCropWidth = %u, minCropHeight = %u\n", restrictionSize.minCropWidth,
                        restrictionSize.minCropHeight);
    result.appendFormat("    cropXAlign = %u, cropYAlign = %u\n", restrictionSize.cropXAlign,
                        restrictionSize.cropYAlign);
    result.appendFormat("    cropWidthAlign = %u, cropHeightAlign = %u\n",
                        restrictionSize.cropWidthAlign, restrictionSize.cropHeightAlign);
}

ExynosMPPSource::ExynosMPPSource()
      : mSourceType(MPP_SOURCE_MAX), mSource(NULL), mOtfMPP(NULL), mM2mMPP(NULL) {
    memset(&mSrcImg, 0, sizeof(mSrcImg));
    mSrcImg.acquireFenceFd = -1;
    mSrcImg.releaseFenceFd = -1;
    memset(&mDstImg, 0, sizeof(mDstImg));
    mDstImg.acquireFenceFd = -1;
    mDstImg.releaseFenceFd = -1;
    memset(&mMidImg, 0, sizeof(mMidImg));
    mMidImg.acquireFenceFd = -1;
    mMidImg.releaseFenceFd = -1;

    mHWResourceAmount.clear();
}

ExynosMPPSource::ExynosMPPSource(uint32_t sourceType, void *source)
    : mSourceType(sourceType),
    mSource(source),
    mOtfMPP(NULL),
    mM2mMPP(NULL)
{
    memset(&mSrcImg, 0, sizeof(mSrcImg));
    mSrcImg.acquireFenceFd = -1;
    mSrcImg.releaseFenceFd = -1;
    memset(&mDstImg, 0, sizeof(mDstImg));
    mDstImg.acquireFenceFd = -1;
    mDstImg.releaseFenceFd = -1;
    memset(&mMidImg, 0, sizeof(mMidImg));
    mMidImg.acquireFenceFd = -1;
    mMidImg.releaseFenceFd = -1;
}

void ExynosMPPSource::setExynosImage(const exynos_image& src_img, const exynos_image& dst_img) {
    mSrcImg = src_img;
    mDstImg = dst_img;
}

void ExynosMPPSource::setExynosMidImage(const exynos_image& mid_img) {
    mMidImg = mid_img;
}

ExynosMPP::ExynosMPP(ExynosResourceManager* resourceManager,
        uint32_t physicalType, uint32_t logicalType, const char *name,
        uint32_t physicalIndex, uint32_t logicalIndex, uint32_t preAssignInfo)
: mResourceManager(resourceManager),
    mMPPType(MPP_TYPE_NONE),
    mPhysicalType(physicalType),
    mLogicalType(logicalType),
    mName(name),
    mPhysicalIndex(physicalIndex),
    mLogicalIndex(logicalIndex),
    mPreAssignDisplayInfo(preAssignInfo),
    mHWState(MPP_HW_STATE_IDLE),
    mLastStateFenceFd(-1),
    mAssignedState(MPP_ASSIGN_STATE_FREE),
    mEnable(true),
    mAssignedDisplay(NULL),
    mMaxSrcLayerNum(1),
    mPrevAssignedState(MPP_ASSIGN_STATE_FREE),
    mPrevAssignedDisplayType(-1),
    mReservedDisplay(-1),
    mResourceManageThread(android::sp<ResourceManageThread>::make(this)),
    mCapacity(-1),
    mUsedCapacity(0),
    mAllocOutBufFlag(true),
    mFreeOutBufFlag(true),
    mHWBusyFlag(false),
    mCurrentDstBuf(0),
    mPrivDstBuf(-1),
    mNeedCompressedTarget(false),
    mDstAllocatedSize(DST_SIZE_UNKNOWN),
    mUseM2MSrcFence(false),
    mAttr(0),
    mAssignOrder(0),
    mAXIPortId(0),
    mHWBlockId(0),
    mNeedSolidColorLayer(false)
{
    if (mPhysicalType < MPP_DPP_NUM) {
        mClockKhz = VPP_CLOCK;
        mPPC = VPP_PIXEL_PER_CLOCK;
    }

    if (mPhysicalType == MPP_G2D) {
        mClockKhz = G2D_CLOCK;
        if (mLogicalType == MPP_LOGICAL_G2D_RGB) {

            char value[256];
            int afbc_prop;
            property_get("ro.vendor.ddk.set.afbc", value, "0");
            afbc_prop = atoi(value);
            if (afbc_prop == 0)
                mNeedCompressedTarget = false;
            else
                mNeedCompressedTarget = true;

            mMaxSrcLayerNum = G2D_MAX_SRC_NUM;
        } else if (mLogicalType == MPP_LOGICAL_G2D_COMBO &&
                (mPreAssignDisplayInfo & HWC_DISPLAY_VIRTUAL_BIT)) {
            mMaxSrcLayerNum = G2D_MAX_SRC_NUM - 1;
            mAllocOutBufFlag = false;
            mNeedCompressedTarget = false;
            mUseM2MSrcFence = true;
        }
        /* Capacity means time(ms) that can be used for operation */
        mCapacity = MPP_G2D_CAPACITY;
        mAcrylicHandle = AcrylicFactory::createAcrylic("default_compositor");
        if (mAcrylicHandle == NULL) {
            MPP_LOGE("Fail to allocate acrylic handle");
            abort();
        } else {
            MPP_LOGI("mAcrylicHandle is created: %p", mAcrylicHandle);
        }
    }

    /* Basic feature supported flags */
    for (const auto &feature: feature_table) {
        if (feature.hwType == mPhysicalType)
            mAttr = feature.attr;
    }

    if (mPhysicalType == MPP_MSC) {
        mClockKhz = MSC_CLOCK;
        /* To do
        * Capacity should be set
        */
        mCapacity = MPP_MSC_CAPACITY;
        mAcrylicHandle = AcrylicFactory::createAcrylic("default_scaler");
        if (mAcrylicHandle == NULL) {
            MPP_LOGE("Fail to allocate acrylic handle");
            abort();
        } else {
            MPP_LOGI("mAcrylicHandle is created: %p", mAcrylicHandle);
        }
    }

    if (mMaxSrcLayerNum > 1) {
        mNeedSolidColorLayer = true;
        mAcrylicHandle->setDefaultColor(0, 0, 0, 0);
    }

    mAssignedSources.clear();
    resetUsedCapacity();

    mResourceManageThread->mRunning = true;
    mResourceManageThread->run("MPPThread");

    memset(&mPrevFrameInfo, 0, sizeof(mPrevFrameInfo));
    for (int i = 0; i < NUM_MPP_SRC_BUFS; i++) {
        mPrevFrameInfo.srcInfo[i].acquireFenceFd = -1;
        mPrevFrameInfo.srcInfo[i].releaseFenceFd = -1;
        mPrevFrameInfo.dstInfo[i].acquireFenceFd = -1;
        mPrevFrameInfo.dstInfo[i].releaseFenceFd = -1;
    }

    for (uint32_t i = 0; i < NUM_MPP_SRC_BUFS; i++) {
        memset(&mSrcImgs[i], 0, sizeof(mSrcImgs[i]));
        mSrcImgs[i].acrylicAcquireFenceFd = -1;
        mSrcImgs[i].acrylicReleaseFenceFd = -1;
    }
    for (uint32_t i = 0; i < NUM_MPP_DST_BUFS(mLogicalType); i++) {
        memset(&mDstImgs[i], 0, sizeof(mDstImgs[i]));
        mDstImgs[i].acrylicAcquireFenceFd = -1;
        mDstImgs[i].acrylicReleaseFenceFd = -1;
    }

    for (uint32_t i = 0; i < DISPLAY_MODE_NUM; i++)
    {
        mPreAssignDisplayList[i] = 0;
    }
}

ExynosMPP::~ExynosMPP()
{
    mResourceManageThread->mRunning = false;
    mResourceManageThread->requestExitAndWait();
}


ExynosMPP::ResourceManageThread::ResourceManageThread(ExynosMPP *exynosMPP)
: mExynosMPP(exynosMPP),
    mRunning(false)
{
}

ExynosMPP::ResourceManageThread::~ResourceManageThread()
{
}

bool ExynosMPP::isDataspaceSupportedByMPP(struct exynos_image &src, struct exynos_image &dst)
{
    uint32_t srcStandard = (src.dataSpace & HAL_DATASPACE_STANDARD_MASK);
    uint32_t dstStandard = (dst.dataSpace & HAL_DATASPACE_STANDARD_MASK);
    uint32_t srcTransfer = (src.dataSpace & HAL_DATASPACE_TRANSFER_MASK);
    uint32_t dstTransfer = (dst.dataSpace & HAL_DATASPACE_TRANSFER_MASK);

    /* No conversion case */
    if ((srcStandard == dstStandard) && (srcTransfer == dstTransfer))
        return true;

    /* Unspecified conversion case */
    if (((srcStandard == HAL_DATASPACE_STANDARD_UNSPECIFIED) ||
         (dstStandard == HAL_DATASPACE_STANDARD_UNSPECIFIED)) &&
        ((srcTransfer == HAL_DATASPACE_TRANSFER_UNSPECIFIED) ||
         (dstTransfer == HAL_DATASPACE_TRANSFER_UNSPECIFIED)))
        return true;

    /* WCG support check */
    /* 'Src is not HDR' and 'src,dst has differenct dataspace' means WCG case */
    /* Some MPPs are only support HDR but WCG */
    if (!hasHdrInfo(src) && ((mAttr & MPP_ATTR_WCG) == 0))
        return false;

    /* Standard support check */
    auto standard_it = dataspace_standard_map.find(srcStandard);
    if ((standard_it == dataspace_standard_map.end()) ||
        ((mAttr & standard_it->second) == 0))
        return false;

    /* Transfer support check */
    auto transfer_it = dataspace_transfer_map.find(srcTransfer);
    if ((transfer_it == dataspace_transfer_map.end()) ||
        ((mAttr & transfer_it->second) == 0))
        return false;

    return checkCSCRestriction(src, dst);
}

bool ExynosMPP::isSupportedHDR(struct exynos_image &src, struct exynos_image &dst)
{

    uint32_t srcStandard = (src.dataSpace & HAL_DATASPACE_STANDARD_MASK);
    uint32_t dstStandard = (dst.dataSpace & HAL_DATASPACE_STANDARD_MASK);
    uint32_t srcTransfer = (src.dataSpace & HAL_DATASPACE_TRANSFER_MASK);
    uint32_t dstTransfer = (dst.dataSpace & HAL_DATASPACE_TRANSFER_MASK);

    if (hasHdr10Plus(src) || hasHdrInfo(src) ) {
        if (mAttr & MPP_ATTR_HDR10PLUS)
            return true;
        else if ((srcStandard == dstStandard) && (srcTransfer == dstTransfer))
            return true;
        else if ((mLogicalType == MPP_LOGICAL_G2D_COMBO) && (mPreAssignDisplayInfo & HWC_DISPLAY_VIRTUAL_BIT))
            return true;
        else
            return false;
    }
    return true;
}

bool ExynosMPP::isSupportedHStrideCrop(struct exynos_image __unused &src)
{
    return true;
}

bool ExynosMPP::isSupportedBlend(struct exynos_image &src)
{
    switch(src.blending) {
    case HWC2_BLEND_MODE_NONE:
    case HWC2_BLEND_MODE_PREMULTIPLIED:
    case HWC2_BLEND_MODE_COVERAGE:
        return true;
    default:
        return false;
    }
}

bool ExynosMPP::checkRotationCondition(struct exynos_image &src)
{
    /* Check only DPP types */
    if (mPhysicalType >= MPP_DPP_NUM)
        return true;

    /* If DPP has their own restriction, implmemnt module codes */
    if (mAttr & MPP_ATTR_ROT_90) {
        if (isFormatYUV420(src.format) == true)
            return true;
    }

    /* Other DPPs */
    if ((src.transform & HAL_TRANSFORM_ROT_90) == 0)
    {
        if ((src.compressionInfo.type == COMP_TYPE_AFBC) && (src.transform != 0)) return false;
        return true;
    } else {
        return false;
    }

    return true;
}

bool ExynosMPP::isSupportedTransform(struct exynos_image &src)
{
    if (src.transform == 0) return true;

    /* If MPP need to check additional condition,
     * implement checkRotationCondition function to check it */
    /* For example, DPP need to check custom conditons */
    if (!checkRotationCondition(src))
        return false;

    for(auto transform_map : transform_map_table) {
        if (src.transform & transform_map.hal_tr) {
            if (!(mAttr & transform_map.hwc_tr))
                return false;
        }
    }

    return true;
}

bool ExynosMPP::isSupportedCompression(struct exynos_image &src)
{
    if (src.compressionInfo.type == COMP_TYPE_AFBC) {
        if (mAttr & MPP_ATTR_AFBC)
            return true;
        else
            return false;
    }

    return true;
}

bool ExynosMPP::isSupportedCapability(ExynosDisplay &display, struct exynos_image &src)
{
    if (display.mType != HWC_DISPLAY_EXTERNAL)
        return true;

    if (!(mAttr & MPP_ATTR_USE_CAPA))
        return true;

    if (mResourceManager->hasHdrLayer || mResourceManager->hasDrmLayer) {
        if (getDrmMode(src.usageFlags) != NO_DRM)
            return true;
        else if (hasHdrInfo(src))
            return true;
        else
            return false;
    }

    return true;
}

bool ExynosMPP::isSupportedDRM(struct exynos_image &src)
{
    if (getDrmMode(src.usageFlags) == NO_DRM)
        return true;

    if (mLogicalType == MPP_LOGICAL_G2D_RGB)
        return false;

    return true;
}

bool ExynosMPP::checkCSCRestriction(struct exynos_image &src, struct exynos_image &dst)
{
    return true;
}

bool ExynosMPP::isDimLayerSupported()
{
    if (mAttr & MPP_ATTR_DIM)
        return true;

    return false;
}

bool ExynosMPP::isSrcFormatSupported(struct exynos_image &src)
{
    if (mLogicalType == MPP_LOGICAL_G2D_YUV) {
        /* Support YUV layer and HDR RGB layer */
        if (isFormatRgb(src.format) && (hasHdrInfo(src) == false))
            return false;
    }
    if ((mLogicalType == MPP_LOGICAL_G2D_RGB) &&
        isFormatYUV(src.format))
        return false;
    if ((mLogicalType == MPP_LOGICAL_MSC_YUV) &&
        isFormatRgb(src.format)) {
        return false;
    }

    if (mResourceManager == NULL) return false;

    for (uint32_t i = 0 ; i < mResourceManager->mFormatRestrictionCnt; i++) {
        if ((mResourceManager->mFormatRestrictions[i].hwType == mPhysicalType) &&
                ((mResourceManager->mFormatRestrictions[i].nodeType == NODE_NONE) ||
                 (mResourceManager->mFormatRestrictions[i].nodeType == NODE_SRC)) &&
                (mResourceManager->mFormatRestrictions[i].format == src.format))
            return true;
    }

    return false;
}

bool ExynosMPP::isDstFormatSupported(struct exynos_image &dst)
{

    for (uint32_t i = 0 ; i < mResourceManager->mFormatRestrictionCnt; i++) {
        if ((mResourceManager->mFormatRestrictions[i].hwType == mPhysicalType) &&
                ((mResourceManager->mFormatRestrictions[i].nodeType == NODE_NONE) ||
                 (mResourceManager->mFormatRestrictions[i].nodeType == NODE_DST)) &&
                (mResourceManager->mFormatRestrictions[i].format == dst.format))
            return true;
    }

    return false;
}

uint32_t ExynosMPP::getMaxUpscale(const struct exynos_image &src,
                                  const struct exynos_image __unused &dst) const {
    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].maxUpScale;
}

bool ExynosMPP::checkDownscaleCap(const float resolution, const float displayRatio_V) const {
    if (mPhysicalType >= MPP_DPP_NUM) return true;

    return float(mClockKhz) >= ((resolution * VPP_RESOL_MARGIN) / (mPPC * displayRatio_V));
}

uint32_t ExynosMPP::getDownscaleRestriction(const struct exynos_image &src,
                                            const struct exynos_image & /*dst*/) const {
    auto idx = getRestrictionClassification(src);
    return mDstSizeRestrictions[idx].maxDownScale;
}

uint32_t ExynosMPP::getMaxDownscale(const ExynosDisplay &display, const struct exynos_image &src,
                                    const struct exynos_image &dst) const {
    uint32_t maxDownscale = getDownscaleRestriction(src, dst);

    if (maxDownscale <= 1) {
        return maxDownscale;
    }

    if (mPhysicalType < MPP_DPP_NUM) {
        float resolution = float(src.w) * float(src.h) * display.getBtsRefreshRate() / 1000;
        if (!checkDownscaleCap(resolution, float(dst.h) / float(display.mYres))) {
            return 1;
        }
    }

    return maxDownscale;
}

uint32_t ExynosMPP::getSrcXOffsetAlign(struct exynos_image &src)
{
    /* Refer module(ExynosMPPModule) for chip specific restrictions */
    uint32_t idx = getRestrictionClassification(src);
    if ((mPhysicalType == MPP_MSC) &&
            ((src.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_S10B) ||
            (src.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_S10B))) {
        return 16;
    }
    return mSrcSizeRestrictions[idx].cropXAlign;
}
uint32_t ExynosMPP::getSrcXOffsetAlign(uint32_t idx)
{
    if (idx >= RESTRICTION_MAX)
    {
        MPP_LOGE("invalid idx: %d", idx);
        return 16;
    }
    return mSrcSizeRestrictions[idx].cropXAlign;
}
uint32_t ExynosMPP::getSrcYOffsetAlign(struct exynos_image &src)
{
    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].cropYAlign;
}
uint32_t ExynosMPP::getSrcYOffsetAlign(uint32_t idx)
{
    if (idx >= RESTRICTION_MAX)
    {
        MPP_LOGE("invalid idx: %d", idx);
        return 16;
    }
    return mSrcSizeRestrictions[idx].cropYAlign;
}
uint32_t ExynosMPP::getSrcWidthAlign(struct exynos_image &src)
{
    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].fullWidthAlign;
}
uint32_t ExynosMPP::getSrcHeightAlign(struct exynos_image &src)
{
    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].fullHeightAlign;
}
uint32_t ExynosMPP::getSrcMaxWidth(struct exynos_image &src)
{
    if (isFormatYUV(src.format))
        return 4096;

    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].maxFullWidth;
}
uint32_t ExynosMPP::getSrcMaxHeight(struct exynos_image &src)
{
    if (isFormatYUV(src.format))
        return 4096;

    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].maxFullHeight;
}
uint32_t ExynosMPP::getSrcMinWidth(struct exynos_image &src)
{
    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].minFullWidth;
}
uint32_t ExynosMPP::getSrcMinWidth(uint32_t idx)
{
    if (idx >= RESTRICTION_MAX)
    {
        MPP_LOGE("invalid idx: %d", idx);
        return 16;
    }
    return mSrcSizeRestrictions[idx].minFullWidth;
}
uint32_t ExynosMPP::getSrcMinHeight(struct exynos_image &src)
{
    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].minFullHeight;
}
uint32_t ExynosMPP::getSrcMinHeight(uint32_t idx)
{
    if (idx >= RESTRICTION_MAX)
    {
        MPP_LOGE("invalid idx: %d", idx);
        return 16;
    }
    return mSrcSizeRestrictions[idx].minFullHeight;
}
uint32_t ExynosMPP::getSrcMaxCropWidth(struct exynos_image &src)
{
    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].maxCropWidth;
}
uint32_t ExynosMPP::getSrcMaxCropHeight(struct exynos_image &src)
{
    if ((mMPPType == MPP_TYPE_OTF) &&
        (src.transform & HAL_TRANSFORM_ROT_90))
        return 2160;

    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].maxCropHeight;
}
uint32_t ExynosMPP::getSrcMaxCropSize(struct exynos_image &src)
{
    return (getSrcMaxCropWidth(src) * getSrcMaxCropHeight(src));
}
uint32_t ExynosMPP::getSrcMinCropWidth(struct exynos_image &src)
{
    if (((src.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_S10B) ||
         (src.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_S10B)) &&
        (mPhysicalType == MPP_G2D))
        return 2;
    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].minCropWidth;
}
uint32_t ExynosMPP::getSrcMinCropHeight(struct exynos_image &src)
{
    if (((src.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_S10B) ||
         (src.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_S10B)) &&
        (mPhysicalType == MPP_G2D))
        return 2;
    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].minCropHeight;
}
uint32_t ExynosMPP::getSrcCropWidthAlign(const struct exynos_image &src) const {
    if (((src.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_S10B) ||
         (src.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_S10B)) &&
        (mPhysicalType == MPP_G2D))
        return 2;
    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].cropWidthAlign;
}

/* This is used for only otfMPP */
uint32_t ExynosMPP::getSrcCropWidthAlign(uint32_t idx) const {
    if (idx >= RESTRICTION_MAX)
    {
        MPP_LOGE("invalid idx: %d", idx);
        return 16;
    }
    return mSrcSizeRestrictions[idx].cropWidthAlign;
}
uint32_t ExynosMPP::getSrcCropHeightAlign(const struct exynos_image &src) const {
    if (((src.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_S10B) ||
         (src.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_S10B)) &&
        (mPhysicalType == MPP_G2D))
        return 2;

    uint32_t idx = getRestrictionClassification(src);
    return mSrcSizeRestrictions[idx].cropHeightAlign;
}

/* This is used for only otfMPP */
uint32_t ExynosMPP::getSrcCropHeightAlign(uint32_t idx) const {
    if (idx >= RESTRICTION_MAX)
    {
        MPP_LOGE("invalid idx: %d", idx);
        return 16;
    }
    return mSrcSizeRestrictions[idx].cropHeightAlign;
}
uint32_t ExynosMPP::getDstMaxWidth(struct exynos_image &dst)
{
    uint32_t idx = getRestrictionClassification(dst);
    return mDstSizeRestrictions[idx].maxCropWidth;
}
uint32_t ExynosMPP::getDstMaxHeight(struct exynos_image &dst)
{
    uint32_t idx = getRestrictionClassification(dst);
    return mDstSizeRestrictions[idx].maxCropHeight;
}
uint32_t ExynosMPP::getDstMinWidth(struct exynos_image &dst)
{
    if (((dst.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_S10B) ||
         (dst.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_S10B)) &&
        (mPhysicalType == MPP_G2D))
        return 64;

    if ((mNeedSolidColorLayer == false) && mNeedCompressedTarget)
        return 16;

    if ((mPhysicalType == MPP_G2D) && (mNeedSolidColorLayer == false) &&
            isFormatSBWC(dst.format))
        return 32;

    uint32_t idx = getRestrictionClassification(dst);
    return mDstSizeRestrictions[idx].minCropWidth;
}
uint32_t ExynosMPP::getDstMinHeight(struct exynos_image &dst)
{
    if ((mNeedSolidColorLayer == false) && mNeedCompressedTarget)
        return 16;

    if ((mPhysicalType == MPP_G2D) && (mNeedSolidColorLayer == false) &&
            isFormatSBWC(dst.format))
        return 8;

    uint32_t idx = getRestrictionClassification(dst);
    return mDstSizeRestrictions[idx].minCropHeight;
}
uint32_t ExynosMPP::getDstWidthAlign(const struct exynos_image &dst) const {
    if (((dst.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_S10B) ||
         (dst.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_S10B)) &&
        (mPhysicalType == MPP_G2D))
        return 64;

    if ((mNeedSolidColorLayer == false) && mNeedCompressedTarget)
        return 16;

    if ((mPhysicalType == MPP_G2D) && (mNeedSolidColorLayer == false) &&
            isFormatSBWC(dst.format))
        return 32;

    uint32_t idx = getRestrictionClassification(dst);
    return mDstSizeRestrictions[idx].cropWidthAlign;
}
uint32_t ExynosMPP::getDstHeightAlign(const struct exynos_image &dst) const {
    if ((mNeedSolidColorLayer == false) && mNeedCompressedTarget)
        return 16;

    if ((mPhysicalType == MPP_G2D) && (mNeedSolidColorLayer == false) &&
            isFormatSBWC(dst.format))
        return 8;

    uint32_t idx = getRestrictionClassification(dst);
    return mDstSizeRestrictions[idx].cropHeightAlign;
}
uint32_t ExynosMPP::getDstXOffsetAlign(struct exynos_image &dst)
{
    if ((mNeedSolidColorLayer == false) && mNeedCompressedTarget)
        return 16;

    if ((mPhysicalType == MPP_G2D) && (mNeedSolidColorLayer == false) &&
            isFormatSBWC(dst.format))
        return 32;

    uint32_t idx = getRestrictionClassification(dst);
    return mDstSizeRestrictions[idx].cropXAlign;
}
uint32_t ExynosMPP::getDstYOffsetAlign(struct exynos_image &dst)
{
    if ((mNeedSolidColorLayer == false) && mNeedCompressedTarget)
        return 16;

    if ((mPhysicalType == MPP_G2D) && (mNeedSolidColorLayer == false) &&
            isFormatSBWC(dst.format))
        return 8;

    uint32_t idx = getRestrictionClassification(dst);
    return mDstSizeRestrictions[idx].cropYAlign;
}
uint32_t ExynosMPP::getOutBufAlign()
{
    if (mNeedCompressedTarget)
        return 16;
    else
        return 1;
}

int32_t ExynosMPP::isSupportLayerColorTransform(
        struct exynos_image &src, struct exynos_image __unused &dst)
{
    if (src.needColorTransform == false)
        return true;

    if (mAttr & MPP_ATTR_LAYER_TRANSFORM)
        return true;

    return false;
}

bool ExynosMPP::ResourceManageThread::threadLoop()
{
    if (mExynosMPP == NULL)
        return false;

    ALOGI("%s threadLoop is started", mExynosMPP->mName.c_str());
    while(mRunning) {
        Mutex::Autolock lock(mMutex);
        while((mFreedBuffers.size() == 0) &&
                (mStateFences.size() == 0)) {
            mCondition.wait(mMutex);
        }

        if ((mExynosMPP->mHWState == MPP_HW_STATE_RUNNING) &&
                (mStateFences.size() != 0)) {
            if (checkStateFences()) {
                mExynosMPP->mHWState = MPP_HW_STATE_IDLE;
            }
        } else {
            if ((mStateFences.size() != 0) &&
                    (mExynosMPP->mHWState != MPP_HW_STATE_RUNNING)) {
                ALOGW("%s, mHWState(%d) but mStateFences size(%zu)",
                        mExynosMPP->mName.c_str(), mExynosMPP->mHWState,
                        mStateFences.size());
                checkStateFences();
            }
        }

        if (mFreedBuffers.size() != 0) {
            freeBuffers();
        }
    }
    return true;
}

void ExynosMPP::ResourceManageThread::freeBuffers()
{
    VendorGraphicBufferAllocator& gAllocator(VendorGraphicBufferAllocator::get());
    android::List<exynos_mpp_img_info >::iterator it;
    android::List<exynos_mpp_img_info >::iterator end;
    it = mFreedBuffers.begin();
    end = mFreedBuffers.end();

    uint32_t freebufNum = 0;
    while (it != end) {
        exynos_mpp_img_info freeBuffer = (exynos_mpp_img_info)(*it);
        HDEBUGLOGD(eDebugMPP|eDebugFence|eDebugBuf, "freebufNum: %d, buffer: %p", freebufNum, freeBuffer.bufferHandle);
        dumpExynosMPPImgInfo(eDebugMPP|eDebugFence|eDebugBuf, freeBuffer);
        if (fence_valid(freeBuffer.acrylicAcquireFenceFd)) {
            if (sync_wait(freeBuffer.acrylicAcquireFenceFd, 1000) < 0)
                HWC_LOGE(NULL, "%s:: acquire fence sync_wait error", mExynosMPP->mName.c_str());
            freeBuffer.acrylicAcquireFenceFd =
                fence_close(freeBuffer.acrylicAcquireFenceFd, mExynosMPP->mAssignedDisplay,
                        FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_ALL);
        }
        if (fence_valid(freeBuffer.acrylicReleaseFenceFd)) {
            if (sync_wait(freeBuffer.acrylicReleaseFenceFd, 1000) < 0)
                HWC_LOGE(NULL, "%s:: release fence sync_wait error", mExynosMPP->mName.c_str());
            freeBuffer.acrylicReleaseFenceFd =
                fence_close(freeBuffer.acrylicReleaseFenceFd, mExynosMPP->mAssignedDisplay,
                        FENCE_TYPE_SRC_RELEASE, FENCE_IP_ALL);
        }
        gAllocator.free(freeBuffer.bufferHandle);
        it = mFreedBuffers.erase(it);
    }
}

bool ExynosMPP::ResourceManageThread::checkStateFences()
{
    bool ret = true;
    android::List<int >::iterator it;
    android::List<int >::iterator end;

    it = mStateFences.begin();
    end = mStateFences.end();
    uint32_t waitFenceNum = 0;
    while (it != end) {
        int fence = (int)(*it);
        HDEBUGLOGD(eDebugMPP|eDebugFence, "%d wait fence: %d", waitFenceNum, fence);
        waitFenceNum++;
        if (fence_valid(fence)) {
            if (sync_wait(fence, 5000) < 0) {
                HWC_LOGE(NULL, "%s::[%s][%d] sync_wait(%d) error(%s)", __func__,
                        mExynosMPP->mName.c_str(), mExynosMPP->mLogicalIndex, fence, strerror(errno));
                ret = false;
            }
            fence = fence_close(fence, mExynosMPP->mAssignedDisplay,
                    FENCE_TYPE_ALL, FENCE_IP_ALL);
        }
        it = mStateFences.erase(it);
    }
    return ret;
}

void ExynosMPP::ResourceManageThread::addFreedBuffer(exynos_mpp_img_info freedBuffer)
{
    android::Mutex::Autolock lock(mMutex);
    mFreedBuffers.push_back(freedBuffer);
    mCondition.signal();
}

void ExynosMPP::ResourceManageThread::addStateFence(int fence)
{
    Mutex::Autolock lock(mMutex);
    HDEBUGLOGD(eDebugMPP|eDebugFence, "wait fence is added: %d", fence);
    mStateFences.push_back(fence);
    mCondition.signal();
}

/**
 * @param w
 * @param h
 * @param color
 * @param usage
 * @return int32_t
 */
int32_t ExynosMPP::allocOutBuf(uint32_t w, uint32_t h, uint32_t format, uint64_t usage, uint32_t index) {
    ATRACE_CALL();
    uint32_t dstStride = 0;

    MPP_LOGD(eDebugMPP|eDebugBuf, "index: %d++++++++", index);

    if (index >= NUM_MPP_DST_BUFS(mLogicalType)) {
        return -EINVAL;
    }

    exynos_mpp_img_info freeDstBuf = mDstImgs[index];
    MPP_LOGD(eDebugMPP|eDebugBuf, "mDstImg[%d] is reallocated", index);
    dumpExynosMPPImgInfo(eDebugMPP, mDstImgs[index]);

    uint64_t allocUsage = getBufferUsage(usage);
    if (!needCompressDstBuf()) {
        allocUsage |= VendorGraphicBufferUsage::NO_AFBC;
    }
    buffer_handle_t dstBuffer;

    MPP_LOGD(eDebugMPP|eDebugBuf, "\tw: %d, h: %d, format: 0x%8x, previousBuffer: %p, allocUsage: 0x%" PRIx64 ", usage: 0x%" PRIx64 "",
            w, h, format, freeDstBuf.bufferHandle, allocUsage, usage);

    status_t error = NO_ERROR;

    {
        ATRACE_CALL();

        VendorGraphicBufferAllocator& gAllocator(VendorGraphicBufferAllocator::get());
        error = gAllocator.allocate(w, h, format, 1, allocUsage, &dstBuffer, &dstStride, "HWC");
    }

    if ((error != NO_ERROR) || (dstBuffer == NULL)) {
        MPP_LOGE("failed to allocate destination buffer(%dx%d): %d", w, h, error);
        return -EINVAL;
    }

    memset(&mDstImgs[index], 0, sizeof(mDstImgs[index]));

    mDstImgs[index].acrylicAcquireFenceFd = -1;
    mDstImgs[index].acrylicReleaseFenceFd = -1;
    mDstImgs[index].bufferHandle = dstBuffer;
    mDstImgs[index].bufferType = getBufferType(usage);
    mDstImgs[index].format = format;

    MPP_LOGD(eDebugMPP|eDebugBuf, "free outbuf[%d] %p", index, freeDstBuf.bufferHandle);

    if (freeDstBuf.bufferHandle != NULL) {
        freeOutBuf(freeDstBuf);
    } else {
        if (mAssignedDisplay != NULL) {
            freeDstBuf.acrylicAcquireFenceFd = fence_close(freeDstBuf.acrylicAcquireFenceFd,
                    mAssignedDisplay, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_G2D);
            freeDstBuf.acrylicReleaseFenceFd = fence_close(freeDstBuf.acrylicReleaseFenceFd,
                    mAssignedDisplay, FENCE_TYPE_SRC_RELEASE, FENCE_IP_G2D);
        }
    }

    MPP_LOGD(eDebugMPP|eDebugBuf, "dstBuffer(%p)-----------", dstBuffer);

    return NO_ERROR;
}

/**
 * @param outbuf
 * @return int32_t
 */
int32_t ExynosMPP::setOutBuf(buffer_handle_t outbuf, int32_t fence) {
    mDstImgs[mCurrentDstBuf].bufferHandle = NULL;
    if (outbuf != NULL) {
        mDstImgs[mCurrentDstBuf].bufferHandle = outbuf;
        mDstImgs[mCurrentDstBuf].format =
            VendorGraphicBufferMeta::get_format(mDstImgs[mCurrentDstBuf].bufferHandle);
    }
    setDstAcquireFence(fence);
    return NO_ERROR;
}

/**
 * @param dst
 * @return int32_t
 */
int32_t ExynosMPP::freeOutBuf(struct exynos_mpp_img_info dst) {
    mResourceManageThread->addFreedBuffer(dst);
    dst.bufferHandle = NULL;
    return NO_ERROR;
}

uint32_t ExynosMPP::getBufferType(uint64_t usage)
{
    if (getDrmMode(usage) == SECURE_DRM)
        return MPP_BUFFER_SECURE_DRM;
    else if (getDrmMode(usage) == NORMAL_DRM)
        return MPP_BUFFER_NORMAL_DRM;
    else {
        if (exynosHWCControl.dumpMidBuf)
            return MPP_BUFFER_DUMP;
        else
            return MPP_BUFFER_NORMAL;
    }
}

uint32_t ExynosMPP::getBufferType(const buffer_handle_t handle)
{
    uint64_t usage = VendorGraphicBufferMeta::get_usage(handle);

    return getBufferType(usage);
}

uint64_t ExynosMPP::getBufferUsage(uint64_t usage)
{
    uint64_t allocUsage = 0;
    if (getBufferType(usage) == MPP_BUFFER_DUMP) {
        allocUsage = BufferUsage::CPU_READ_OFTEN |
            BufferUsage::CPU_WRITE_OFTEN;
    } else {
        allocUsage = BufferUsage::CPU_READ_NEVER |
            BufferUsage::CPU_WRITE_NEVER |
            VendorGraphicBufferUsage::NOZEROED |
            BufferUsage::COMPOSER_OVERLAY;
    }

    if (getDrmMode(usage) == SECURE_DRM) {
        allocUsage |= BufferUsage::PROTECTED;
        allocUsage &= ~VendorGraphicBufferUsage::PRIVATE_NONSECURE;
    } else if (getDrmMode(usage) == NORMAL_DRM) {
        allocUsage |= BufferUsage::PROTECTED;
        allocUsage |= VendorGraphicBufferUsage::PRIVATE_NONSECURE;
    }

    return allocUsage;
}

bool ExynosMPP::needCompressDstBuf() const {
    return (mMaxSrcLayerNum > 1) && mNeedCompressedTarget;
}

uint32_t ExynosMPP::getAlignedDstFullWidth(struct exynos_image& dst) {
    return pixel_align(dst.fullWidth, getDstStrideAlignment(dst.format));
}

bool ExynosMPP::needDstBufRealloc(struct exynos_image &dst, uint32_t index)
{
    MPP_LOGD(eDebugMPP|eDebugBuf, "index: %d++++++++", index);

    if (index >= NUM_MPP_DST_BUFS(mLogicalType)) {
        MPP_LOGE("%s:: index(%d) is not valid", __func__, index);
        return false;
    }
    buffer_handle_t dst_handle = NULL;
    if (mDstImgs[index].bufferHandle != NULL)
        dst_handle = mDstImgs[index].bufferHandle;

    if (dst_handle == NULL) {
        MPP_LOGD(eDebugMPP|eDebugBuf, "\tDstImag[%d]  handle is NULL", index);
        return true;
    }

    int32_t assignedDisplayType = -1;
    if (mAssignedDisplay != NULL) {
        assignedDisplayType = mAssignedDisplay->mType;
    } else {
        MPP_LOGE("%s:: mpp is not assigned", __func__);
        return false;
    }

    ExynosDisplay *prevAssignedDisplay = mDstImgs[index].assignedDisplay;
    if(prevAssignedDisplay == NULL) {
        MPP_LOGD(eDebugMPP|eDebugBuf, "\tDstImag[%d] prevAssignedDisplay is NULL", index);
        return true;
    }

    VendorGraphicBufferMeta gmeta(dst_handle);

    MPP_LOGD(eDebugMPP | eDebugBuf, "\tdst_handle(%p) afbc (%u) sbwc (%u) lossy (%u)", dst_handle,
             isAFBCCompressed(dst_handle), isFormatSBWC(gmeta.format), isFormatLossy(gmeta.format));
    MPP_LOGD(eDebugMPP | eDebugBuf,
             "\tAssignedDisplay[%d, %d] format[0x%8x, 0x%8x], bufferType[%d, %d], "
             "usageFlags: 0x%" PRIx64 ", need comp_type 0x%x lossy %u",
             mPrevAssignedDisplayType, assignedDisplayType, gmeta.format, dst.format,
             mDstImgs[index].bufferType, getBufferType(dst.usageFlags), dst.usageFlags,
             dst.compressionInfo.type, isFormatLossy(dst.format));

    bool realloc = (mPrevAssignedDisplayType != assignedDisplayType) ||
            (prevAssignedDisplay != mAssignedDisplay) ||
            (formatToBpp(gmeta.format) < formatToBpp(dst.format)) ||
            ((gmeta.stride * gmeta.vstride) <
             (int)(getAlignedDstFullWidth(dst) * dst.fullHeight)) ||
            (mDstImgs[index].bufferType != getBufferType(dst.usageFlags)) ||
            (isAFBCCompressed(dst_handle) != (dst.compressionInfo.type == COMP_TYPE_AFBC)) ||
            (isFormatSBWC(gmeta.format) != isFormatSBWC(dst.format)) ||
            (isFormatLossy(gmeta.format) != isFormatLossy(dst.format));

    MPP_LOGD(eDebugMPP|eDebugBuf, "realloc: %d--------", realloc);
    return realloc;
}

bool ExynosMPP::canUsePrevFrame()
{
    if ((mAssignedDisplay && !mAssignedDisplay->mDisplayControl.skipM2mProcessing) ||
        !exynosHWCControl.skipM2mProcessing)
        return false;

    /* virtual display always require composition */
    if (mAllocOutBufFlag == false)
        return false;

    if (mPrevFrameInfo.srcNum != mAssignedSources.size())
        return false;

    for (uint32_t i = 0; i < mPrevFrameInfo.srcNum; i++) {
        if ((mPrevFrameInfo.srcInfo[i].bufferHandle != mAssignedSources[i]->mSrcImg.bufferHandle) ||
            (mPrevFrameInfo.srcInfo[i].x != mAssignedSources[i]->mSrcImg.x) ||
            (mPrevFrameInfo.srcInfo[i].y != mAssignedSources[i]->mSrcImg.y) ||
            (mPrevFrameInfo.srcInfo[i].w != mAssignedSources[i]->mSrcImg.w) ||
            (mPrevFrameInfo.srcInfo[i].h != mAssignedSources[i]->mSrcImg.h) ||
            (mPrevFrameInfo.srcInfo[i].format != mAssignedSources[i]->mSrcImg.format) ||
            (mPrevFrameInfo.srcInfo[i].usageFlags != mAssignedSources[i]->mSrcImg.usageFlags) ||
            (mPrevFrameInfo.srcInfo[i].dataSpace != mAssignedSources[i]->mSrcImg.dataSpace) ||
            (mPrevFrameInfo.srcInfo[i].blending != mAssignedSources[i]->mSrcImg.blending) ||
            (mPrevFrameInfo.srcInfo[i].transform != mAssignedSources[i]->mSrcImg.transform) ||
            (mPrevFrameInfo.srcInfo[i].compressionInfo.type !=
             mAssignedSources[i]->mSrcImg.compressionInfo.type) ||
            (mPrevFrameInfo.srcInfo[i].planeAlpha != mAssignedSources[i]->mSrcImg.planeAlpha) ||
            (mPrevFrameInfo.dstInfo[i].x != mAssignedSources[i]->mMidImg.x) ||
            (mPrevFrameInfo.dstInfo[i].y != mAssignedSources[i]->mMidImg.y) ||
            (mPrevFrameInfo.dstInfo[i].w != mAssignedSources[i]->mMidImg.w) ||
            (mPrevFrameInfo.dstInfo[i].h != mAssignedSources[i]->mMidImg.h) ||
            (mPrevFrameInfo.dstInfo[i].format != mAssignedSources[i]->mMidImg.format))
            return false;
    }

   int32_t prevDstIndex  = (mCurrentDstBuf + NUM_MPP_DST_BUFS(mLogicalType) - 1)% NUM_MPP_DST_BUFS(mLogicalType);
   if (mDstImgs[prevDstIndex].bufferHandle == NULL)
       return false;

    return true;
}

int32_t ExynosMPP::setupLayer(exynos_mpp_img_info *srcImgInfo, struct exynos_image &src, struct exynos_image &dst)
{
    int ret = NO_ERROR;

    if (srcImgInfo->mppLayer == NULL) {
        if ((srcImgInfo->mppLayer = mAcrylicHandle->createLayer()) == NULL)
        {
            MPP_LOGE("%s:: Fail to create layer", __func__);
            return -EINVAL;
        }
    }

    if (src.bufferHandle == NULL) {
        MPP_LOGE("%s:: Invalid source handle", __func__);
        return -EINVAL;
    }

    buffer_handle_t srcHandle = NULL;
    if (src.bufferHandle != NULL)
        srcHandle = src.bufferHandle;

    VendorGraphicBufferMeta gmeta(srcHandle);
    int bufFds[MAX_HW2D_PLANES];
    size_t bufLength[MAX_HW2D_PLANES];
    uint32_t attribute = 0;
    uint32_t bufferNum = getBufferNumOfFormat(gmeta.format, getCompressionType(srcHandle));
    android_dataspace_t dataspace = src.dataSpace;
    if (dataspace == HAL_DATASPACE_UNKNOWN)
    {
        if (isFormatRgb(gmeta.format))
            dataspace = HAL_DATASPACE_V0_SRGB;
        else
            dataspace = HAL_DATASPACE_V0_BT601_625;
    }

    if (bufferNum == 0)
    {
        MPP_LOGE("%s:: Fail to get bufferNum(%d), format(0x%8x, afbc %d)", __func__, bufferNum,
                 gmeta.format, isAFBCCompressed(srcHandle));
        return -EINVAL;
    }
    bufFds[0] = gmeta.fd;
    bufFds[1] = gmeta.fd1;
    bufFds[2] = gmeta.fd2;
    if (getBufLength(srcHandle, MAX_HW2D_PLANES, bufLength, gmeta.format, src.fullWidth, src.fullHeight) != NO_ERROR) {
        MPP_LOGE("%s:: invalid bufferLength(%zu, %zu, %zu), format(0x%8x)", __func__,
                bufLength[0], bufLength[1], bufLength[2], gmeta.format);
        return -EINVAL;
    }

    /* HDR process */
    if (hasHdrInfo(src)) {
        unsigned int max = (src.metaParcel.sHdrStaticInfo.sType1.mMaxDisplayLuminance/10000);
        unsigned int min = src.metaParcel.sHdrStaticInfo.sType1.mMinDisplayLuminance;

        srcImgInfo->mppLayer->setMasterDisplayLuminance(min,max);
        MPP_LOGD(eDebugMPP, "HWC2: G2D luminance min %d, max %d", min, max);
        MPP_LOGD(eDebugMPP|eDebugFence, "G2D getting HDR source!");

        srcImgInfo->mppLayer->setLayerHDR(true);
    } else
        srcImgInfo->mppLayer->setLayerHDR(false);

    /* Transfer MetaData */
    if (src.hasMetaParcel) {
        srcImgInfo->mppLayer->setLayerData(&src.metaParcel, sizeof(src.metaParcel));
    }

    srcImgInfo->bufferType = getBufferType(srcHandle);
    if (srcImgInfo->bufferType == MPP_BUFFER_SECURE_DRM)
        attribute |= AcrylicCanvas::ATTR_PROTECTED;
    /*Change AFBC attribute on the basis of the modifier*/
    if (src.compressionInfo.type == COMP_TYPE_AFBC) {
        if ((src.compressionInfo.modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) ==
            AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) {
            attribute |= AcrylicCanvas::ATTR_COMPRESSED_WIDEBLK;
        } else {
            attribute |= AcrylicCanvas::ATTR_COMPRESSED;
        }
    }

    srcImgInfo->bufferHandle = srcHandle;
    srcImgInfo->acrylicAcquireFenceFd =
        hwcCheckFenceDebug(mAssignedDisplay, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_G2D, src.acquireFenceFd);

    MPP_LOGD(eDebugMPP|eDebugFence, "source configuration:");
    MPP_LOGD(eDebugMPP, "\tImageDimension[%d, %d], ImageType[0x%8x, 0x%8x]",
            src.fullWidth, src.fullHeight,
            gmeta.format, dataspace);
    MPP_LOGD(eDebugMPP|eDebugFence, "\tImageBuffer handle: %p, fds[%d, %d, %d], bufLength[%zu, %zu, %zu], bufferNum: %d, acquireFence: %d, attribute: %d",
            srcHandle, bufFds[0], bufFds[1], bufFds[2], bufLength[0], bufLength[1], bufLength[2],
            bufferNum, srcImgInfo->acrylicAcquireFenceFd, attribute);
    MPP_LOGD(eDebugMPP, "\tsrc_rect[%d, %d, %d, %d], dst_rect[%d, %d, %d, %d], transform(0x%4x)",
            (int)src.x, (int)src.y, (int)(src.x + src.w), (int)(src.y + src.h),
            (int)dst.x, (int)dst.y, (int)(dst.x + dst.w), (int)(dst.y + dst.h), src.transform);

    srcImgInfo->mppLayer->setImageDimension(src.fullWidth, src.fullHeight);

    if (gmeta.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_PRIV) {
        srcImgInfo->mppLayer->setImageType(HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M, dataspace);
    } else {
        srcImgInfo->mppLayer->setImageType(gmeta.format, dataspace);
    }

    if (mPhysicalType == MPP_G2D) {
        setFenceName(srcImgInfo->acrylicAcquireFenceFd, FENCE_G2D_SRC_LAYER);
        setFenceInfo(srcImgInfo->acrylicAcquireFenceFd, mAssignedDisplay, FENCE_TYPE_SRC_ACQUIRE,
                     FENCE_IP_G2D, HwcFenceDirection::TO);
    } else if (mPhysicalType == MPP_MSC) {
        setFenceName(srcImgInfo->acrylicAcquireFenceFd, FENCE_MSC_SRC_LAYER);
        setFenceInfo(srcImgInfo->acrylicAcquireFenceFd, mAssignedDisplay, FENCE_TYPE_SRC_ACQUIRE,
                     FENCE_IP_MSC, HwcFenceDirection::TO);
    } else {
        MPP_LOGE("%s:: invalid mPhysicalType(%d)", __func__, mPhysicalType);
    }

    srcImgInfo->mppLayer->setImageBuffer(bufFds, bufLength, bufferNum,
            srcImgInfo->acrylicAcquireFenceFd, attribute);

    if (mMaxSrcLayerNum > 1) {
        srcImgInfo->mppLayer->setCompositMode(src.blending, (uint8_t)(255 * src.planeAlpha), src.zOrder);
    } else {
        srcImgInfo->mppLayer->setCompositMode(src.blending, 255, src.zOrder);
    }

    hwc_rect_t src_rect = {(int)src.x, (int)src.y, (int)(src.x + src.w), (int)(src.y + src.h)};
    hwc_rect_t dst_rect = {(int)dst.x, (int)dst.y, (int)(dst.x + dst.w), (int)(dst.y + dst.h)};

    if ((mAssignedDisplay != NULL) &&
        ((mAssignedDisplay->mType == HWC_DISPLAY_VIRTUAL) ||
         (mAssignedDisplay->mType == HWC_DISPLAY_EXTERNAL)))
        srcImgInfo->mppLayer->setCompositArea(src_rect, dst_rect, src.transform, AcrylicLayer::ATTR_NORESAMPLING);
    else {
        if(isFormatYUV(src.format))
            srcImgInfo->mppLayer->setCompositArea(src_rect, dst_rect, src.transform, AcrylicLayer::ATTR_NORESAMPLING);
        else
            srcImgInfo->mppLayer->setCompositArea(src_rect, dst_rect, src.transform);
    }

    srcImgInfo->acrylicAcquireFenceFd = -1;
    srcImgInfo->format = gmeta.format;

    if (gmeta.format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_PRIV) {
        srcImgInfo->format = HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M;
    }

    return ret;
}

dstMetaInfo_t ExynosMPP::getDstMetaInfo(android_dataspace_t dstDataspace)
{
    dstMetaInfo_t metaInfo;

    if ((mAssignedSources.size() <= 1) &&
            (mAssignedSources[0]->mSrcImg.dataSpace == dstDataspace)) {
        metaInfo.minLuminance =
            (uint16_t)mAssignedSources[0]->mSrcImg.metaParcel.sHdrStaticInfo.sType1.mMinDisplayLuminance;
        metaInfo.maxLuminance =
            (uint16_t)(mAssignedSources[0]->mSrcImg.metaParcel.sHdrStaticInfo.sType1.mMaxDisplayLuminance/10000);
    } else {
        // minLuminance: 0.0001nit unit, maxLuminance: 1nit unit
        metaInfo.minLuminance = (uint16_t)(mAssignedDisplay->mMinLuminance * 10000);
        metaInfo.maxLuminance = (uint16_t)mAssignedDisplay->mMaxLuminance;
    }

    return metaInfo;
}

uint32_t ExynosMPP::getDstStrideAlignment(int format) {
    /* In cases of Single-FD format, stride alignment should be matched. */
    if (format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN)
        return 64;
    else if (format == HAL_PIXEL_FORMAT_EXYNOS_YCbCr_P010_SPN)
        return 128;
    else
        return G2D_JUSTIFIED_DST_ALIGN;
}

int32_t ExynosMPP::setupDst(exynos_mpp_img_info *dstImgInfo)
{
    int ret = NO_ERROR;
    bool isComposition = (mMaxSrcLayerNum > 1);
    buffer_handle_t dstHandle = dstImgInfo->bufferHandle;
    int bufFds[MAX_HW2D_PLANES];
    size_t bufLength[MAX_HW2D_PLANES];
    uint32_t attribute = 0;
    uint32_t bufferNum = getBufferNumOfFormat(dstImgInfo->format, getCompressionType(dstHandle));
    if (bufferNum == 0)
    {
        MPP_LOGE("%s:: Fail to get bufferNum(%d), format(0x%8x, afbc %d)", __func__, bufferNum,
                 dstImgInfo->format, isAFBCCompressed(dstHandle));
        return -EINVAL;
    }

    android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
    VendorGraphicBufferMeta gmeta(dstHandle);

    if (isComposition) {
        if (isFormatRgb(dstImgInfo->format)) {
            if ((mAssignedDisplay != NULL) &&
                (mAssignedDisplay->mColorMode != HAL_COLOR_MODE_NATIVE))
                dataspace = colorModeToDataspace(mAssignedDisplay->mColorMode);
        } else {
            dataspace =
                (android_dataspace)(HAL_DATASPACE_STANDARD_BT709 | HAL_DATASPACE_TRANSFER_GAMMA2_2 | HAL_DATASPACE_RANGE_LIMITED);
        }
    } else {
        dataspace = mAssignedSources[0]->mMidImg.dataSpace;
    }

    if (dataspace == HAL_DATASPACE_UNKNOWN)
    {
        if (isFormatRgb(dstImgInfo->format))
            dataspace = HAL_DATASPACE_V0_SRGB;
        else
            dataspace = HAL_DATASPACE_V0_BT601_625;
    }

    bufFds[0] = gmeta.fd;
    bufFds[1] = gmeta.fd1;
    bufFds[2] = gmeta.fd2;
    if (getBufLength(dstHandle, MAX_HW2D_PLANES, bufLength, dstImgInfo->format,
                gmeta.stride, gmeta.vstride) != NO_ERROR) {
        MPP_LOGE("%s:: invalid bufferLength(%zu, %zu, %zu), format(0x%8x)", __func__,
                bufLength[0], bufLength[1], bufLength[2], dstImgInfo->format);
        return -EINVAL;
    }

    dstImgInfo->bufferType = getBufferType(dstHandle);
    if (dstImgInfo->bufferType == MPP_BUFFER_SECURE_DRM)
        attribute |= AcrylicCanvas::ATTR_PROTECTED;

    dstImgInfo->assignedDisplay = mAssignedDisplay;
    if (mAssignedDisplay != NULL) {
        mAcrylicHandle->setCanvasDimension(pixel_align(mAssignedDisplay->mXres,
                                                       getDstStrideAlignment(dstImgInfo->format)),
                                           pixel_align(mAssignedDisplay->mYres,
                                                       G2D_JUSTIFIED_DST_ALIGN));
    }

    /* setup dst */
    if (needCompressDstBuf()) {
        attribute |= AcrylicCanvas::ATTR_COMPRESSED;
    }

    if (mPhysicalType == MPP_G2D) {
        setFenceName(dstImgInfo->acrylicAcquireFenceFd, FENCE_G2D_DST_DPP);
        /* Might be closed next frame */
        setFenceInfo(dstImgInfo->acrylicAcquireFenceFd, mAssignedDisplay, FENCE_TYPE_DST_ACQUIRE,
                     FENCE_IP_G2D, HwcFenceDirection::TO);
    } else if (mPhysicalType == MPP_MSC) {
        setFenceName(dstImgInfo->acrylicAcquireFenceFd, FENCE_MSC_DST_DPP);
        /* Might be closed next frame */
        setFenceInfo(dstImgInfo->acrylicAcquireFenceFd, mAssignedDisplay, FENCE_TYPE_DST_ACQUIRE,
                     FENCE_IP_MSC, HwcFenceDirection::TO);
    } else {
        MPP_LOGE("%s:: invalid mPhysicalType(%d)", __func__, mPhysicalType);
    }

    mAcrylicHandle->setCanvasImageType(dstImgInfo->format, dataspace);

    if ((mLogicalType == MPP_LOGICAL_G2D_COMBO) &&
            (mAssignedDisplay != NULL) &&
            (mAssignedDisplay->mType == HWC_DISPLAY_VIRTUAL) &&
            (((ExynosVirtualDisplay *)mAssignedDisplay)->mIsWFDState == (int)LLWFD)) {
        mAcrylicHandle->setCanvasImageType(HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN, dataspace);
        dstImgInfo->acrylicAcquireFenceFd = fence_close(dstImgInfo->acrylicAcquireFenceFd,
                mAssignedDisplay, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_G2D);
        mAcrylicHandle->setCanvasBuffer(bufFds, bufLength, bufferNum,
                dstImgInfo->acrylicAcquireFenceFd, attribute);
        mAcrylicHandle->setCanvasOTF(attribute);
    }
    else {
        dstImgInfo->acrylicAcquireFenceFd =
            hwcCheckFenceDebug(mAssignedDisplay, FENCE_TYPE_DST_ACQUIRE, FENCE_IP_G2D, dstImgInfo->acrylicAcquireFenceFd);
        mAcrylicHandle->setCanvasBuffer(bufFds, bufLength, bufferNum,
                dstImgInfo->acrylicAcquireFenceFd, attribute);
    }

    dstMetaInfo_t metaInfo = getDstMetaInfo(dataspace);
    if ((mAssignedDisplay != NULL) &&
        (mAssignedDisplay->mType != HWC_DISPLAY_VIRTUAL)) {
        mAcrylicHandle->setTargetDisplayLuminance(metaInfo.minLuminance, metaInfo.maxLuminance);
    }

    MPP_LOGD(eDebugMPP|eDebugFence, "destination configuration:");
    MPP_LOGD(eDebugMPP, "\tImageDimension[%d, %d], ImageType[0x%8x, %d], target luminance[%d, %d]",
            gmeta.stride, gmeta.vstride,
            dstImgInfo->format, dataspace, metaInfo.minLuminance, metaInfo.maxLuminance);
    MPP_LOGD(eDebugMPP|eDebugFence, "\tImageBuffer handle: %p, fds[%d, %d, %d], bufLength[%zu, %zu, %zu], bufferNum: %d, acquireFence: %d, attribute: %d",
            dstHandle, bufFds[0], bufFds[1], bufFds[2], bufLength[0], bufLength[1], bufLength[2],
            bufferNum, dstImgInfo->acrylicAcquireFenceFd, attribute);


    dstImgInfo->acrylicAcquireFenceFd = -1;
    dstImgInfo->dataspace = dataspace;

    return ret;
}

int32_t ExynosMPP::doPostProcessingInternal()
{
    ATRACE_CALL();
    int ret = NO_ERROR;
    size_t sourceNum = mAssignedSources.size();

    if (mAcrylicHandle == NULL) {
        MPP_LOGE("%s:: mAcrylicHandle is NULL", __func__);
        return -EINVAL;
    }

    /* setup source layers */
    for(size_t i = 0; i < sourceNum; i++) {
        MPP_LOGD(eDebugMPP|eDebugFence, "Setup [%zu] source: %p", i, mAssignedSources[i]);
        if ((ret = setupLayer(&mSrcImgs[i], mAssignedSources[i]->mSrcImg, mAssignedSources[i]->mMidImg)) != NO_ERROR) {
            MPP_LOGE("%s:: fail to setupLayer[%zu], ret %d",
                    __func__, i, ret);
            return ret;
        }
    }

    if ((ret = setColorConversionInfo()) != NO_ERROR) {
            MPP_LOGE("%s:: fail to setColorConversionInfo ret %d",
                    __func__, ret);
            return ret;
    }

    if (mPrevFrameInfo.srcNum > sourceNum) {
        MPP_LOGD(eDebugMPP, "prev sourceNum(%d), current sourceNum(%zu)",
                mPrevFrameInfo.srcNum, sourceNum);
        for (size_t i = sourceNum; i < mPrevFrameInfo.srcNum; i++)
        {
            MPP_LOGD(eDebugMPP, "Remove mSrcImgs[%zu], %p", i, mSrcImgs[i].mppLayer);
            if (mSrcImgs[i].mppLayer != NULL) {
                delete mSrcImgs[i].mppLayer;
                mSrcImgs[i].mppLayer = NULL;
            }
        }
    }

    if (mAcrylicHandle->layerCount() != mAssignedSources.size()) {
        MPP_LOGE("Different layer number, acrylic layers(%d), assigned size(%zu)",
                mAcrylicHandle->layerCount(), mAssignedSources.size());
        return -EINVAL;
    }
    MPP_LOGD(eDebugFence, "setupDst ++ mDstImgs[%d] acrylicAcquireFenceFd(%d)",
            mCurrentDstBuf, mDstImgs[mCurrentDstBuf].acrylicAcquireFenceFd);

    setupDst(&mDstImgs[mCurrentDstBuf]);

    MPP_LOGD(eDebugFence, "setupDst -- mDstImgs[%d] acrylicAcquireFenceFd(%d) closed",
            mCurrentDstBuf, mDstImgs[mCurrentDstBuf].acrylicAcquireFenceFd);


    int usingFenceCnt = 1;
    bool acrylicReturn = true;

#ifndef DISABLE_FENCE
    if (mUseM2MSrcFence)
        usingFenceCnt = sourceNum + 1; // Get and Use src + dst fence
    else
        usingFenceCnt = 1;             // Get and Use only dst fence
    int *releaseFences = new int[usingFenceCnt];
    int dstBufIdx = usingFenceCnt - 1;
#else
    usingFenceCnt = 0;                 // Get and Use no fences
    int dstBufIdx = 0;
    int *releaseFences = NULL;
#endif

    acrylicReturn = mAcrylicHandle->execute(releaseFences, usingFenceCnt);

    if (acrylicReturn == false) {
        MPP_LOGE("%s:: fail to excute compositor", __func__);
        for(size_t i = 0; i < sourceNum; i++) {
            mSrcImgs[i].acrylicReleaseFenceFd = -1;
            MPP_LOGE("src[%zu]: ImageDimension[%d, %d], src_rect[%d, %d, %d, %d], dst_rect[%d, %d, %d, %d], transform(0x%4x)",
                    i,
                    mAssignedSources[i]->mSrcImg.fullWidth, mAssignedSources[i]->mSrcImg.fullHeight,
                    mAssignedSources[i]->mSrcImg.x, mAssignedSources[i]->mSrcImg.y,
                    mAssignedSources[i]->mSrcImg.x + mAssignedSources[i]->mSrcImg.w,
                    mAssignedSources[i]->mSrcImg.y + mAssignedSources[i]->mSrcImg.h,
                    mAssignedSources[i]->mMidImg.x, mAssignedSources[i]->mMidImg.y,
                    mAssignedSources[i]->mMidImg.x + mAssignedSources[i]->mMidImg.w,
                    mAssignedSources[i]->mMidImg.y + mAssignedSources[i]->mMidImg.h,
                    mAssignedSources[i]->mSrcImg.transform);
        }
        mDstImgs[mCurrentDstBuf].acrylicReleaseFenceFd = -1;
        ret = -EPERM;
    } else {

        // set fence informations from acryl
        if (mPhysicalType == MPP_G2D) {
            setFenceInfo(releaseFences[dstBufIdx], mAssignedDisplay, FENCE_TYPE_DST_ACQUIRE,
                         FENCE_IP_G2D, HwcFenceDirection::FROM);
            if (usingFenceCnt > 1) {
                for(size_t i = 0; i < sourceNum; i++) {
                    // TODO DPU release fence is tranferred to m2mMPP's source layer fence
                    setFenceInfo(releaseFences[i], mAssignedDisplay, FENCE_TYPE_SRC_RELEASE,
                                 FENCE_IP_G2D, HwcFenceDirection::FROM);
                }
            }
        } else if (mPhysicalType == MPP_MSC) {
            setFenceInfo(releaseFences[dstBufIdx], mAssignedDisplay, FENCE_TYPE_DST_ACQUIRE,
                         FENCE_IP_MSC, HwcFenceDirection::FROM);
            if (usingFenceCnt > 1) {
                for(size_t i = 0; i < sourceNum; i++) {
                    // TODO DPU release fence is tranferred to m2mMPP's source layer fence
                    setFenceInfo(releaseFences[i], mAssignedDisplay, FENCE_TYPE_SRC_RELEASE,
                                 FENCE_IP_MSC, HwcFenceDirection::FROM);
                }
            }
        } else {
            MPP_LOGE("%s:: invalid mPhysicalType(%d)", __func__, mPhysicalType);
        }

        if ((mLogicalType == MPP_LOGICAL_G2D_COMBO) &&
                (mAssignedDisplay != NULL) &&
                (mAssignedDisplay->mType == HWC_DISPLAY_VIRTUAL)) {
            if (((ExynosVirtualDisplay *)mAssignedDisplay)->mIsWFDState == (int)LLWFD) {
                if (usingFenceCnt != 0) // Use no fences
                    releaseFences[dstBufIdx] = fence_close(releaseFences[dstBufIdx],
                            mAssignedDisplay, FENCE_TYPE_SRC_RELEASE, FENCE_IP_G2D); // Close dst buf's fence
            }
            if (mUseM2MSrcFence) {
                if (((ExynosVirtualDisplay *)mAssignedDisplay)->mIsWFDState != (int)GOOGLEWFD) {
                    for (size_t i = 0; i < sourceNum; i++)
                        releaseFences[i] = fence_close(releaseFences[i],
                                mAssignedDisplay, FENCE_TYPE_SRC_RELEASE, FENCE_IP_G2D);
                }
            }
        }

        if (usingFenceCnt == 0) { // Use no fences
            for(size_t i = 0; i < sourceNum; i++) {
                mSrcImgs[i].acrylicReleaseFenceFd = -1;
            }
            mDstImgs[mCurrentDstBuf].acrylicReleaseFenceFd = -1;
        } else {
            for(size_t i = 0; i < sourceNum; i++) {
                if (mUseM2MSrcFence)
                    mSrcImgs[i].acrylicReleaseFenceFd =
                        hwcCheckFenceDebug(mAssignedDisplay, FENCE_TYPE_SRC_RELEASE, FENCE_IP_G2D, releaseFences[i]);
                else
                    mSrcImgs[i].acrylicReleaseFenceFd = -1;
                MPP_LOGD(eDebugFence, "mSrcImgs[%zu] acrylicReleaseFenceFd: %d",
                        i, mSrcImgs[i].acrylicReleaseFenceFd);
            }

            if (mDstImgs[mCurrentDstBuf].acrylicReleaseFenceFd >= 0) {
                MPP_LOGE("mDstImgs[%d].acrylicReleaseFenceFd(%d) is not initialized",
                        mCurrentDstBuf,
                        mDstImgs[mCurrentDstBuf].acrylicReleaseFenceFd);
            }

            if (mPhysicalType == MPP_G2D)
                mDstImgs[mCurrentDstBuf].acrylicReleaseFenceFd =
                    hwcCheckFenceDebug(mAssignedDisplay, FENCE_TYPE_DST_RELEASE, FENCE_IP_G2D, releaseFences[dstBufIdx]);
            else if (mPhysicalType == MPP_MSC)
                mDstImgs[mCurrentDstBuf].acrylicReleaseFenceFd =
                    hwcCheckFenceDebug(mAssignedDisplay, FENCE_TYPE_DST_RELEASE, FENCE_IP_MSC, releaseFences[dstBufIdx]);

            MPP_LOGD(eDebugFence, "mDstImgs[%d] acrylicReleaseFenceFd: %d , releaseFences[%d]",
                    mCurrentDstBuf, mDstImgs[mCurrentDstBuf].acrylicReleaseFenceFd, dstBufIdx);
        }

        if (exynosHWCControl.dumpMidBuf) {
            ALOGI("dump image");
            exynosHWCControl.dumpMidBuf = false;
            if ((mDstImgs[mCurrentDstBuf].acrylicReleaseFenceFd > 0) &&
                (sync_wait(mDstImgs[mCurrentDstBuf].acrylicReleaseFenceFd, 1000) < 0)) {
                ALOGE("%s:: fence sync_wait error to dump image", __func__);
            } else {
                buffer_handle_t dstHandle = mDstImgs[mCurrentDstBuf].bufferHandle;
                VendorGraphicBufferMeta gmeta(dstHandle);

                ALOGI("dump image fw: %d, fh:%d, size: %d", gmeta.stride, gmeta.vstride, gmeta.size);
                FILE *fp;
                fp = fopen(MPP_DUMP_PATH,"ab");

                if (fp) {
                    void *temp = mmap(0, gmeta.size, PROT_READ|PROT_WRITE, MAP_SHARED, gmeta.fd, 0);
                    if (temp) {
                        ALOGI("write...%p", temp);
                        int write_size = fwrite(temp, gmeta.size, 1, fp);
                        if (write_size < 0) {
                            ALOGI("write error: %s", strerror(errno));
                        } else {
                            ALOGI("write size: %d", write_size);
                        }
                        munmap(temp, gmeta.size);
                    } else {
                        ALOGE("mmap is NULL %s", strerror(errno));
                    }
                    fclose(fp);
                } else {
                    ALOGE("open fail %s", strerror(errno));
                }
            }
        }
    }

#ifndef DISABLE_FENCE
    delete [] releaseFences;
#endif

    return ret;
}

bool ExynosMPP::canSkipProcessing()
{
    if ((mAssignedDisplay == NULL) || (mAssignedSources.size() == 0))
        return true;
    ExynosMPPSource *source = mAssignedSources[0];
    exynos_image dst = source->mMidImg;
    if ((mLogicalType == MPP_LOGICAL_G2D_RGB) ||
        (mLogicalType == MPP_LOGICAL_G2D_COMBO)) {
        dst = mAssignedDisplay->mExynosCompositionInfo.mDstImg;
    }
    return ((needDstBufRealloc(dst, mCurrentDstBuf) == false) && canUsePrevFrame());
}

/**
 * @param dst
 * @return int32_t 0 on success, or a negative error code on failure.
 */
int32_t ExynosMPP::doPostProcessing(struct exynos_image& dst) {
    ATRACE_CALL();
    MPP_LOGD(eDebugMPP, "total assigned sources (%zu)++++++++", mAssignedSources.size());

    int ret = NO_ERROR;
    bool realloc = false;
    if (mAssignedSources.size() == 0) {
        MPP_LOGE("Assigned source size(%zu) is not valid",
                mAssignedSources.size());
        ret = -EINVAL;
        goto save_frame_info;
    }

    // Check whether destination buffer allocation is required
    if (mAllocOutBufFlag) {
        if ((realloc = needDstBufRealloc(dst, mCurrentDstBuf)) == true) {
            //  allocate mDstImgs[mCurrentDstBuf]
            uint32_t bufAlign = getOutBufAlign();
            bool isComposition = (mMaxSrcLayerNum > 1);
            if (isComposition)
                dst.format = DEFAULT_MPP_DST_FORMAT;

            uint32_t allocFormat = dst.format;
            if (mFreeOutBufFlag == false)
                allocFormat = DEFAULT_MPP_DST_FORMAT;

            if ((allocFormat == HAL_PIXEL_FORMAT_RGBA_1010102) ||
                (allocFormat == HAL_PIXEL_FORMAT_GOOGLE_NV12_SP_10B) ||
                (allocFormat == HAL_PIXEL_FORMAT_YCBCR_P010))
                allocFormat = DEFAULT_MPP_DST_FORMAT;

            ret = allocOutBuf(ALIGN_UP(mAssignedDisplay->mXres, bufAlign),
                    ALIGN_UP(mAssignedDisplay->mYres, bufAlign),
                    allocFormat, dst.usageFlags, mCurrentDstBuf);
        }
        if (ret < 0) {
            MPP_LOGE("%s:: fail to allocate dst buffer[%d]", __func__, mCurrentDstBuf);
            goto save_frame_info;
        }
        if (mDstImgs[mCurrentDstBuf].format != dst.format) {
            MPP_LOGD(eDebugMPP, "dst format is changed (%d -> %d)",
                    mDstImgs[mCurrentDstBuf].format, dst.format);
            mDstImgs[mCurrentDstBuf].format = dst.format;
        }
    }

    if ((realloc == false) && canUsePrevFrame()) {
        mCurrentDstBuf = (mCurrentDstBuf + NUM_MPP_DST_BUFS(mLogicalType) - 1)% NUM_MPP_DST_BUFS(mLogicalType);
        MPP_LOGD(eDebugMPP|eDebugFence, "Reuse previous frame, dstImg[%d]", mCurrentDstBuf);
        for (uint32_t i = 0; i < mAssignedSources.size(); i++) {
            mAssignedSources[i]->mSrcImg.acquireFenceFd =
                fence_close(mAssignedSources[i]->mSrcImg.acquireFenceFd,
                        mAssignedDisplay, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_G2D);
        }
        goto save_frame_info;
    }

    /* G2D or sclaer case */
    if ((ret = doPostProcessingInternal()) < 0) {
        MPP_LOGE("%s:: fail to post processing, ret %d",
                __func__, ret);
        goto save_frame_info;
    }

save_frame_info:
    /* Save current frame information for next frame*/
    mPrevAssignedDisplayType = mAssignedDisplay->mType;
    mPrevFrameInfo.srcNum = (uint32_t)mAssignedSources.size();
    for (uint32_t i = 0; i < mPrevFrameInfo.srcNum; i++) {
        mPrevFrameInfo.srcInfo[i] = mAssignedSources[i]->mSrcImg;
        mPrevFrameInfo.dstInfo[i] = mAssignedSources[i]->mMidImg;
    }

    MPP_LOGD(eDebugMPP, "mPrevAssignedState: %d, mPrevAssignedDisplayType: %d--------------",
            mAssignedState, mAssignedDisplay->mType);

    return ret;
}

/*
 * This function should be called after doPostProcessing()
 * because doPostProcessing() sets
 * mSrcImgs[].mppImg.releaseFenceFd
 */
int32_t ExynosMPP::getSrcReleaseFence(uint32_t srcIndex)
{
    if (srcIndex >= NUM_MPP_SRC_BUFS)
        return -EINVAL;

    return mSrcImgs[srcIndex].acrylicReleaseFenceFd;

    return -EINVAL;
}

int32_t ExynosMPP::resetSrcReleaseFence()
{
    MPP_LOGD(eDebugFence, "");
    for (uint32_t i = 0; i < mAssignedSources.size(); i++) {
        mSrcImgs[i].acrylicReleaseFenceFd = -1;
    }
    return NO_ERROR;
}

int32_t ExynosMPP::getDstImageInfo(exynos_image *img)
{
    if ((mCurrentDstBuf < 0) || (mCurrentDstBuf >= NUM_MPP_DST_BUFS(mLogicalType)) ||
        (mAssignedDisplay == NULL)) {
        MPP_LOGE("mCurrentDstBuf(%d), mAssignedDisplay(%p)", mCurrentDstBuf, mAssignedDisplay);
        return -EINVAL;
    }

    memset(img, 0, sizeof(exynos_image));
    img->acquireFenceFd = -1;
    img->releaseFenceFd = -1;

    if (needCompressDstBuf()) {
        img->compressionInfo.type = COMP_TYPE_AFBC;
        img->compressionInfo.modifier = AFBC_FORMAT_MOD_BLOCK_SIZE_16x16;
    }

    if (mDstImgs[mCurrentDstBuf].bufferHandle == NULL) {
        img->acquireFenceFd = -1;
        img->releaseFenceFd = -1;
        return -EFAULT;
    } else {
        img->bufferHandle = mDstImgs[mCurrentDstBuf].bufferHandle;
        img->compressionInfo = getCompressionInfo(img->bufferHandle);
        VendorGraphicBufferMeta gmeta(img->bufferHandle);
        img->fullWidth = gmeta.stride;
        img->fullHeight = gmeta.vstride;
        if ((mLogicalType == MPP_LOGICAL_G2D_RGB) ||
            (mLogicalType == MPP_LOGICAL_G2D_COMBO)) {
            if (mAssignedSources.size() == 1) {
                img->x = mAssignedSources[0]->mDstImg.x;
                img->y = mAssignedSources[0]->mDstImg.y;
                img->w = mAssignedSources[0]->mDstImg.w;
                img->h = mAssignedSources[0]->mDstImg.h;
            } else {
                img->x = 0;
                img->y = 0;
                img->w = mAssignedDisplay->mXres;
                img->h = mAssignedDisplay->mXres;
            }
        } else {
            img->x = mAssignedSources[0]->mMidImg.x;
            img->y = mAssignedSources[0]->mMidImg.y;
            img->w = mAssignedSources[0]->mMidImg.w;
            img->h = mAssignedSources[0]->mMidImg.h;
            img->needColorTransform =
                mAssignedSources[0]->mMidImg.needColorTransform;
        }

        img->format = mDstImgs[mCurrentDstBuf].format;
        MPP_LOGD(eDebugFence, "get dstBuf[%d] accquireFence(%d)", mCurrentDstBuf,
                mDstImgs[mCurrentDstBuf].acrylicAcquireFenceFd);
        img->acquireFenceFd = mDstImgs[mCurrentDstBuf].acrylicAcquireFenceFd;
        img->releaseFenceFd = mDstImgs[mCurrentDstBuf].acrylicReleaseFenceFd;
        img->dataSpace = mDstImgs[mCurrentDstBuf].dataspace;
    }
    return NO_ERROR;
}

/*
 * This function should be called after getDstReleaseFence()
 * by ExynosDisplay
 */
int32_t ExynosMPP::setDstAcquireFence(int acquireFence)
{

    int dstBufIndex = 0;

    dstBufIndex = mPrivDstBuf;

    if (mPrivDstBuf == mCurrentDstBuf)
        MPP_LOGD(eDebugFence|eDebugMPP,
                "M2MMPP : same buffer was reused idx %d, %d",mPrivDstBuf, mCurrentDstBuf);

    if (dstBufIndex < 0 || dstBufIndex >= NUM_MPP_DST_BUFS(mLogicalType)) {
        // TODO fence_close..
        acquireFence = fence_close(acquireFence, mAssignedDisplay, FENCE_TYPE_DST_ACQUIRE, FENCE_IP_ALL);
        mPrivDstBuf = mCurrentDstBuf;
        return -EINVAL;
    }

    if (acquireFence < 0) {
        mPrivDstBuf = mCurrentDstBuf;
        return -EINVAL;
    }

    if (mDstImgs[dstBufIndex].acrylicAcquireFenceFd >= 0) {
        MPP_LOGD(eDebugFence,"mDstImgs[%d].acrylicAcquireFenceFd: %d is closed", dstBufIndex,
                mDstImgs[dstBufIndex].acrylicAcquireFenceFd);
        fence_close(mDstImgs[dstBufIndex].acrylicAcquireFenceFd, mAssignedDisplay,
                FENCE_TYPE_DST_ACQUIRE, FENCE_IP_ALL);
    }
    if (mPhysicalType == MPP_MSC)
        mDstImgs[dstBufIndex].acrylicAcquireFenceFd =
            hwcCheckFenceDebug(mAssignedDisplay, FENCE_TYPE_DST_ACQUIRE, FENCE_IP_MSC, acquireFence);
    else
        mDstImgs[dstBufIndex].acrylicAcquireFenceFd =
            hwcCheckFenceDebug(mAssignedDisplay, FENCE_TYPE_DST_ACQUIRE, FENCE_IP_G2D, acquireFence);

    MPP_LOGD(eDebugFence,"->mDstImgs[%d].acrylicAcquireFenceFd: %d", dstBufIndex,
            mDstImgs[dstBufIndex].acrylicAcquireFenceFd);

    mPrivDstBuf = mCurrentDstBuf;

    return NO_ERROR;
}

int32_t ExynosMPP::resetDstReleaseFence()
{
    MPP_LOGD(eDebugFence, "");

    if (mCurrentDstBuf < 0 || mCurrentDstBuf >= NUM_MPP_DST_BUFS(mLogicalType))
        return -EINVAL;

    mDstImgs[mCurrentDstBuf].acrylicReleaseFenceFd = -1;

    return NO_ERROR;
}

int32_t ExynosMPP::requestHWStateChange(uint32_t state)
{
    MPP_LOGD(eDebugMPP|eDebugFence|eDebugBuf, "state: %d", state);
    /* Set HW state to running */
    if (mHWState == state) {
        if ((mPhysicalType == MPP_G2D) && (state == MPP_HW_STATE_IDLE) && (mHWBusyFlag == false)) {
            int ret = NO_ERROR;
            if ((ret = prioritize(-1)) != NO_ERROR)
                MPP_LOGI("prioritize (%d) will be applied on next work", ret);
        }
        return NO_ERROR;
    }

    if (state == MPP_HW_STATE_RUNNING) {
        mHWState = MPP_HW_STATE_RUNNING;
    } else if (state == MPP_HW_STATE_IDLE) {
        if (mLastStateFenceFd >= 0) {
            mResourceManageThread->addStateFence(mLastStateFenceFd);
        } else {
            mHWState = MPP_HW_STATE_IDLE;
        }

        mLastStateFenceFd = -1;

        if ((mPhysicalType == MPP_G2D) && (mHWBusyFlag == false)) {
            int ret = NO_ERROR;
            if ((ret = prioritize(-1)) != NO_ERROR)
                MPP_LOGI("prioritize (%d) is not applied on next work", ret);
        }

        /* Free all of output buffers */
        if (mMPPType == MPP_TYPE_M2M) {
            for(uint32_t i = 0; i < NUM_MPP_DST_BUFS(mLogicalType); i++) {
                exynos_mpp_img_info freeDstBuf = mDstImgs[i];
                memset(&mDstImgs[i], 0, sizeof(mDstImgs[i]));
                mDstImgs[i].acrylicAcquireFenceFd = freeDstBuf.acrylicAcquireFenceFd;
                mDstImgs[i].acrylicReleaseFenceFd = freeDstBuf.acrylicReleaseFenceFd;
                freeDstBuf.acrylicAcquireFenceFd = -1;
                freeDstBuf.acrylicReleaseFenceFd = -1;

                if (mFreeOutBufFlag == true) {
                    MPP_LOGD(eDebugMPP|eDebugFence|eDebugBuf, "free outbuf[%d] %p",
                            i, freeDstBuf.bufferHandle);
                    if (freeDstBuf.bufferHandle != NULL && mAllocOutBufFlag) {
                        freeOutBuf(freeDstBuf);
                    }
                } else {
                    mDstImgs[i].bufferHandle = freeDstBuf.bufferHandle;
                    mDstImgs[i].bufferType = freeDstBuf.bufferType;
                    mDstImgs[i].assignedDisplay = freeDstBuf.assignedDisplay;
                }
            }
        }

        for (uint32_t i = 0; i < NUM_MPP_SRC_BUFS; i++)
        {
            if (mSrcImgs[i].mppLayer != NULL) {
                delete mSrcImgs[i].mppLayer;
                mSrcImgs[i].mppLayer = NULL;
            }
        }
        memset(&mPrevFrameInfo, 0, sizeof(mPrevFrameInfo));
        for (int i = 0; i < NUM_MPP_SRC_BUFS; i++) {
            mPrevFrameInfo.srcInfo[i].acquireFenceFd = -1;
            mPrevFrameInfo.srcInfo[i].releaseFenceFd = -1;
            mPrevFrameInfo.dstInfo[i].acquireFenceFd = -1;
            mPrevFrameInfo.dstInfo[i].releaseFenceFd = -1;
        }
    }

    return NO_ERROR;
}

int32_t ExynosMPP::setHWStateFence(int32_t fence)
{
    MPP_LOGD(eDebugFence, "Update HWState fence, Close(%d), set(%d)",
            mLastStateFenceFd, fence);
    mLastStateFenceFd = fence;

    return NO_ERROR;
}

/**
 * @param ..
 * @return int32_t
 */
int32_t ExynosMPP::setupRestriction() {

    MPP_LOGD(eDebugMPP, "mPhysicalType(%d)", mPhysicalType);

    for (uint32_t i = 0; i < RESTRICTION_MAX; i++) {
        const restriction_size_element *restriction_size_table = mResourceManager->mSizeRestrictions[i];
        for (uint32_t j = 0; j < mResourceManager->mSizeRestrictionCnt[i]; j++) {
            if (restriction_size_table[j].key.hwType == mPhysicalType) {
                if ((restriction_size_table[j].key.nodeType == NODE_SRC) ||
                        (restriction_size_table[j].key.nodeType == NODE_NONE)) {
                    memcpy(&mSrcSizeRestrictions[i], &restriction_size_table[j].sizeRestriction,
                            sizeof(mSrcSizeRestrictions[i]));
                    MPP_LOGD(eDebugMPP, "\tSet mSrcSizeRestrictions[%d], "
                            "[%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d]",
                            i, mSrcSizeRestrictions[i].maxDownScale, mSrcSizeRestrictions[i].maxUpScale,
                            mSrcSizeRestrictions[i].maxFullWidth, mSrcSizeRestrictions[i].maxFullHeight,
                            mSrcSizeRestrictions[i].minFullWidth, mSrcSizeRestrictions[i].minFullHeight,
                            mSrcSizeRestrictions[i].fullWidthAlign, mSrcSizeRestrictions[i].fullHeightAlign,
                            mSrcSizeRestrictions[i].maxCropWidth, mSrcSizeRestrictions[i].maxCropHeight,
                            mSrcSizeRestrictions[i].minCropWidth, mSrcSizeRestrictions[i].minCropHeight,
                            mSrcSizeRestrictions[i].cropXAlign, mSrcSizeRestrictions[i].cropYAlign,
                            mSrcSizeRestrictions[i].cropWidthAlign, mSrcSizeRestrictions[i].cropHeightAlign);

                }
                if ((restriction_size_table[j].key.nodeType == NODE_DST) ||
                        (restriction_size_table[j].key.nodeType == NODE_NONE)) {
                    memcpy(&mDstSizeRestrictions[i], &restriction_size_table[j].sizeRestriction,
                            sizeof(mDstSizeRestrictions[i]));
                    MPP_LOGD(eDebugMPP, "\tSet mDstSizeRestrictions[%d], "
                            "[%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d]",
                            i, mDstSizeRestrictions[i].maxDownScale, mDstSizeRestrictions[i].maxUpScale,
                            mDstSizeRestrictions[i].maxFullWidth, mDstSizeRestrictions[i].maxFullHeight,
                            mDstSizeRestrictions[i].minFullWidth, mDstSizeRestrictions[i].minFullHeight,
                            mDstSizeRestrictions[i].fullWidthAlign, mDstSizeRestrictions[i].fullHeightAlign,
                            mDstSizeRestrictions[i].maxCropWidth, mDstSizeRestrictions[i].maxCropHeight,
                            mDstSizeRestrictions[i].minCropWidth, mDstSizeRestrictions[i].minCropHeight,
                            mDstSizeRestrictions[i].cropXAlign, mDstSizeRestrictions[i].cropYAlign,
                            mDstSizeRestrictions[i].cropWidthAlign, mDstSizeRestrictions[i].cropHeightAlign);
                }
            }
        }
    }

    return NO_ERROR;
}

int64_t ExynosMPP::isSupported(ExynosDisplay &display, struct exynos_image &src, struct exynos_image &dst)
{
    uint32_t maxSrcWidth = getSrcMaxWidth(src);
    uint32_t maxSrcHeight = getSrcMaxHeight(src);
    uint32_t minSrcWidth = getSrcMinWidth(src);
    uint32_t minSrcHeight = getSrcMinHeight(src);
    uint32_t srcWidthAlign = getSrcWidthAlign(src);
    uint32_t srcHeightAlign = getSrcHeightAlign(src);

    uint32_t maxSrcCropWidth = getSrcMaxCropWidth(src);
    uint32_t maxSrcCropHeight = getSrcMaxCropHeight(src);
    uint32_t maxSrcCropSize = getSrcMaxCropSize(src);
    uint32_t minSrcCropWidth = getSrcMinCropWidth(src);
    uint32_t minSrcCropHeight = getSrcMinCropHeight(src);
    uint32_t srcCropWidthAlign = getSrcCropWidthAlign(src);
    uint32_t srcCropHeightAlign = getSrcCropHeightAlign(src);
    uint32_t srcXOffsetAlign = getSrcXOffsetAlign(src);
    uint32_t srcYOffsetAlign = getSrcYOffsetAlign(src);

    uint32_t maxDstWidth = getDstMaxWidth(dst);
    uint32_t maxDstHeight = getDstMaxHeight(dst);
    uint32_t minDstWidth = getDstMinWidth(dst);
    uint32_t minDstHeight = getDstMinHeight(dst);
    uint32_t dstWidthAlign = getDstWidthAlign(dst);
    uint32_t dstHeightAlign = getDstHeightAlign(dst);
    uint32_t dstXOffsetAlign = getDstXOffsetAlign(dst);
    uint32_t dstYOffsetAlign = getDstYOffsetAlign(dst);

    uint32_t maxDownscale = getMaxDownscale(display, src, dst);
    uint32_t maxUpscale = getMaxUpscale(src, dst);

    exynos_image rot_dst = dst;
    bool isPerpendicular = !!(src.transform & HAL_TRANSFORM_ROT_90);
    if (isPerpendicular) {
        rot_dst.w = dst.h;
        rot_dst.h = dst.w;
    }

    if (dst.w > maxDstWidth)
        return -eMPPExeedMaxDstWidth;
    else if (dst.h > maxDstHeight)
        return -eMPPExeedMaxDstHeight;
    else if (dst.w < minDstWidth)
        return -eMPPExeedMinDstWidth;
    else if (dst.h < minDstHeight)
        return -eMPPExeedMinDstHeight;
    else if (src.isDimLayer()) { // Dim layer
        if (isDimLayerSupported()) {
            return NO_ERROR;
        } else {
            return -eMPPUnsupportedDIMLayer;
        }
    }
    if (!isSupportedCapability(display, src))
        return -eMPPSaveCapability;
    else if (!isSrcFormatSupported(src))
        return -eMPPUnsupportedFormat;
    else if (!isDstFormatSupported(dst))
        return -eMPPUnsupportedFormat;
    else if (!isDataspaceSupportedByMPP(src, dst))
        return -eMPPUnsupportedCSC;
    else if (!isSupportedHDR(src, dst))
        return -eMPPUnsupportedDynamicMeta;
    else if (!isSupportedBlend(src))
        return -eMPPUnsupportedBlending;
    else if (!isSupportedTransform(src))
        return -eMPPUnsupportedRotation;
    else if (src.fullWidth < minSrcWidth)
        return -eMPPExeedMinSrcWidth;
    else if (src.fullHeight < minSrcHeight)
        return -eMPPExeedMinSrcHeight;
    else if (src.w < minSrcCropWidth)
        return -eMPPExeedSrcWCropMin;
    else if (src.h < minSrcCropHeight)
        return -eMPPExeedSrcHCropMin;
    else if ((dst.w % dstWidthAlign != 0) || (dst.h % dstHeightAlign != 0))
        return -eMPPNotAlignedDstSize;
    else if (src.w > rot_dst.w * maxDownscale)
        return -eMPPExeedMaxDownScale;
    else if (rot_dst.w > src.w * maxUpscale)
        return -eMPPExeedMaxUpScale;
    else if (src.h > rot_dst.h * maxDownscale)
        return -eMPPExeedMaxDownScale;
    else if (rot_dst.h > src.h * maxUpscale)
        return -eMPPExeedMaxUpScale;
    else if (!isSupportedDRM(src))
        return -eMPPUnsupportedDRM;
    else if (!isSupportedHStrideCrop(src))
        return -eMPPStrideCrop;
    else if (src.fullWidth > maxSrcWidth)
        return -eMPPExceedHStrideMaximum;
    else if (src.fullWidth % srcWidthAlign != 0)
        return -eMPPNotAlignedHStride;

    if ((src.w * src.h) > maxSrcCropSize)
        return -eMPPExeedSrcCropMax;

    if (getDrmMode(src.usageFlags) == NO_DRM) {
        if (src.fullHeight > maxSrcHeight)
            return -eMPPExceedVStrideMaximum;
        else if (src.fullHeight % srcHeightAlign != 0)
            return -eMPPNotAlignedVStride;
        else if (src.w > maxSrcCropWidth)
            return -eMPPExeedSrcWCropMax;
        else if (src.h > maxSrcCropHeight)
            return -eMPPExeedSrcHCropMax;
        else if ((src.w % srcCropWidthAlign != 0) || (src.h % srcCropHeightAlign != 0))
            return -eMPPNotAlignedCrop;
        else if ((src.x % srcXOffsetAlign != 0) || (src.y % srcYOffsetAlign != 0))
            return -eMPPNotAlignedOffset;
    }

    if ((dst.x % dstXOffsetAlign != 0) || (dst.y % dstYOffsetAlign != 0))
        return -eMPPNotAlignedOffset;

    if (!isSupportedCompression(src))
        return -eMPPUnsupportedCompression;

    if (!isSupportLayerColorTransform(src,dst))
        return -eMPPUnsupportedColorTransform;

    return NO_ERROR;
}

int32_t ExynosMPP::resetMPP()
{
    mAssignedState = MPP_ASSIGN_STATE_FREE;
    mAssignedDisplay = NULL;
    mAssignedSources.clear();
    resetUsedCapacity();
    mReservedDisplay = -1;
    mHWBusyFlag = false;

    return NO_ERROR;
}

int32_t ExynosMPP::resetAssignedState()
{
    for (int i = (int)mAssignedSources.size(); i-- > 0;) {
        ExynosMPPSource *mppSource = mAssignedSources[i];
        if (mppSource->mOtfMPP == this) {
            mppSource->mOtfMPP = NULL;
        }
        if (mppSource->mM2mMPP == this) {
            mppSource->mM2mMPP = NULL;
        }
        mAssignedSources.removeItemsAt(i);
    }

    /* Keep status if mAssignedState is MPP_ASSIGN_STATE_RESERVED */
    if ((mAssignedState & MPP_ASSIGN_STATE_ASSIGNED) &&
        (mAssignedSources.size() == 0)) {
        mAssignedState &= ~MPP_ASSIGN_STATE_ASSIGNED;
        mAssignedDisplay = NULL;
    }

    /* All mpp source are removed, reset capacity information */
    resetUsedCapacity();

    return NO_ERROR;
}

int32_t ExynosMPP::resetAssignedState(ExynosMPPSource *mppSource)
{
    bool needUpdateCapacity = false;
    for (int i = (int)mAssignedSources.size(); i-- > 0;) {
        ExynosMPPSource *source = mAssignedSources[i];
        if (source == mppSource) {
            if (mppSource->mM2mMPP == this) {
                mppSource->mM2mMPP = NULL;
            }
            /* Update information for used capacity */
            /* This should be called before mAssignedSources.removeItemsAt(mppSource) */
            needUpdateCapacity = removeCapacity(mppSource);

            mAssignedSources.removeItemsAt(i);

            if (needUpdateCapacity)
                updateUsedCapacity();

            break;
        }
    }

    /* Keep status if mAssignedState is MPP_ASSIGN_STATE_RESERVED */
    if ((mAssignedState & MPP_ASSIGN_STATE_ASSIGNED) &&
        (mAssignedSources.size() == 0)) {
        mAssignedState &= ~MPP_ASSIGN_STATE_ASSIGNED;
        mAssignedDisplay = NULL;
    }

    return NO_ERROR;
}

int32_t ExynosMPP::reserveMPP(int32_t displayId)
{
    mAssignedState |= MPP_ASSIGN_STATE_RESERVED;
    mReservedDisplay = displayId;

    return NO_ERROR;
}

int32_t ExynosMPP::assignMPP(ExynosDisplay *display, ExynosMPPSource* mppSource)
{
    mAssignedState |= MPP_ASSIGN_STATE_ASSIGNED;

    if (mMPPType == MPP_TYPE_OTF)
        mppSource->mOtfMPP = this;
    else if (mMPPType == MPP_TYPE_M2M)
        mppSource->mM2mMPP = this;
    else {
        MPP_LOGE("%s:: Invalid mppType(%d)", __func__, mMPPType);
        return -EINVAL;
    }

    mAssignedDisplay = display;

    /* Update information for used capacity */
    /* This should be called before mAssignedSources.add(mppSource) */
    bool needUpdateCapacity = addCapacity(mppSource);

    mAssignedSources.add(mppSource);

    MPP_LOGD(eDebugCapacity|eDebugMPP, "\tassigned to source(%p) type(%d), mAssignedSources(%zu)",
            mppSource, mppSource->mSourceType,
            mAssignedSources.size());

    if (needUpdateCapacity)
        updateUsedCapacity();

    if (mMaxSrcLayerNum > 1) {
        std::sort(mAssignedSources.begin(), mAssignedSources.end(), exynosMPPSourceComp);
    }

    return NO_ERROR;
}

uint32_t ExynosMPP::getSrcMaxBlendingNum(struct exynos_image __unused &src, struct exynos_image __unused &dst)
{
    uint32_t maxSrcLayerNum = mMaxSrcLayerNum;
    return maxSrcLayerNum;
}

uint32_t ExynosMPP::getAssignedSourceNum()
{
    return mAssignedSources.size();
}

/* Based on multi-resolution support */
void ExynosMPP::setDstAllocSize(uint32_t width, uint32_t height)
{
    switch(width) {
    case 720:
        mDstAllocatedSize = ((height >= 1480) ? DST_SIZE_HD_PLUS : DST_SIZE_HD);
        break;
    case 1080:
        mDstAllocatedSize = ((height >= 2220) ? DST_SIZE_FHD_PLUS : DST_SIZE_FHD);
        break;
    case 1440:
        mDstAllocatedSize = ((height >= 2960) ? DST_SIZE_WQHD_PLUS : DST_SIZE_WQHD);
        break;
    default:
        mDstAllocatedSize = DST_SIZE_UNKNOWN;
        break;
    }
}

dst_alloc_buf_size_t ExynosMPP::getDstAllocSize()
{
    return mDstAllocatedSize;
}

bool ExynosMPP::needPreAllocation()
{
    bool ret = false;

    if ((mLogicalType == MPP_LOGICAL_G2D_RGB) &&
        (mPreAssignDisplayList[mResourceManager->mDevice->mDisplayMode] == HWC_DISPLAY_PRIMARY_BIT))
        ret = true;

    return ret;
}

bool ExynosMPP::isAssignableState(ExynosDisplay *display, struct exynos_image &src, struct exynos_image &dst)
{
    bool isAssignable = false;

    if (mAssignedState == MPP_ASSIGN_STATE_FREE) {
        if (mHWState == MPP_HW_STATE_IDLE)
            isAssignable = true;
        else {
            if ((mPrevAssignedDisplayType < 0) ||
                ((uint32_t)mPrevAssignedDisplayType == display->mType))
                isAssignable = true;
            else
                isAssignable = false;
        }
    }

    if ((mAssignedState & MPP_ASSIGN_STATE_ASSIGNED) && (mAssignedState & MPP_ASSIGN_STATE_RESERVED))
    {
        if (mReservedDisplay == (int32_t)display->getId()) {
            if (mAssignedSources.size() < getSrcMaxBlendingNum(src, dst))
                isAssignable = true;
            else
                isAssignable = false;
        } else {
            isAssignable = false;
        }
    } else if ((mAssignedState & MPP_ASSIGN_STATE_ASSIGNED) && !(mAssignedState & MPP_ASSIGN_STATE_RESERVED)) {
        if (mAssignedSources.size() < getSrcMaxBlendingNum(src, dst))
            isAssignable = true;
        else
            isAssignable = false;
    } else if (mAssignedState & MPP_ASSIGN_STATE_RESERVED) {
        if (mReservedDisplay == (int32_t)display->getId())
            isAssignable = true;
        else
            isAssignable = false;
    }

    MPP_LOGD(eDebugMPP, "\tisAssignableState(%d), assigned size(%zu), getSrcMaxBlendingNum(%d)",
            isAssignable, mAssignedSources.size(), getSrcMaxBlendingNum(src, dst));
    return isAssignable;
}

bool ExynosMPP::isAssignable(ExynosDisplay *display, struct exynos_image &src,
                             struct exynos_image &dst, float totalUsedCapacity)
{
    bool isAssignable = isAssignableState(display, src, dst);
    return (isAssignable && hasEnoughCapa(display, src, dst, totalUsedCapacity));
}

bool ExynosMPP::hasEnoughCapa(ExynosDisplay *display, struct exynos_image &src,
                              struct exynos_image &dst, float totalUsedCapacity)
{
    if (mCapacity == -1)
        return true;

    MPP_LOGD(eDebugCapacity | eDebugMPP, "totalUsedCapacity(%f), mUsedCapacity(%f)",
             totalUsedCapacity, mUsedCapacity);

    /* mUsedCapacity should be re-calculated including src, dst passed as parameters*/
    totalUsedCapacity -= mUsedCapacity;

    float requiredCapacity = getRequiredCapacity(display, src, dst);

    MPP_LOGD(eDebugCapacity | eDebugMPP, "mCapacity(%f), usedCapacity(%f), RequiredCapacity(%f)",
             mCapacity, totalUsedCapacity, requiredCapacity);

    if (mCapacity >= (totalUsedCapacity + requiredCapacity))
        return true;
    else if (isCapacityExceptionCondition(totalUsedCapacity, requiredCapacity, src))
        return true;
    else
        return false;
}

bool ExynosMPP::isCapacityExceptionCondition(float totalUsedCapacity, float requiredCapacity,
                                             struct exynos_image &src)
{
    if ((hasHdrInfo(src) && (totalUsedCapacity == 0) &&
         (requiredCapacity < (mCapacity * MPP_HDR_MARGIN)))) {
        return true;
    } else {
        return false;
    }
}

void ExynosMPP::getPPCIndex(const struct exynos_image &src,
        const struct exynos_image &dst,
        uint32_t &formatIndex, uint32_t &rotIndex, uint32_t &scaleIndex,
        const struct exynos_image &criteria)
{
    formatIndex = 0;
    rotIndex = 0;
    scaleIndex = 0;

    /* Compare SBWC, AFBC and 10bitYUV420 first! because can be overlapped with other format */
    if (isFormatSBWC(criteria.format) && hasPPC(mPhysicalType, PPC_FORMAT_SBWC, PPC_ROT_NO))
        formatIndex = PPC_FORMAT_SBWC;
    else if (src.compressionInfo.type == COMP_TYPE_AFBC) {
        if ((isFormatRgb(criteria.format)) && hasPPC(mPhysicalType, PPC_FORMAT_AFBC_RGB, PPC_ROT_NO))
            formatIndex = PPC_FORMAT_AFBC_RGB;
        else if ((isFormatYUV(criteria.format)) && hasPPC(mPhysicalType, PPC_FORMAT_AFBC_YUV, PPC_ROT_NO))
            formatIndex = PPC_FORMAT_AFBC_YUV;
        else {
            formatIndex = PPC_FORMAT_RGB32;
            MPP_LOGW("%s:: AFBC PPC is not existed. Use default PPC", __func__);
        }
    } else if (isFormatP010(criteria.format) && hasPPC(mPhysicalType, PPC_FORMAT_P010, PPC_ROT_NO))
        formatIndex = PPC_FORMAT_P010;
    else if (isFormatYUV420(criteria.format) && hasPPC(mPhysicalType, PPC_FORMAT_YUV420, PPC_ROT_NO))
        formatIndex = PPC_FORMAT_YUV420;
    else if (isFormatYUV422(criteria.format) && hasPPC(mPhysicalType, PPC_FORMAT_YUV422, PPC_ROT_NO))
        formatIndex = PPC_FORMAT_YUV422;
    else
        formatIndex = PPC_FORMAT_RGB32;

    if (((criteria.transform & HAL_TRANSFORM_ROT_90) != 0) ||
        (mRotatedSrcCropBW > 0))
        rotIndex = PPC_ROT;
    else
        rotIndex = PPC_ROT_NO;

    uint32_t srcResolution = src.w * src.h;
    uint32_t dstResolution = dst.w * dst.h;

    if (mPhysicalType == MPP_G2D) {
        if (srcResolution == dstResolution) {
            scaleIndex = PPC_SCALE_NO;
        } else if (dstResolution > srcResolution) {
            /* scale up case */
            if (dstResolution >= (srcResolution * 4))
                scaleIndex = PPC_SCALE_UP_4_;
            else
                scaleIndex = PPC_SCALE_UP_1_4;
        } else {
            /* scale down case */
            if ((dstResolution * 16) <= srcResolution)
                scaleIndex = PPC_SCALE_DOWN_16_;
            else if (((dstResolution * 9) <= srcResolution) &&
                    (srcResolution < (dstResolution * 16)))
                scaleIndex = PPC_SCALE_DOWN_9_16;
            else if (((dstResolution * 4) <= srcResolution) &&
                    (srcResolution < (dstResolution * 9)))
                scaleIndex = PPC_SCALE_DOWN_4_9;
            else
                scaleIndex = PPC_SCALE_DOWN_1_4;
        }
    } else scaleIndex = 0; /* MSC doesn't refer scale Index */
}

float ExynosMPP::getPPC(const struct exynos_image &src,
        const struct exynos_image &dst, const struct exynos_image &criteria,
        const struct exynos_image *assignCheckSrc,
        const struct exynos_image *assignCheckDst)
{
    float PPC = 0;
    uint32_t formatIndex = 0;
    uint32_t rotIndex = 0;
    uint32_t scaleIndex = 0;

    getPPCIndex(src, dst, formatIndex, rotIndex, scaleIndex, criteria);

    if ((rotIndex == PPC_ROT_NO) && (assignCheckSrc != NULL) &&
        ((assignCheckSrc->transform & HAL_TRANSFORM_ROT_90) != 0)) {
        rotIndex = PPC_ROT;
    }

    if (mPhysicalType == MPP_G2D || mPhysicalType == MPP_MSC) {
        if (hasPPC(mPhysicalType, formatIndex, rotIndex)) {
            PPC = ppc_table_map.at(PPC_IDX(mPhysicalType, formatIndex, rotIndex)).ppcList[scaleIndex];
        }
    }

    if (PPC == 0) {
        MPP_LOGE("%s:: mPhysicalType(%d), formatIndex(%d), rotIndex(%d), scaleIndex(%d), PPC(%f) is not valid",
                __func__, mPhysicalType, formatIndex, rotIndex, scaleIndex, PPC);
        PPC = 0.000001;  /* It means can't use mPhysicalType H/W  */
    }

    MPP_LOGD(eDebugCapacity, "srcW(%d), srcH(%d), dstW(%d), dstH(%d), rot(%d)"
            "formatIndex(%d), rotIndex(%d), scaleIndex(%d), PPC(%f)",
            src.w, src.h, dst.w, dst.h, src.transform,
            formatIndex, rotIndex, scaleIndex, PPC);
    return PPC;
}

float ExynosMPP::getAssignedCapacity()
{
    float capacity = 0;
    float baseCycles = 0;
    uint32_t rotIndex = 0;

    if (mPhysicalType != MPP_G2D)
        return 0;

    /*
     * Client target is assigned to m2mMPP
     * even if capacity is not enough
     */
    if ((mAssignedDisplay != NULL) &&
        (mAssignedDisplay->mType == HWC_DISPLAY_VIRTUAL))
        return 0;


    for (uint32_t i = 0; i < mAssignedSources.size(); i++) {
        if ((mAssignedSources[i]->mSrcImg.transform & HAL_TRANSFORM_ROT_90) != 0)
            rotIndex = PPC_ROT;
    }

    MPP_LOGD(eDebugCapacity, "Check all of assigned layers cycles");
    /* PPC of layers that were added before should be changed */
    /* Check cycles of all assigned layers again */
    if ((mAssignedDisplay != NULL) && (mMaxSrcLayerNum > 1)) {
        baseCycles += ((mAssignedDisplay->mXres * mAssignedDisplay->mYres) / G2D_BASE_PPC_COLORFILL);
        MPP_LOGD(eDebugCapacity, "colorfill cycles: %f, total cycles: %f",
                ((mAssignedDisplay->mXres * mAssignedDisplay->mYres) / G2D_BASE_PPC_COLORFILL), baseCycles);
    }

    for (uint32_t i = 0; i < mAssignedSources.size(); i++) {
        float srcCycles = 0;
        uint32_t srcResolution = mAssignedSources[i]->mSrcImg.w * mAssignedSources[i]->mSrcImg.h;
        uint32_t dstResolution = mAssignedSources[i]->mMidImg.w * mAssignedSources[i]->mMidImg.h;
        uint32_t maxResolution = max(srcResolution, dstResolution);
        float PPC = 0;

        if (mAssignedSources[i]->mSrcImg.layerFlags & EXYNOS_HWC_DIM_LAYER) {
            PPC = G2D_BASE_PPC_COLORFILL;
        } else {
            PPC = getPPC(mAssignedSources[i]->mSrcImg, mAssignedSources[i]->mMidImg, mAssignedSources[i]->mSrcImg,
                    &mAssignedSources[i]->mSrcImg, &mAssignedSources[i]->mMidImg);
        }
        srcCycles = maxResolution/PPC;

        /* Hdr and drm layer is exception */
        if ((hasHdrInfo(mAssignedSources[i]->mSrcImg) ||
            (getDrmMode(mAssignedSources[i]->mSrcImg.usageFlags) != NO_DRM))) {
            MPP_LOGD(eDebugCapacity, "Src[%d] is skipped(drm or hdr), cycles: %f, PPC: %f, srcResolution: %d, dstResolution: %d, rot(%d)",
                    i, srcCycles, PPC, srcResolution, dstResolution, mAssignedSources[i]->mSrcImg.transform);
            continue;
        }

        baseCycles += srcCycles;

        MPP_LOGD(eDebugCapacity, "Src[%d] cycles: %f, total cycles: %f, PPC: %f, srcResolution: %d, dstResolution: %d, rot(%d)",
                i, srcCycles, baseCycles, PPC, srcResolution, dstResolution, mAssignedSources[i]->mSrcImg.transform);
    }

    capacity = baseCycles / mClockKhz;

    return capacity;
}

float ExynosMPP::getRequiredCapacity(ExynosDisplay *display, struct exynos_image &src,
        struct exynos_image &dst)
{
    float capacity = 0;
    float cycles = 0;
    if (mPhysicalType == MPP_G2D) {
        /* Initialize value with the cycles that were already assigned */
        float baseCycles = mUsedBaseCycles;
        float srcCycles = 0;
        uint32_t srcResolution = src.w * src.h;
        uint32_t dstResolution = dst.w * dst.h;
        uint32_t maxResolution = max(srcResolution, dstResolution);
        float curBaseCycles = 0;
        float PPC = 0;

        if ((mAssignedSources.size() == 0) ||
            (mRotatedSrcCropBW != 0) ||
            ((mRotatedSrcCropBW == 0) &&
             ((src.transform & HAL_TRANSFORM_ROT_90) == 0))) {
            /* Just add cycles for current layer */
            if ((mAssignedSources.size() == 0) &&
                (display != NULL) && (mMaxSrcLayerNum > 1)) {
                curBaseCycles = ((display->mXres * display->mYres) / G2D_BASE_PPC_COLORFILL);
                MPP_LOGD(eDebugCapacity, "There is no assigned layer. Colorfill cycles: %f should be added",
                        curBaseCycles);
            }
            curBaseCycles += getRequiredBaseCycles(src, dst);
            baseCycles += curBaseCycles;
            MPP_LOGD(eDebugCapacity, "mUsedBaseCycles was %f, Add base cycles %f, totalBaseCycle(%f)",
                    mUsedBaseCycles, curBaseCycles, baseCycles);
        } else {
            /* Recalculate cycles for all of layers */
            baseCycles = 0;
            MPP_LOGD(eDebugCapacity, "Check all of assigned layers cycles");
            /* PPC of layers that were added before should be changed */
            /* Check cycles of all assigned layers again */
            if ((display != NULL) && (mMaxSrcLayerNum > 1)) {
                baseCycles += ((display->mXres * display->mYres) / G2D_BASE_PPC_COLORFILL);
                MPP_LOGD(eDebugCapacity, "colorfill cycles: %f, total cycles: %f",
                        ((display->mXres * display->mYres) / G2D_BASE_PPC_COLORFILL), cycles);
            }

            for (uint32_t i = 0; i < mAssignedSources.size(); i++) {
                float assignedSrcCycles = 0;
                uint32_t assignedSrcResolution = mAssignedSources[i]->mSrcImg.w * mAssignedSources[i]->mSrcImg.h;
                uint32_t assignedDstResolution = mAssignedSources[i]->mMidImg.w * mAssignedSources[i]->mMidImg.h;
                uint32_t assignedMaxResolution = max(assignedSrcResolution, assignedDstResolution);
                float assignedPPC = getPPC(mAssignedSources[i]->mSrcImg, mAssignedSources[i]->mMidImg,
                        mAssignedSources[i]->mSrcImg, &src, &dst);

                assignedSrcCycles = assignedMaxResolution/assignedPPC;
                baseCycles += assignedSrcCycles;

                MPP_LOGD(eDebugCapacity, "Src[%d] cycles: %f, total cycles: %f, PPC: %f, srcResolution: %d, dstResolution: %d, rot(%d)",
                        i, assignedSrcCycles, baseCycles, assignedPPC, assignedSrcResolution, assignedDstResolution, mAssignedSources[i]->mSrcImg.transform);
            }

            PPC = getPPC(src, dst, src, &src, &dst);

            srcCycles = maxResolution/PPC;
            baseCycles += srcCycles;

            MPP_LOGD(eDebugCapacity, "check mppSource cycles: %f, total cycles: %f, PPC: %f, srcResolution: %d, dstResolution: %d, rot(%d)",
                    srcCycles, baseCycles, PPC, srcResolution, dstResolution, src.transform);
        }

        capacity = baseCycles / mClockKhz;

        MPP_LOGD(eDebugCapacity, "baseCycles: %f, capacity: %f",
                baseCycles, capacity);
    } else if (mPhysicalType == MPP_MSC) {
        /* Initialize value with the capacity that were already assigned */
        capacity = mUsedCapacity;

        /* Just add capacity for current layer */
        float srcPPC = getPPC(src, dst, src);
        float dstPPC = getPPC(src, dst, dst);
        float srcCapacity = (float((src.w * src.h))) / (mClockKhz * srcPPC);
        float dstCapacity = (float((dst.w * dst.h))) / (mClockKhz * dstPPC);

        capacity += max(srcCapacity, dstCapacity);

    }

    return capacity;
}

float ExynosMPP::getRequiredBaseCycles(struct exynos_image &src, struct exynos_image &dst)
{
    if (mPhysicalType != MPP_G2D)
        return 0;

    uint32_t srcResolution = src.w * src.h;
    uint32_t dstResolution = dst.w * dst.h;
    uint32_t maxResolution = max(srcResolution, dstResolution);

    return maxResolution/(float)getPPC(src, dst, src);
}

bool ExynosMPP::addCapacity(ExynosMPPSource* mppSource)
{
    if ((mppSource == NULL) || mCapacity == -1)
        return false;

    if (mPhysicalType == MPP_G2D) {
        bool needUpdateCapacity = true;
        if ((mAssignedSources.size() == 0) ||
            (mRotatedSrcCropBW != 0) ||
            ((mRotatedSrcCropBW == 0) &&
             ((mppSource->mSrcImg.transform & HAL_TRANSFORM_ROT_90) == 0))) {
            needUpdateCapacity = false;
        }

        if (needUpdateCapacity)
            return true;

        if ((mMaxSrcLayerNum > 1) &&
            (mAssignedSources.size() == 0)) {
            if (mAssignedDisplay != NULL) {
                /* This will be the first mppSource that is assigned to the ExynosMPP */
                /* Add capacity for background */
                mUsedBaseCycles += ((mAssignedDisplay->mXres * mAssignedDisplay->mYres) / G2D_BASE_PPC_COLORFILL);
                MPP_LOGD(eDebugCapacity, "\tcolorfill cycles: %f, total cycles: %f",
                        ((mAssignedDisplay->mXres * mAssignedDisplay->mYres) / G2D_BASE_PPC_COLORFILL), mUsedBaseCycles);
            } else {
                MPP_LOGE("mAssignedDisplay is null");
            }
        }

        float baseCycles = getRequiredBaseCycles(mppSource->mSrcImg, mppSource->mMidImg);
        mUsedBaseCycles += baseCycles;

        uint32_t srcResolution = mppSource->mSrcImg.w * mppSource->mSrcImg.h;
        uint32_t dstResolution = mppSource->mMidImg.w * mppSource->mMidImg.h;
        if ((mppSource->mSrcImg.transform & HAL_TRANSFORM_ROT_90) == 0)
            mNoRotatedSrcCropBW += srcResolution;
        else
            mRotatedSrcCropBW += srcResolution;

        mUsedCapacity = mUsedBaseCycles / mClockKhz;

        MPP_LOGD(eDebugCapacity, "src num: %zu base cycle is added: %f, mUsedBaseCycles: %f, mUsedCapacity(%f), srcResolution: %d, dstResolution: %d, rot: %d, mNoRotatedSrcCropBW(%d), mRotatedSrcCropBW(%d)",
                mAssignedSources.size(),
                baseCycles, mUsedBaseCycles, mUsedCapacity, srcResolution, dstResolution,
                mppSource->mSrcImg.transform, mNoRotatedSrcCropBW, mRotatedSrcCropBW);
    } else if (mPhysicalType == MPP_MSC) {
        mUsedCapacity = getRequiredCapacity(NULL, mppSource->mSrcImg, mppSource->mMidImg);
    }

    return false;
}

bool ExynosMPP::removeCapacity(ExynosMPPSource* mppSource)
{
    if ((mppSource == NULL) || (mCapacity == -1))
        return false;

    if (mPhysicalType == MPP_G2D) {
        uint32_t srcResolution = mppSource->mSrcImg.w * mppSource->mSrcImg.h;
        uint32_t dstResolution = mppSource->mDstImg.w * mppSource->mDstImg.h;

        uint32_t prevRotatedSrcCropBW = mRotatedSrcCropBW;

        if (mppSource->mSrcImg.transform == 0)
            mNoRotatedSrcCropBW -= srcResolution;
        else
            mRotatedSrcCropBW -= srcResolution;

        if ((prevRotatedSrcCropBW > 0) && (mRotatedSrcCropBW == 0))
            return true;

        float baseCycles = getRequiredBaseCycles(mppSource->mSrcImg, mppSource->mMidImg);
        mUsedBaseCycles -= baseCycles;

        mUsedCapacity = mUsedBaseCycles / mClockKhz;

        MPP_LOGD(eDebugCapacity, "src num: %zu, base cycle is removed: %f, mUsedBaseCycles: %f, mUsedCapacity(%f), srcResolution: %d, dstResolution: %d, rot: %d, mNoRotatedSrcCropBW(%d), mRotatedSrcCropBW(%d)",
                mAssignedSources.size(),
                baseCycles, mUsedBaseCycles, mUsedCapacity, srcResolution, dstResolution,
                mppSource->mSrcImg.transform, mNoRotatedSrcCropBW, mRotatedSrcCropBW);
    } else if (mPhysicalType == MPP_MSC) {
        exynos_image &src = mppSource->mSrcImg;
        exynos_image &dst = mppSource->mDstImg;
        uint32_t srcResolution = src.w * src.h;
        uint32_t dstResolution = dst.w * dst.h;

        float srcCapacity = (float)srcResolution / getPPC(src, dst, src);
        float dstCapacity  = (float)dstResolution  / getPPC(src, dst, dst);

        mUsedCapacity -= max(srcCapacity, dstCapacity);
    }

    return false;
}

void ExynosMPP::resetUsedCapacity()
{
    mUsedCapacity = 0;
    mUsedBaseCycles = 0;
    mRotatedSrcCropBW = 0;
    mNoRotatedSrcCropBW = 0;
}

int32_t ExynosMPP::updateUsedCapacity()
{
    int32_t ret = NO_ERROR;
    if (mCapacity == -1)
        return ret;

    float capacity = 0;
    mUsedCapacity = 0;

    mRotatedSrcCropBW = 0;
    mNoRotatedSrcCropBW = 0;

    if ((mPhysicalType == MPP_G2D) &&
        (mAssignedDisplay != NULL) &&
        (mAssignedSources.size() > 0)) {
        float cycles = 0;

        if (mMaxSrcLayerNum > 1) {
            cycles += ((mAssignedDisplay->mXres * mAssignedDisplay->mYres) / G2D_BASE_PPC_COLORFILL);
            MPP_LOGD(eDebugCapacity, "\tcolorfill cycles: %f, total cycles: %f",
                    ((mAssignedDisplay->mXres * mAssignedDisplay->mYres) / G2D_BASE_PPC_COLORFILL), cycles);
        }
        for (uint32_t i = 0; i < mAssignedSources.size(); i++) {
            uint32_t srcResolution = mAssignedSources[i]->mSrcImg.w * mAssignedSources[i]->mSrcImg.h;
            if ((mAssignedSources[i]->mSrcImg.transform & HAL_TRANSFORM_ROT_90) == 0)
                mNoRotatedSrcCropBW += srcResolution;
            else
                mRotatedSrcCropBW += srcResolution;
        }
        MPP_LOGD(eDebugCapacity, "mNoRotatedSrcCropBW(%d), mRotatedSrcCropBW(%d)",
                mNoRotatedSrcCropBW, mRotatedSrcCropBW);
        for (uint32_t i = 0; i < mAssignedSources.size(); i++) {
            float srcCycles = 0;
            uint32_t srcResolution = mAssignedSources[i]->mSrcImg.w * mAssignedSources[i]->mSrcImg.h;
            uint32_t dstResolution = mAssignedSources[i]->mMidImg.w * mAssignedSources[i]->mMidImg.h;
            uint32_t maxResolution = max(srcResolution, dstResolution);
            float PPC = getPPC(mAssignedSources[i]->mSrcImg, mAssignedSources[i]->mMidImg, mAssignedSources[i]->mSrcImg);
            srcCycles = maxResolution/PPC;
            cycles += srcCycles;

            MPP_LOGD(eDebugCapacity, "Src[%d] cycles: %f, total cycles: %f, PPC: %f, srcResolution: %d, dstResolution: %d, rot(%d)",
                    i, srcCycles, cycles, PPC, srcResolution, dstResolution, mAssignedSources[i]->mSrcImg.transform);
        }

        mUsedBaseCycles = cycles;
        capacity = cycles / mClockKhz;

        mUsedCapacity = capacity;

    }
    MPP_LOGD(eDebugCapacity, "assigned layer size(%zu), mUsedCapacity: %f", mAssignedSources.size(), mUsedCapacity);

    return mUsedCapacity;
}

uint32_t ExynosMPP::getRestrictionClassification(const struct exynos_image &img) const {
    return !!(isFormatRgb(img.format) == false);
}

int ExynosMPP::prioritize(int priority)
{
    if ((mPhysicalType != MPP_G2D) ||
        (mAcrylicHandle == NULL)) {
        MPP_LOGE("invalid function call");
        return -1;
    }
    int ret = NO_ERROR;
    ret = mAcrylicHandle->prioritize(priority);

    if ((priority > 0) && (ret == 1))
    {
        /* G2D Driver returned EBUSY */
        mHWBusyFlag = true;
    }
    MPP_LOGD(eDebugMPP, "set resource prioritize (%d), ret(%d), mHWBusyFlag(%d)", priority, ret, mHWBusyFlag);

    return ret;
}

uint32_t ExynosMPP::increaseDstBuffIndex()
{
    if (mAllocOutBufFlag)
        mCurrentDstBuf = (mCurrentDstBuf + 1) % NUM_MPP_DST_BUFS(mLogicalType);
    return mCurrentDstBuf;
}

void ExynosMPP::reloadResourceForHWFC()
{
    ALOGI("reloadResourceForHWFC()");
    delete mAcrylicHandle;
    mAcrylicHandle = AcrylicFactory::createAcrylic("default_compositor");
    if (mAcrylicHandle == NULL) {
        MPP_LOGE("Fail to allocate compositor");
    } else {
        mAcrylicHandle->setDefaultColor(0, 0, 0, 0);
        MPP_LOGI("The resource is reloaded for HWFC: %p", mAcrylicHandle);
    }
    for (uint32_t i = 0; i < NUM_MPP_SRC_BUFS; i++)
    {
        if (mSrcImgs[i].mppLayer != NULL) {
            delete mSrcImgs[i].mppLayer;
            mSrcImgs[i].mppLayer = NULL;
        }
    }
}

void ExynosMPP::setTargetDisplayLuminance(uint16_t min, uint16_t max)
{
    MPP_LOGD(eDebugMPP, "%s: min(%d), max(%d)", __func__, min, max);
    if (mAcrylicHandle == NULL) {
        MPP_LOGE("mAcrylicHandle is NULL");
    } else
        mAcrylicHandle->setTargetDisplayLuminance(min, max);
}

void ExynosMPP::setTargetDisplayDevice(int device)
{
    ALOGI("%s: device(%d)", __func__, device);
    if (mAcrylicHandle == NULL) {
        MPP_LOGE("mAcrylicHandle is NULL");
    } else
        mAcrylicHandle->setTargetDisplayInfo(&device);
}

void ExynosMPP::dump(String8& result)
{
    int32_t assignedDisplayType = -1;
    if (mAssignedDisplay != NULL)
        assignedDisplayType = mAssignedDisplay->mType;

    result.appendFormat("%s: types mppType(%d), (p:%d, l:0x%2x), indexs(p:%d, l:%d), preAssignDisplay(0x%2x)\n",
            mName.c_str(), mMPPType, mPhysicalType, mLogicalType, mPhysicalIndex, mLogicalIndex, mPreAssignDisplayInfo);
    result.appendFormat("\tEnable: %d, HWState: %d, AssignedState: %d, assignedDisplay(%d)\n",
            mEnable, mHWState, mAssignedState, assignedDisplayType);
    result.appendFormat("\tPrevAssignedState: %d, PrevAssignedDisplayType: %d, ReservedDisplay: %d\n",
            mPrevAssignedState, mPrevAssignedDisplayType, mReservedDisplay);
    result.appendFormat("\tassinedSourceNum(%zu), Capacity(%f), CapaUsed(%f), mCurrentDstBuf(%d)\n",
            mAssignedSources.size(), mCapacity, mUsedCapacity, mCurrentDstBuf);

}

void ExynosMPP::closeFences()
{
    for (uint32_t i = 0; i < mAssignedSources.size(); i++)
    {
        mSrcImgs[i].acrylicAcquireFenceFd =
            fence_close(mSrcImgs[i].acrylicAcquireFenceFd, mAssignedDisplay,
                    FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_G2D);
        mSrcImgs[i].acrylicReleaseFenceFd =
            fence_close(mSrcImgs[i].acrylicReleaseFenceFd, mAssignedDisplay,
                    FENCE_TYPE_SRC_RELEASE, FENCE_IP_G2D);
    }

    mDstImgs[mCurrentDstBuf].acrylicAcquireFenceFd =
        fence_close(mDstImgs[mCurrentDstBuf].acrylicAcquireFenceFd, mAssignedDisplay,
                FENCE_TYPE_DST_ACQUIRE, FENCE_IP_G2D);
    mDstImgs[mCurrentDstBuf].acrylicReleaseFenceFd =
        fence_close(mDstImgs[mCurrentDstBuf].acrylicReleaseFenceFd, mAssignedDisplay,
                FENCE_TYPE_DST_RELEASE, FENCE_IP_G2D);
}

void ExynosMPP::updateAttr()
{
    MPP_LOGD(eDebugAttrSetting, "updateAttr::mPhysicalType(%d), mAttr(0x%" PRIx64 ")",
            mPhysicalType, mAttr);

    if (mResourceManager == NULL) return;

    auto iter = mResourceManager->mMPPAttrs.find(mPhysicalType);
    if (iter != mResourceManager->mMPPAttrs.end()) {
        mAttr = iter->second;
        MPP_LOGD(eDebugAttrSetting, "After mAttr(0x%" PRIx64 ")", mAttr);
    }
}

void ExynosMPP::updatePreassignedDisplay(uint32_t fromDisplayBit, uint32_t toDisplayBit)
{
    /*
     * If the pre-assigned resources are required to changed,
     * this function will modify PreAssign table.
     */
    for (uint32_t i = 0; i < DISPLAY_MODE_NUM; i++) {
        if (mPreAssignDisplayList[i] == fromDisplayBit)
            mPreAssignDisplayList[i] = toDisplayBit;
    }

    if (mPreAssignDisplayInfo == fromDisplayBit)
        mPreAssignDisplayInfo = toDisplayBit;
}
