/*
 * Copyright (C) 2020 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.
 */

#include "BrightnessController.h"
#include "ExynosDisplayDrmInterfaceModule.h"
#include "ExynosPrimaryDisplayModule.h"
#include <drm/samsung_drm.h>

using BrightnessRange = BrightnessController::BrightnessRange;

using namespace gs101;

/////////////////////////////////////////////////// ExynosDisplayDrmInterfaceModule //////////////////////////////////////////////////////////////////
ExynosDisplayDrmInterfaceModule::ExynosDisplayDrmInterfaceModule(ExynosDisplay *exynosDisplay)
: ExynosDisplayDrmInterface(exynosDisplay)
{
}

ExynosDisplayDrmInterfaceModule::~ExynosDisplayDrmInterfaceModule()
{
}

void ExynosDisplayDrmInterfaceModule::parseBpcEnums(const DrmProperty& property)
{
    const std::vector<std::pair<uint32_t, const char *>> bpcEnums = {
        {static_cast<uint32_t>(BPC_UNSPECIFIED), "Unspecified"},
        {static_cast<uint32_t>(BPC_8), "8bpc"},
        {static_cast<uint32_t>(BPC_10), "10bpc"},
    };

    ALOGD("Init bpc enums");
    DrmEnumParser::parseEnums(property, bpcEnums, mBpcEnums);
    for (auto &e : mBpcEnums) {
        ALOGD("bpc [bpc: %d, drm: %" PRId64 "]", e.first, e.second);
    }
}

int32_t ExynosDisplayDrmInterfaceModule::initDrmDevice(DrmDevice *drmDevice)
{
    int ret = NO_ERROR;
    if ((ret = ExynosDisplayDrmInterface::initDrmDevice(drmDevice)) != NO_ERROR)
        return ret;

    mOldDqeBlobs.init(drmDevice);

    initOldDppBlobs(drmDevice);
    if (mDrmCrtc->force_bpc_property().id())
        parseBpcEnums(mDrmCrtc->force_bpc_property());

    mOldHistoBlobs.init(drmDevice);

    return ret;
}

void ExynosDisplayDrmInterfaceModule::destroyOldBlobs(
        std::vector<uint32_t> &oldBlobs)
{
    for (auto &blob : oldBlobs) {
        mDrmDevice->DestroyPropertyBlob(blob);
    }
    oldBlobs.clear();
}

template<typename StageDataType>
int32_t ExynosDisplayDrmInterfaceModule::setDisplayColorBlob(
        const DrmProperty &prop,
        const uint32_t type,
        const StageDataType &stage,
        const typename GsInterfaceType::IDqe &dqe,
        ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq)
{
    /* dirty bit is valid only if enable is true */
    if (!prop.id())
        return NO_ERROR;
    if (!mForceDisplayColorSetting && stage.enable && !stage.dirty)
        return NO_ERROR;

    int32_t ret = 0;
    uint32_t blobId = 0;
    uint64_t lutSize;

    if (stage.enable) {
        switch (type) {
            case DqeBlobs::CGC:
                ret = gs::ColorDrmBlobFactory::cgc(dqe.Cgc().config, mDrmDevice, blobId);
                break;
            case DqeBlobs::DEGAMMA_LUT:
                std::tie(ret, lutSize) = mDrmCrtc->degamma_lut_size_property().value();
                if (ret < 0) {
                    HWC_LOGE(mExynosDisplay, "%s: there is no degamma_lut_size (ret = %d)",
                             __func__, ret);
                } else {
                    ret = gs::ColorDrmBlobFactory::degamma(lutSize, dqe.DegammaLut().config,
                                                           mDrmDevice, blobId);
                }
                break;
            case DqeBlobs::REGAMMA_LUT:
                std::tie(ret, lutSize) = mDrmCrtc->gamma_lut_size_property().value();
                if (ret < 0) {
                    HWC_LOGE(mExynosDisplay, "%s: there is no gamma_lut_size (ret = %d)", __func__,
                             ret);
                } else {
                    ret = gs::ColorDrmBlobFactory::regamma(lutSize, dqe.RegammaLut().config,
                                                           mDrmDevice, blobId);
                }
                break;
            case DqeBlobs::GAMMA_MAT:
                ret = gs::ColorDrmBlobFactory::gammaMatrix(dqe.GammaMatrix().config, mDrmDevice,
                                                        blobId);
                break;
            case DqeBlobs::LINEAR_MAT:
                ret = gs::ColorDrmBlobFactory::linearMatrix(dqe.LinearMatrix().config, mDrmDevice,
                                                         blobId);
                break;
            case DqeBlobs::DISP_DITHER:
                ret = gs::ColorDrmBlobFactory::displayDither(dqe.DqeControl().config, mDrmDevice,
                                                          blobId);
                break;
            case DqeBlobs::CGC_DITHER:
                ret = gs::ColorDrmBlobFactory::cgcDither(dqe.DqeControl().config, mDrmDevice, blobId);
                break;
            default:
                ret = -EINVAL;
        }
        if (ret != NO_ERROR) {
            HWC_LOGE(mExynosDisplay, "%s: create blob fail", __func__);
            return ret;
        }
    }

    /* Skip setting when previous and current setting is same with 0 */
    if ((blobId == 0) && (mOldDqeBlobs.getBlob(type) == 0))
        return ret;

    if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), prop, blobId)) < 0) {
        HWC_LOGE(mExynosDisplay, "%s: Fail to set property",
                __func__);
        return ret;
    }
    mOldDqeBlobs.addBlob(type, blobId);

    // disp_dither and cgc dither are part of DqeCtrl stage and the notification
    // will be sent after all data in DqeCtrl stage are applied.
    if (type != DqeBlobs::DISP_DITHER && type != DqeBlobs::CGC_DITHER)
        stage.NotifyDataApplied();

    return ret;
}

