/* * Copyright (C) 2022 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. */ #define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL) #include "ExynosPrimaryDisplayModule.h" #include #include "ExynosHWCHelper.h" #define OP_MANAGER_LOGD(msg, ...) \ ALOGD("[%s] OperationRateManager::%s:" msg, mDisplay->mDisplayName.c_str(), __func__, \ ##__VA_ARGS__) #define OP_MANAGER_LOGI(msg, ...) \ ALOGI("[%s] OperationRateManager::%s:" msg, mDisplay->mDisplayName.c_str(), __func__, \ ##__VA_ARGS__) #define OP_MANAGER_LOGE(msg, ...) \ ALOGE("[%s] OperationRateManager::%s:" msg, mDisplay->mDisplayName.c_str(), __func__, \ ##__VA_ARGS__) using namespace zuma; ExynosPrimaryDisplayModule::ExynosPrimaryDisplayModule(uint32_t index, ExynosDevice* device, const std::string& displayName) : gs201::ExynosPrimaryDisplayModule(index, device, displayName) { int32_t hs_hz = property_get_int32("vendor.primarydisplay.op.hs_hz", 0); int32_t ns_hz = property_get_int32("vendor.primarydisplay.op.ns_hz", 0); if (hs_hz && ns_hz) { mOperationRateManager = std::make_unique(this, hs_hz, ns_hz); } } ExynosPrimaryDisplayModule::~ExynosPrimaryDisplayModule () { } int32_t ExynosPrimaryDisplayModule::validateWinConfigData() { return ExynosDisplay::validateWinConfigData(); } int32_t ExynosPrimaryDisplayModule::OperationRateManager::getTargetOperationRate() const { if (mDisplayPowerMode == HWC2_POWER_MODE_DOZE || mDisplayPowerMode == HWC2_POWER_MODE_DOZE_SUSPEND) { return LP_OP_RATE; } else { return mDisplayTargetOperationRate; } } int32_t ExynosPrimaryDisplayModule::OperationRateManager::setTargetOperationRate( const int32_t rate) { if (mDisplayTargetOperationRate == rate) return NO_ERROR; OP_MANAGER_LOGI("set target operation rate %d", rate); mDisplayTargetOperationRate = rate; return NO_ERROR; } ExynosPrimaryDisplayModule::OperationRateManager::OperationRateManager( ExynosPrimaryDisplay* display, int32_t hsHz, int32_t nsHz) : gs201::ExynosPrimaryDisplayModule::OperationRateManager(), mDisplay(display), mDisplayHsOperationRate(hsHz), mDisplayNsOperationRate(nsHz), mDisplayPeakRefreshRate(0), mDisplayRefreshRate(0), mDisplayLastDbv(0), mDisplayDbv(0), mDisplayPowerMode(HWC2_POWER_MODE_ON), mDisplayLowBatteryModeEnabled(false) { mDisplayNsMinDbv = property_get_int32("vendor.primarydisplay.op.ns_min_dbv", 0); mDisplayTargetOperationRate = mDisplayHsOperationRate; OP_MANAGER_LOGI("Op Rate: NS=%d HS=%d NsMinDbv=%d", mDisplayNsOperationRate, mDisplayHsOperationRate, mDisplayNsMinDbv); } ExynosPrimaryDisplayModule::OperationRateManager::~OperationRateManager() {} int32_t ExynosPrimaryDisplayModule::OperationRateManager::onPeakRefreshRate(uint32_t rate) { Mutex::Autolock lock(mLock); char rateStr[PROP_VALUE_MAX]; std::sprintf(rateStr, "%d", rate); OP_MANAGER_LOGD("rate=%d", rate); mDisplayPeakRefreshRate = rate; if (property_set("persist.vendor.primarydisplay.op.peak_refresh_rate", rateStr) < 0) { OP_MANAGER_LOGE("failed to set property persist.primarydisplay.op.peak_refresh_rate"); } return 0; } int32_t ExynosPrimaryDisplayModule::OperationRateManager::onLowPowerMode(bool enabled) { Mutex::Autolock lock(mLock); OP_MANAGER_LOGD("enabled=%d", enabled); mDisplayLowBatteryModeEnabled = enabled; return 0; } int32_t ExynosPrimaryDisplayModule::OperationRateManager::onConfig(hwc2_config_t cfg) { Mutex::Autolock lock(mLock); mDisplayRefreshRate = mDisplay->getRefreshRate(cfg); OP_MANAGER_LOGD("rate=%d", mDisplayRefreshRate); updateOperationRateLocked(DispOpCondition::SET_CONFIG); return 0; } int32_t ExynosPrimaryDisplayModule::OperationRateManager::onBrightness(uint32_t dbv) { Mutex::Autolock lock(mLock); if (dbv == 0 || mDisplayLastDbv == dbv) return 0; OP_MANAGER_LOGD("dbv=%d", dbv); mDisplayDbv = dbv; /* Update peak_refresh_rate from persist/vendor prop after a brightness change. 1. Otherwise there will be NS-HS-NS switch during the onPowerMode. 2. When constructor is called, persist property is not ready yet and returns 0. */ if (!mDisplayPeakRefreshRate) { char rateStr[PROP_VALUE_MAX]; int32_t vendorPeakRefreshRate = 0, persistPeakRefreshRate = 0; if (property_get("persist.vendor.primarydisplay.op.peak_refresh_rate", rateStr, "0") >= 0 && atoi(rateStr) > 0) { persistPeakRefreshRate = atoi(rateStr); mDisplayPeakRefreshRate = persistPeakRefreshRate; } else { vendorPeakRefreshRate = property_get_int32("vendor.primarydisplay.op.peak_refresh_rate", 0); mDisplayPeakRefreshRate = vendorPeakRefreshRate; } OP_MANAGER_LOGD("peak_refresh_rate=%d[vendor: %d|persist %d]", mDisplayPeakRefreshRate, vendorPeakRefreshRate, persistPeakRefreshRate); } return updateOperationRateLocked(DispOpCondition::SET_DBV); } int32_t ExynosPrimaryDisplayModule::OperationRateManager::onPowerMode(int32_t mode) { std::string modeName = "Unknown"; if (mode == HWC2_POWER_MODE_ON) { modeName = "On"; } else if (mode == HWC2_POWER_MODE_OFF) { modeName = "Off"; } else if (mode == HWC2_POWER_MODE_DOZE || mode == HWC2_POWER_MODE_DOZE_SUSPEND) { modeName = "LP"; } Mutex::Autolock lock(mLock); OP_MANAGER_LOGD("mode=%s", modeName.c_str()); mDisplayPowerMode = static_cast(mode); return updateOperationRateLocked(DispOpCondition::PANEL_SET_POWER); } int32_t ExynosPrimaryDisplayModule::OperationRateManager::updateOperationRateLocked( const DispOpCondition cond) { int32_t ret = HWC2_ERROR_NONE, dbv; ATRACE_CALL(); if (cond == DispOpCondition::SET_DBV) { dbv = mDisplayDbv; } else { dbv = mDisplayLastDbv; } int32_t desiredOpRate = mDisplayHsOperationRate; int32_t curRefreshRate = mDisplay->getRefreshRate(mDisplay->mActiveConfig); bool isSteadyLowRefreshRate = (mDisplayPeakRefreshRate && mDisplayPeakRefreshRate <= mDisplayNsOperationRate) || mDisplayLowBatteryModeEnabled; int32_t effectiveOpRate = 0; // check minimal operation rate needed if (isSteadyLowRefreshRate && curRefreshRate <= mDisplayNsOperationRate) { desiredOpRate = mDisplayNsOperationRate; } // check blocking zone if (dbv < mDisplayNsMinDbv) { desiredOpRate = mDisplayHsOperationRate; } if (mDisplayPowerMode == HWC2_POWER_MODE_DOZE || mDisplayPowerMode == HWC2_POWER_MODE_DOZE_SUSPEND) { mDisplayTargetOperationRate = LP_OP_RATE; desiredOpRate = mDisplayTargetOperationRate; effectiveOpRate = desiredOpRate; } else if (mDisplayPowerMode != HWC2_POWER_MODE_ON) { return ret; } if (cond == DispOpCondition::SET_CONFIG) { curRefreshRate = mDisplayRefreshRate; if ((curRefreshRate > mDisplayNsOperationRate) && (curRefreshRate <= mDisplayHsOperationRate)) effectiveOpRate = mDisplayHsOperationRate; } else if (cond == DispOpCondition::PANEL_SET_POWER) { if (mDisplayPowerMode == HWC2_POWER_MODE_ON) { mDisplayTargetOperationRate = getTargetOperationRate(); } effectiveOpRate = desiredOpRate; } else if (cond == DispOpCondition::SET_DBV) { // TODO: tune brightness delta for different brightness curve and values int32_t delta = abs(dbv - mDisplayLastDbv); if ((desiredOpRate == mDisplayHsOperationRate) || (delta > BRIGHTNESS_DELTA_THRESHOLD)) { effectiveOpRate = desiredOpRate; } mDisplayLastDbv = dbv; if (effectiveOpRate > LP_OP_RATE && (effectiveOpRate != mDisplayTargetOperationRate)) { OP_MANAGER_LOGD("brightness delta=%d", delta); } else { return ret; } } if (!mDisplay->isConfigSettingEnabled() && effectiveOpRate == mDisplayNsOperationRate) { OP_MANAGER_LOGI("rate switching is disabled, skip NS op rate update"); return ret; } else if (effectiveOpRate > LP_OP_RATE) { ret = setTargetOperationRate(effectiveOpRate); } OP_MANAGER_LOGI("Target@%d(desired:%d) | Refresh@%d(peak:%d), Battery:%s, DBV:%d(NsMin:%d)", mDisplayTargetOperationRate, desiredOpRate, curRefreshRate, mDisplayPeakRefreshRate, mDisplayLowBatteryModeEnabled ? "Low" : "OK", mDisplayLastDbv, mDisplayNsMinDbv); return ret; } void ExynosPrimaryDisplayModule::checkPreblendingRequirement() { if (!hasDisplayColor()) { DISPLAY_LOGD(eDebugTDM, "%s is skipped because of no displaycolor", __func__); return; } String8 log; int count = 0; auto checkPreblending = [&](const int idx, ExynosMPPSource* mppSrc) -> int { auto* colorManager = getColorManager(); if (!colorManager) return false; auto& dpp = colorManager->getDppForLayer(mppSrc); mppSrc->mNeedPreblending = dpp.EotfLut().enable | dpp.Gm().enable | dpp.Dtm().enable | dpp.OetfLut().enable; if (hwcCheckDebugMessages(eDebugTDM)) { log.appendFormat(" i=%d,pb(%d-%d,%d,%d,%d)", idx, mppSrc->mNeedPreblending, dpp.EotfLut().enable, dpp.Gm().enable, dpp.Dtm().enable, dpp.OetfLut().enable); } return mppSrc->mNeedPreblending; }; // for client target count += checkPreblending(-1, &mClientCompositionInfo); // for normal layers for (size_t i = 0; i < mLayers.size(); ++i) { count += checkPreblending(i, mLayers[i]); } DISPLAY_LOGD(eDebugTDM, "disp(%d),cnt=%d%s", mDisplayId, count, log.c_str()); }