int32_t ExynosDisplayDrmInterfaceModule::setDisplayColorSetting(
        ExynosDisplayDrmInterface::DrmModeAtomicReq& drmReq) {
    if (!mForceDisplayColorSetting && !mColorSettingChanged)
        return NO_ERROR;

    ExynosDeviceModule* device = static_cast<ExynosDeviceModule*>(mExynosDisplay->mDevice);
    gs101::ColorManager* colorManager = device->getDisplayColorManager(mExynosDisplay);

    int ret = NO_ERROR;
    const typename GsInterfaceType::IDqe& dqe = colorManager->getDqe();

    if ((mDrmCrtc->cgc_lut_property().id() != 0) &&
        (ret = setDisplayColorBlob(mDrmCrtc->cgc_lut_property(),
                static_cast<uint32_t>(DqeBlobs::CGC),
                dqe.Cgc(), dqe, drmReq) != NO_ERROR)) {
        HWC_LOGE(mExynosDisplay, "%s: set Cgc blob fail", __func__);
        return ret;
    }
    if ((ret = setDisplayColorBlob(mDrmCrtc->degamma_lut_property(),
                static_cast<uint32_t>(DqeBlobs::DEGAMMA_LUT),
                dqe.DegammaLut(), dqe, drmReq) != NO_ERROR)) {
        HWC_LOGE(mExynosDisplay, "%s: set DegammaLut blob fail", __func__);
        return ret;
    }
    if ((ret = setDisplayColorBlob(mDrmCrtc->gamma_lut_property(),
                static_cast<uint32_t>(DqeBlobs::REGAMMA_LUT),
                dqe.RegammaLut(), dqe, drmReq) != NO_ERROR)) {
        HWC_LOGE(mExynosDisplay, "%s: set RegammaLut blob fail", __func__);
        return ret;
    }
    if ((ret = setDisplayColorBlob(mDrmCrtc->gamma_matrix_property(),
                static_cast<uint32_t>(DqeBlobs::GAMMA_MAT),
                dqe.GammaMatrix(), dqe, drmReq) != NO_ERROR)) {
        HWC_LOGE(mExynosDisplay, "%s: set GammaMatrix blob fail", __func__);
        return ret;
    }
    if ((ret = setDisplayColorBlob(mDrmCrtc->linear_matrix_property(),
                static_cast<uint32_t>(DqeBlobs::LINEAR_MAT),
                dqe.LinearMatrix(), dqe, drmReq) != NO_ERROR)) {
        HWC_LOGE(mExynosDisplay, "%s: set LinearMatrix blob fail", __func__);
        return ret;
    }
    if ((ret = setDisplayColorBlob(mDrmCrtc->disp_dither_property(),
                static_cast<uint32_t>(DqeBlobs::DISP_DITHER),
                dqe.DqeControl(), dqe, drmReq) != NO_ERROR)) {
        HWC_LOGE(mExynosDisplay, "%s: set DispDither blob fail", __func__);
        return ret;
    }
    if ((ret = setDisplayColorBlob(mDrmCrtc->cgc_dither_property(),
                static_cast<uint32_t>(DqeBlobs::CGC_DITHER),
                dqe.DqeControl(), dqe, drmReq) != NO_ERROR)) {
        HWC_LOGE(mExynosDisplay, "%s: set CgcDither blob fail", __func__);
        return ret;
    }

    const DrmProperty &prop_force_bpc = mDrmCrtc->force_bpc_property();
    if (prop_force_bpc.id()) {
        uint32_t bpc = static_cast<uint32_t>(BPC_UNSPECIFIED);
        if (dqe.DqeControl().enable) {
            if (dqe.DqeControl().config->force_10bpc)
                bpc = static_cast<uint32_t>(BPC_10);
        }
        auto [bpcEnum, ret] = DrmEnumParser::halToDrmEnum(bpc, mBpcEnums);
        if (ret < 0) {
            HWC_LOGE(mExynosDisplay, "Fail to convert bpc(%d)", bpc);
        } else {
            if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), prop_force_bpc,
                            bpcEnum, true)) < 0) {
                HWC_LOGE(mExynosDisplay, "%s: Fail to set force bpc property",
                        __func__);
            }
        }
    }
    dqe.DqeControl().NotifyDataApplied();

    return NO_ERROR;
}

template<typename StageDataType>
int32_t ExynosDisplayDrmInterfaceModule::setPlaneColorBlob(
        const std::unique_ptr<DrmPlane> &plane,
        const DrmProperty &prop,
        const uint32_t type,
        const StageDataType &stage,
        const typename GsInterfaceType::IDpp &dpp,
        const uint32_t dppIndex,
        ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq,
        bool forceUpdate)
{
    /* dirty bit is valid only if enable is true */
    if (!prop.id() || (stage.enable && !stage.dirty && !forceUpdate))
        return NO_ERROR;

    uint32_t ix = 0;
    for (;ix < mOldDppBlobs.size(); ix++) {
        if (mOldDppBlobs[ix].planeId == plane->id()) {
            break;
        }
    }
    if (ix >= mOldDppBlobs.size()) {
        HWC_LOGE(mExynosDisplay, "%s: could not find plane %d", __func__, plane->id());
        return -EINVAL;
    }
    DppBlobs &oldDppBlobs = mOldDppBlobs[ix];

    int32_t ret = 0;
    uint32_t blobId = 0;

    if (stage.enable) {
        switch (type) {
            case DppBlobs::EOTF:
                ret = gs::ColorDrmBlobFactory::eotf(dpp.EotfLut().config, mDrmDevice, blobId);
                break;
            case DppBlobs::GM:
                ret = gs::ColorDrmBlobFactory::gm(dpp.Gm().config, mDrmDevice, blobId);
                break;
            case DppBlobs::DTM:
                ret = gs::ColorDrmBlobFactory::dtm(dpp.Dtm().config, mDrmDevice, blobId);
                break;
            case DppBlobs::OETF:
                ret = gs::ColorDrmBlobFactory::oetf(dpp.OetfLut().config, mDrmDevice, blobId);
                break;
            default:
                ret = -EINVAL;
        }
        if (ret != NO_ERROR) {
            HWC_LOGE(mExynosDisplay, "%s: create blob fail", __func__);
            return ret;
        }
    }

    /* Skip setting when previous and current setting is same with 0 */
    if ((blobId == 0) && (oldDppBlobs.getBlob(type) == 0) && !forceUpdate)
        return ret;

    if ((ret = drmReq.atomicAddProperty(plane->id(), prop, blobId)) < 0) {
        HWC_LOGE(mExynosDisplay, "%s: Fail to set property",
                __func__);
        return ret;
    }

    oldDppBlobs.addBlob(type, blobId);
    stage.NotifyDataApplied();

    return ret;
}

int32_t ExynosDisplayDrmInterfaceModule::setPlaneColorSetting(
        ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq,
        const std::unique_ptr<DrmPlane> &plane,
        const exynos_win_config_data &config, uint32_t &solidColor)
{
    if (mColorSettingChanged == false) return NO_ERROR;

    if ((config.assignedMPP == nullptr) ||
        (config.assignedMPP->mAssignedSources.size() == 0)) {
        HWC_LOGE(mExynosDisplay, "%s:: config's mpp source size is invalid",
                __func__);
        return -EINVAL;
    }
    ExynosMPPSource* mppSource = config.assignedMPP->mAssignedSources[0];
    if (mppSource->mSourceType >= MPP_SOURCE_MAX) {
        HWC_LOGE(mExynosDisplay,
                "%s: invalid mpp source type (%d)", __func__, mppSource->mSourceType);
        return -EINVAL;
    }

    ExynosDeviceModule* device = static_cast<ExynosDeviceModule*>(mExynosDisplay->mDevice);
    ColorManager* colorManager = device->getDisplayColorManager(mExynosDisplay);
    if (!colorManager) {
        HWC_LOGE(mExynosDisplay, "%s: no colorManager for this display", __func__);
        return -EINVAL;
    }

    /*
     * Color conversion of Client and Exynos composition buffer
     * is already addressed by GLES or G2D. But as of now, 'dim SDR' is only
     * supported by HWC/displaycolor, we need put client composition under
     * control of HWC/displaycolor.
     */
    if (!colorManager->hasDppForLayer(mppSource)) {
        if (mppSource->mSourceType == MPP_SOURCE_LAYER) {
            HWC_LOGE(mExynosDisplay,
                "%s: layer need color conversion but there is no IDpp",
                __func__);
            return -EINVAL;
        } else if (mppSource->mSourceType == MPP_SOURCE_COMPOSITION_TARGET) {
            return NO_ERROR;
        } else {
            HWC_LOGE(mExynosDisplay,
                "%s: invalid mpp source type (%d)", __func__, mppSource->mSourceType);
            return -EINVAL;
        }
    }

    if (mppSource->mSourceType == MPP_SOURCE_LAYER) {
        ExynosLayer* layer = (ExynosLayer*)mppSource;

        /* color conversion was already handled by m2mMPP */
        if ((layer->mM2mMPP != nullptr) &&
            (layer->mSrcImg.dataSpace != layer->mMidImg.dataSpace)) {
            return NO_ERROR;
        }
    }

    const typename GsInterfaceType::IDpp& dpp = colorManager->getDppForLayer(mppSource);
    const uint32_t dppIndex = static_cast<uint32_t>(colorManager->getDppIndexForLayer(mppSource));
    bool planeChanged = colorManager->checkAndSaveLayerPlaneId(mppSource, plane->id());

    auto &color = dpp.SolidColor();
    // exynos_win_config_data.color ARGB
    solidColor = (color.a << 24) | (color.r << 16) | (color.g << 8) | color.b;

    int ret = 0;
    if ((ret = setPlaneColorBlob(plane, plane->eotf_lut_property(),
                static_cast<uint32_t>(DppBlobs::EOTF),
                dpp.EotfLut(), dpp, dppIndex, drmReq, planeChanged) != NO_ERROR)) {
        HWC_LOGE(mExynosDisplay, "%s: dpp[%d] set oetf blob fail",
                __func__, dppIndex);
        return ret;
    }
    if ((ret = setPlaneColorBlob(plane, plane->gammut_matrix_property(),
                static_cast<uint32_t>(DppBlobs::GM),
                dpp.Gm(), dpp, dppIndex, drmReq, planeChanged) != NO_ERROR)) {
        HWC_LOGE(mExynosDisplay, "%s: dpp[%d] set GM blob fail",
                __func__, dppIndex);
        return ret;
    }
    if ((ret = setPlaneColorBlob(plane, plane->tone_mapping_property(),
                static_cast<uint32_t>(DppBlobs::DTM),
                dpp.Dtm(), dpp, dppIndex, drmReq, planeChanged) != NO_ERROR)) {
        HWC_LOGE(mExynosDisplay, "%s: dpp[%d] set DTM blob fail",
                __func__, dppIndex);
        return ret;
    }
    if ((ret = setPlaneColorBlob(plane, plane->oetf_lut_property(),
                static_cast<uint32_t>(DppBlobs::OETF),
                dpp.OetfLut(), dpp, dppIndex, drmReq, planeChanged) != NO_ERROR)) {
        HWC_LOGE(mExynosDisplay, "%s: dpp[%d] set OETF blob fail",
                __func__, dppIndex);
        return ret;
    }

    return 0;
}

ExynosDisplayDrmInterfaceModule::SaveBlob::~SaveBlob() {
    clearBlobs();
}

void ExynosDisplayDrmInterfaceModule::SaveBlob::clearBlobs() {
    for (auto &it: blobs) {
        mDrmDevice->DestroyPropertyBlob(it);
    }
    blobs.clear();
}

void ExynosDisplayDrmInterfaceModule::SaveBlob::addBlob(
        uint32_t type, uint32_t blob)
{
    if (type >= blobs.size()) {
        ALOGE("Invalid dqe blop type: %d", type);
        return;
    }
    if (blobs[type] > 0)
        mDrmDevice->DestroyPropertyBlob(blobs[type]);

    blobs[type] = blob;
}

uint32_t ExynosDisplayDrmInterfaceModule::SaveBlob::getBlob(uint32_t type)
{
    if (type >= blobs.size()) {
        ALOGE("Invalid dqe blop type: %d", type);
        return 0;
    }
    return blobs[type];
}

void ExynosDisplayDrmInterfaceModule::getDisplayInfo(
        std::vector<displaycolor::DisplayInfo> &display_info) {
    displaycolor::DisplayInfo disp_info;

    if (mExynosDisplay->mType == HWC_DISPLAY_PRIMARY) {
        disp_info.brightness_ranges = mExynosDisplay->mBrightnessController->getBrightnessRanges();
        disp_info.panel_name = GetPanelName();
        disp_info.panel_serial = GetPanelSerial();
        if (mExynosDisplay->mIndex == 0)
            disp_info.display_type = DisplayType::DISPLAY_PRIMARY;
        else
            disp_info.display_type = DisplayType::DISPLAY_SECONDARY;
    } else if (mExynosDisplay->mType == HWC_DISPLAY_EXTERNAL) {
        disp_info.display_type = DisplayType::DISPLAY_EXTERNAL;
        disp_info.panel_name = "external_display";
        disp_info.panel_serial = "0001";
    } else {
        ALOGE("Unsupported display type (%d) in getDisplayInfo!", mExynosDisplay->mType);
        return;
    }

    display_info.push_back(disp_info);
}

const std::string ExynosDisplayDrmInterfaceModule::GetPanelInfo(const std::string &sysfs_rel,
                                                                char delim) {
    ExynosPrimaryDisplayModule* display = (ExynosPrimaryDisplayModule*)mExynosDisplay;
    const std::string& sysfs = display->getPanelSysfsPath();

    if (sysfs.empty()) {
        return "";
    }

    std::string info;
    if (readLineFromFile(sysfs + "/" + sysfs_rel, info, delim) != OK) {
        ALOGE("failed reading %s/%s", sysfs.c_str(), sysfs_rel.c_str());
        return "";
    }

    return info;
}

/* For Histogram */
int32_t ExynosDisplayDrmInterfaceModule::createHistoRoiBlob(uint32_t &blobId) {
    struct histogram_roi histo_roi;

    std::unique_lock<std::mutex> lk((mHistogramInfo->mSetHistInfoMutex));
    histo_roi.start_x = mHistogramInfo->getHistogramROI().start_x;
    histo_roi.start_y = mHistogramInfo->getHistogramROI().start_y;
    histo_roi.hsize = mHistogramInfo->getHistogramROI().hsize;
    histo_roi.vsize = mHistogramInfo->getHistogramROI().vsize;

    int ret = mDrmDevice->CreatePropertyBlob(&histo_roi, sizeof(histo_roi), &blobId);
    if (ret) {
        HWC_LOGE(mExynosDisplay, "Failed to create histogram roi blob %d", ret);
        return ret;
    }

    return NO_ERROR;
}

int32_t ExynosDisplayDrmInterfaceModule::createHistoWeightsBlob(uint32_t &blobId) {
    struct histogram_weights histo_weights;

    std::unique_lock<std::mutex> lk((mHistogramInfo->mSetHistInfoMutex));
    histo_weights.weight_r = mHistogramInfo->getHistogramWeights().weight_r;
    histo_weights.weight_g = mHistogramInfo->getHistogramWeights().weight_g;
    histo_weights.weight_b = mHistogramInfo->getHistogramWeights().weight_b;

    int ret = mDrmDevice->CreatePropertyBlob(&histo_weights, sizeof(histo_weights), &blobId);
    if (ret) {
        HWC_LOGE(mExynosDisplay, "Failed to create histogram weights blob %d", ret);
        return ret;
    }

    return NO_ERROR;
}

int32_t ExynosDisplayDrmInterfaceModule::setDisplayHistoBlob(
        const DrmProperty &prop, const uint32_t type,
        ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq) {
    if (!prop.id()) return NO_ERROR;

    int32_t ret = NO_ERROR;
    uint32_t blobId = 0;

    switch (type) {
        case HistoBlobs::ROI:
            ret = createHistoRoiBlob(blobId);
            break;
        case HistoBlobs::WEIGHTS:
            ret = createHistoWeightsBlob(blobId);
            break;
        default:
            ret = -EINVAL;
    }
    if (ret != NO_ERROR) {
        HWC_LOGE(mExynosDisplay, "%s: Failed to create blob", __func__);
        return ret;
    }

    /* Skip setting when previous and current setting is same with 0 */
    if ((blobId == 0) && (mOldHistoBlobs.getBlob(type) == 0)) return ret;

    if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), prop, blobId)) < 0) {
        HWC_LOGE(mExynosDisplay, "%s: Failed to add property", __func__);
        return ret;
    }
    mOldHistoBlobs.addBlob(type, blobId);

    return ret;
}

int32_t ExynosDisplayDrmInterfaceModule::setDisplayHistogramSetting(
        ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq) {
    if ((isHistogramInfoRegistered() == false) || (isPrimary() == false)) return NO_ERROR;

    int ret = NO_ERROR;

    if ((ret = setDisplayHistoBlob(mDrmCrtc->histogram_roi_property(),
                                   static_cast<uint32_t>(HistoBlobs::ROI), drmReq) != NO_ERROR)) {
        HWC_LOGE(mExynosDisplay, "%s: Failed to set Histo_ROI blob", __func__);
        return ret;
    }
    if ((ret = setDisplayHistoBlob(mDrmCrtc->histogram_weights_property(),
                                   static_cast<uint32_t>(HistoBlobs::WEIGHTS),
                                   drmReq) != NO_ERROR)) {
        HWC_LOGE(mExynosDisplay, "%s: Failed to set Histo_Weights blob", __func__);
        return ret;
    }

    const DrmProperty &prop_histo_threshold = mDrmCrtc->histogram_threshold_property();
    if (prop_histo_threshold.id()) {
        if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), prop_histo_threshold,
                                            (uint64_t)(mHistogramInfo->getHistogramThreshold()),
                                            true)) < 0) {
            HWC_LOGE(mExynosDisplay, "%s: Failed to set histogram thereshold property", __func__);
            return ret;
        }
    }

    return NO_ERROR;
}

int32_t ExynosDisplayDrmInterfaceModule::setHistogramControl(hidl_histogram_control_t control) {
    if ((isHistogramInfoRegistered() == false) || (isPrimary() == false)) return NO_ERROR;

    int ret = NO_ERROR;
    uint32_t crtc_id = mDrmCrtc->id();

    if (control == hidl_histogram_control_t::HISTOGRAM_CONTROL_REQUEST) {
        ret = mDrmDevice->CallVendorIoctl(DRM_IOCTL_EXYNOS_HISTOGRAM_REQUEST, (void *)&crtc_id);
    } else if (control == hidl_histogram_control_t::HISTOGRAM_CONTROL_CANCEL) {
        ret = mDrmDevice->CallVendorIoctl(DRM_IOCTL_EXYNOS_HISTOGRAM_CANCEL, (void *)&crtc_id);
    }

    return ret;
}

int32_t ExynosDisplayDrmInterfaceModule::setHistogramData(void *bin) {
    if (!bin) return -EINVAL;

    /*
     * There are two handling methods.
     * For ContentSampling in HWC_2.3 API, histogram bin needs to be accumulated.
     * For Histogram IDL, histogram bin need to be sent to IDL block.
     */
    if (mHistogramInfo->getHistogramType() == HistogramInfo::HistogramType::HISTOGRAM_HIDL) {
        (mHistogramInfo.get())->callbackHistogram((char16_t *)bin);
    } else {
        /*
         * ContentSampling in HWC2.3 API is not supported
         */
        return -ENOTSUP;
    }

    return NO_ERROR;
}

//////////////////////////////////////////////////// ExynosPrimaryDisplayDrmInterfaceModule //////////////////////////////////////////////////////////////////
ExynosPrimaryDisplayDrmInterfaceModule::ExynosPrimaryDisplayDrmInterfaceModule(ExynosDisplay *exynosDisplay)
: ExynosDisplayDrmInterfaceModule(exynosDisplay)
{
}

ExynosPrimaryDisplayDrmInterfaceModule::~ExynosPrimaryDisplayDrmInterfaceModule()
{
}

//////////////////////////////////////////////////// ExynosExternalDisplayDrmInterfaceModule //////////////////////////////////////////////////////////////////
ExynosExternalDisplayDrmInterfaceModule::ExynosExternalDisplayDrmInterfaceModule(ExynosDisplay *exynosDisplay)
: ExynosDisplayDrmInterfaceModule(exynosDisplay)
{
}

ExynosExternalDisplayDrmInterfaceModule::~ExynosExternalDisplayDrmInterfaceModule()
{
}
