1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
18
19 #include <android/binder_ibinder.h>
20 #include <android/binder_status.h>
21 #include <cutils/properties.h>
22
23 #include "ExynosHWCHelper.h"
24 #include "ExynosPrimaryDisplayModule.h"
25
26 #define DISP_STR(disp) (disp)->mDisplayName.c_str()
27
28 #define OP_MANAGER_LOGI(disp, msg, ...) \
29 ALOGI("[%s] OperationRateManager::%s:" msg, DISP_STR(disp), __func__, ##__VA_ARGS__)
30 #define OP_MANAGER_LOGW(disp, msg, ...) \
31 ALOGW("[%s] OperationRateManager::%s:" msg, DISP_STR(disp), __func__, ##__VA_ARGS__)
32 #define OP_MANAGER_LOGE(disp, msg, ...) \
33 ALOGE("[%s] OperationRateManager::%s:" msg, DISP_STR(disp), __func__, ##__VA_ARGS__)
34
35 static constexpr int64_t kQueryPeriodNanosecs = std::chrono::nanoseconds(100ms).count();
36
37 using namespace zumapro;
38
ExynosPrimaryDisplayModule(uint32_t index,ExynosDevice * device,const std::string & displayName)39 ExynosPrimaryDisplayModule::ExynosPrimaryDisplayModule(uint32_t index, ExynosDevice* device,
40 const std::string& displayName)
41 : gs201::ExynosPrimaryDisplayModule(index, device, displayName) {
42 int32_t hs_hz = property_get_int32("vendor.primarydisplay.op.hs_hz", 0);
43 int32_t ns_hz = property_get_int32("vendor.primarydisplay.op.ns_hz", 0);
44
45 if (hs_hz && ns_hz) {
46 mOperationRateManager = std::make_unique<OperationRateManager>(this, hs_hz, ns_hz);
47 }
48 }
49
~ExynosPrimaryDisplayModule()50 ExynosPrimaryDisplayModule::~ExynosPrimaryDisplayModule() {}
51
validateWinConfigData()52 int32_t ExynosPrimaryDisplayModule::validateWinConfigData() {
53 return ExynosDisplay::validateWinConfigData();
54 }
55
OperationRateManager(ExynosPrimaryDisplay * display,int32_t hsHz,int32_t nsHz)56 ExynosPrimaryDisplayModule::OperationRateManager::OperationRateManager(
57 ExynosPrimaryDisplay* display, int32_t hsHz, int32_t nsHz)
58 : gs201::ExynosPrimaryDisplayModule::OperationRateManager(),
59 mDisplay(display),
60 mDisplayHsOperationRate(hsHz),
61 mDisplayNsOperationRate(nsHz),
62 mDisplayPeakRefreshRate(0),
63 mDisplayRefreshRate(0),
64 mDisplayLastDbv(0),
65 mDisplayDbv(0),
66 mDisplayHsSwitchMinDbv(0),
67 mDisplayPowerMode(HWC2_POWER_MODE_ON),
68 mDisplayLowBatteryModeEnabled(false),
69 mHistogramQueryWorker(nullptr) {
70 mDisplayNsMinDbv = property_get_int32("vendor.primarydisplay.op.ns_min_dbv", 0);
71 mDisplayTargetOperationRate = mDisplayHsOperationRate;
72 OP_MANAGER_LOGI(mDisplay, "Op Rate: NS=%d HS=%d NsMinDbv=%d", mDisplayNsOperationRate,
73 mDisplayHsOperationRate, mDisplayNsMinDbv);
74
75 float histDeltaTh =
76 static_cast<float>(property_get_int32("vendor.primarydisplay.op.hist_delta_th", 0));
77 if (histDeltaTh) {
78 mHistogramQueryWorker = std::make_unique<HistogramQueryWorker>(this, histDeltaTh);
79 mDisplayHsSwitchMinDbv =
80 property_get_int32("vendor.primarydisplay.op.hs_switch_min_dbv", 0);
81 }
82 }
83
~OperationRateManager()84 ExynosPrimaryDisplayModule::OperationRateManager::~OperationRateManager() {}
85
getTargetOperationRate() const86 int32_t ExynosPrimaryDisplayModule::OperationRateManager::getTargetOperationRate() const {
87 if (mDisplayPowerMode == HWC2_POWER_MODE_DOZE ||
88 mDisplayPowerMode == HWC2_POWER_MODE_DOZE_SUSPEND) {
89 return kLowPowerOperationRate;
90 } else {
91 return mDisplayTargetOperationRate;
92 }
93 }
94
onPeakRefreshRate(uint32_t rate)95 int32_t ExynosPrimaryDisplayModule::OperationRateManager::onPeakRefreshRate(uint32_t rate) {
96 char rateStr[PROP_VALUE_MAX];
97 std::sprintf(rateStr, "%d", rate);
98
99 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate, "OperationRateManager: rate=%d",
100 rate);
101
102 Mutex::Autolock lock(mLock);
103 if (property_set("persist.vendor.primarydisplay.op.peak_refresh_rate", rateStr) < 0) {
104 OP_MANAGER_LOGE(mDisplay,
105 "failed to set property persist.primarydisplay.op.peak_refresh_rate");
106 }
107 mDisplayPeakRefreshRate = rate;
108 return 0;
109 }
110
onLowPowerMode(bool enabled)111 int32_t ExynosPrimaryDisplayModule::OperationRateManager::onLowPowerMode(bool enabled) {
112 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate, "enabled=%d", enabled);
113
114 Mutex::Autolock lock(mLock);
115 mDisplayLowBatteryModeEnabled = enabled;
116 return 0;
117 }
118
onConfig(hwc2_config_t cfg)119 int32_t ExynosPrimaryDisplayModule::OperationRateManager::onConfig(hwc2_config_t cfg) {
120 Mutex::Autolock lock(mLock);
121 int32_t targetRefreshRate = mDisplay->getRefreshRate(cfg);
122 if (mHistogramQueryWorker && mHistogramQueryWorker->isRuntimeResolutionConfig() &&
123 mDisplayRefreshRate == targetRefreshRate) {
124 mHistogramQueryWorker->updateConfig(mDisplay->mXres, mDisplay->mYres);
125 // skip op update for Runtime Resolution config
126 return 0;
127 }
128 mDisplayRefreshRate = targetRefreshRate;
129 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate, "OperationRateManager: rate=%d",
130 mDisplayRefreshRate);
131 updateOperationRateLocked(DispOpCondition::SET_CONFIG);
132 return 0;
133 }
134
onBrightness(uint32_t dbv)135 int32_t ExynosPrimaryDisplayModule::OperationRateManager::onBrightness(uint32_t dbv) {
136 Mutex::Autolock lock(mLock);
137 if (dbv == 0 || mDisplayLastDbv == dbv) return 0;
138 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate, "OperationRateManager: dbv=%d", dbv);
139 mDisplayDbv = dbv;
140
141 /*
142 Update peak_refresh_rate from persist/vendor prop after a brightness change.
143 1. Otherwise there will be NS-HS-NS switch during the onPowerMode.
144 2. When constructor is called, persist property is not ready yet and returns 0.
145 */
146 if (!mDisplayPeakRefreshRate) {
147 char rateStr[PROP_VALUE_MAX];
148 int32_t vendorPeakRefreshRate = 0, persistPeakRefreshRate = 0;
149 if (property_get("persist.vendor.primarydisplay.op.peak_refresh_rate", rateStr, "0") >= 0 &&
150 atoi(rateStr) > 0) {
151 persistPeakRefreshRate = atoi(rateStr);
152 mDisplayPeakRefreshRate = persistPeakRefreshRate;
153 } else {
154 vendorPeakRefreshRate =
155 property_get_int32("vendor.primarydisplay.op.peak_refresh_rate", 0);
156 mDisplayPeakRefreshRate = vendorPeakRefreshRate;
157 }
158
159 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
160 "OperationRateManager: peak_refresh_rate=%d[vendor: %d|persist %d]",
161 mDisplayPeakRefreshRate, vendorPeakRefreshRate, persistPeakRefreshRate);
162 }
163
164 return updateOperationRateLocked(DispOpCondition::SET_DBV);
165 }
166
onPowerMode(int32_t mode)167 int32_t ExynosPrimaryDisplayModule::OperationRateManager::onPowerMode(int32_t mode) {
168 std::string modeName = "Unknown";
169 if (mode == HWC2_POWER_MODE_ON) {
170 modeName = "On";
171 } else if (mode == HWC2_POWER_MODE_OFF) {
172 modeName = "Off";
173 } else if (mode == HWC2_POWER_MODE_DOZE || mode == HWC2_POWER_MODE_DOZE_SUSPEND) {
174 modeName = "LP";
175 }
176
177 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate, "OperationRateManager: mode=%s",
178 modeName.c_str());
179
180 Mutex::Autolock lock(mLock);
181 mDisplayPowerMode = static_cast<hwc2_power_mode_t>(mode);
182 return updateOperationRateLocked(DispOpCondition::PANEL_SET_POWER);
183 }
184
onHistogram()185 int32_t ExynosPrimaryDisplayModule::OperationRateManager::onHistogram() {
186 Mutex::Autolock lock(mLock);
187 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
188 "histogram reach to the luma delta threshold");
189 return updateOperationRateLocked(DispOpCondition::HISTOGRAM_DELTA);
190 }
191
updateOperationRateLocked(const DispOpCondition cond)192 int32_t ExynosPrimaryDisplayModule::OperationRateManager::updateOperationRateLocked(
193 const DispOpCondition cond) {
194 int32_t ret = HWC2_ERROR_NONE, dbv;
195
196 ATRACE_CALL();
197 if (cond == DispOpCondition::SET_DBV) {
198 dbv = mDisplayDbv;
199 } else {
200 dbv = mDisplayLastDbv;
201 }
202
203 int32_t desiredOpRate = mDisplayHsOperationRate;
204 bool isSteadyLowRefreshRate =
205 (mDisplayPeakRefreshRate && mDisplayPeakRefreshRate <= mDisplayNsOperationRate) ||
206 mDisplayLowBatteryModeEnabled;
207 bool isDbvInBlockingZone = mDisplayLowBatteryModeEnabled ? (dbv < mDisplayNsMinDbv)
208 : (dbv < mDisplayHsSwitchMinDbv);
209 int32_t effectiveOpRate = 0;
210
211 // check minimal operation rate needed
212 if (isSteadyLowRefreshRate && mDisplayRefreshRate <= mDisplayNsOperationRate) {
213 desiredOpRate = mDisplayNsOperationRate;
214 }
215 // check blocking zone
216 if (isDbvInBlockingZone) {
217 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
218 "OperationRateManager: in blocking zone (dbv %d, min %d)", dbv,
219 mDisplayNsMinDbv);
220 desiredOpRate = mDisplayHsOperationRate;
221 }
222
223 if (mDisplayPowerMode == HWC2_POWER_MODE_DOZE ||
224 mDisplayPowerMode == HWC2_POWER_MODE_DOZE_SUSPEND) {
225 mDisplayTargetOperationRate = kLowPowerOperationRate;
226 desiredOpRate = mDisplayTargetOperationRate;
227 effectiveOpRate = desiredOpRate;
228 } else if (mDisplayPowerMode != HWC2_POWER_MODE_ON) {
229 if (mHistogramQueryWorker) {
230 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
231 "histogram stopQuery due to power off");
232 mHistogramQueryWorker->stopQuery();
233 }
234 return ret;
235 }
236
237 if (cond == DispOpCondition::SET_CONFIG) {
238 if (mDisplayRefreshRate <= mDisplayHsOperationRate) {
239 if (!mHistogramQueryWorker) {
240 if (mDisplayRefreshRate > mDisplayNsOperationRate) {
241 effectiveOpRate = mDisplayHsOperationRate;
242 }
243 } else {
244 if (mDisplayRefreshRate == mDisplayTargetOperationRate && !isDbvInBlockingZone) {
245 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
246 "histogram stopQuery due to the same config");
247 mHistogramQueryWorker->stopQuery();
248 }
249 if (!isDbvInBlockingZone) {
250 if (mDisplayLowBatteryModeEnabled &&
251 (!mDisplayHsSwitchMinDbv || dbv < mDisplayHsSwitchMinDbv)) {
252 // delay the switch of NS->HS until conditions are satisfied
253 desiredOpRate = mDisplayRefreshRate;
254 } else if (mDisplayRefreshRate > mDisplayNsOperationRate) {
255 // will switch to HS immediately
256 effectiveOpRate = mDisplayHsOperationRate;
257 }
258 }
259 }
260 }
261 } else if (cond == DispOpCondition::PANEL_SET_POWER) {
262 if (mDisplayPowerMode == HWC2_POWER_MODE_ON) {
263 mDisplayTargetOperationRate = getTargetOperationRate();
264 }
265 effectiveOpRate = desiredOpRate;
266 } else if (cond == DispOpCondition::SET_DBV) {
267 // TODO: tune brightness delta for different brightness curve and values
268 int32_t delta = abs(dbv - mDisplayLastDbv);
269 if (!mHistogramQueryWorker) {
270 if (desiredOpRate == mDisplayHsOperationRate || delta > kBrightnessDeltaThreshold) {
271 effectiveOpRate = desiredOpRate;
272 }
273 } else {
274 if (delta > kBrightnessDeltaThreshold) {
275 effectiveOpRate = desiredOpRate;
276 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
277 "histogram stopQuery due to dbv delta");
278 mHistogramQueryWorker->stopQuery();
279 }
280 }
281 mDisplayLastDbv = dbv;
282 if (effectiveOpRate > kLowPowerOperationRate &&
283 (effectiveOpRate != mDisplayTargetOperationRate)) {
284 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
285 "OperationRateManager: brightness delta=%d", delta);
286 } else {
287 if (!mHistogramQueryWorker ||
288 (desiredOpRate == mDisplayNsOperationRate && isDbvInBlockingZone)) {
289 return ret;
290 }
291 }
292 } else if (cond == DispOpCondition::HISTOGRAM_DELTA) {
293 effectiveOpRate = desiredOpRate;
294 }
295
296 if (!mDisplay->isConfigSettingEnabled() && effectiveOpRate == mDisplayNsOperationRate) {
297 OP_MANAGER_LOGI(mDisplay, "rate switching is disabled, skip NS op rate update");
298 return ret;
299 } else if (effectiveOpRate > kLowPowerOperationRate &&
300 effectiveOpRate != mDisplayTargetOperationRate) {
301 mDisplayTargetOperationRate = effectiveOpRate;
302 OP_MANAGER_LOGI(mDisplay, "set target operation rate %d", effectiveOpRate);
303 }
304
305 if (mHistogramQueryWorker && mDisplayTargetOperationRate != desiredOpRate) {
306 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate, "histogram startQuery");
307 mHistogramQueryWorker->startQuery();
308 }
309
310 OP_MANAGER_LOGI(mDisplay,
311 "Target@%d(desired:%d) | Refresh@%d(peak:%d), Battery:%s, DBV:%d(NsMin:%d, "
312 "HsSwitchMin:%d)",
313 mDisplayTargetOperationRate, desiredOpRate, mDisplayRefreshRate,
314 mDisplayPeakRefreshRate, mDisplayLowBatteryModeEnabled ? "Low" : "OK",
315 mDisplayLastDbv, mDisplayNsMinDbv, mDisplayHsSwitchMinDbv);
316 return ret;
317 }
318
checkPreblendingRequirement()319 void ExynosPrimaryDisplayModule::checkPreblendingRequirement() {
320 if (!hasDisplayColor()) {
321 DISPLAY_LOGD(eDebugTDM, "%s is skipped because of no displaycolor", __func__);
322 return;
323 }
324
325 String8 log;
326 int count = 0;
327
328 auto checkPreblending = [&](const int idx, ExynosMPPSource* mppSrc) -> int {
329 auto* colorManager = getColorManager();
330 if (!colorManager) return false;
331 auto& dpp = colorManager->getDppForLayer(mppSrc);
332 mppSrc->mNeedPreblending =
333 dpp.EotfLut().enable | dpp.Gm().enable | dpp.Dtm().enable | dpp.OetfLut().enable;
334 if (hwcCheckDebugMessages(eDebugTDM)) {
335 log.appendFormat(" i=%d,pb(%d-%d,%d,%d,%d)", idx, mppSrc->mNeedPreblending,
336 dpp.EotfLut().enable, dpp.Gm().enable, dpp.Dtm().enable,
337 dpp.OetfLut().enable);
338 }
339 return mppSrc->mNeedPreblending;
340 };
341
342 // for client target
343 count += checkPreblending(-1, &mClientCompositionInfo);
344
345 // for normal layers
346 for (size_t i = 0; i < mLayers.size(); ++i) {
347 count += checkPreblending(i, mLayers[i]);
348 }
349 DISPLAY_LOGD(eDebugTDM, "disp(%d),cnt=%d%s", mDisplayId, count, log.c_str());
350 }
351
HistogramQueryWorker(OperationRateManager * opRateManager,float deltaThreshold)352 ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::HistogramQueryWorker(
353 OperationRateManager* opRateManager, float deltaThreshold)
354 : Worker("HistogramQueryWorker", HAL_PRIORITY_URGENT_DISPLAY),
355 mOpRateManager(opRateManager),
356 mSpAIBinder(nullptr),
357 mReady(false),
358 mQueryMode(false),
359 mHistogramLumaDeltaThreshold(deltaThreshold),
360 mPrevHistogramLuma(0) {
361 InitWorker();
362 }
363
~HistogramQueryWorker()364 ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::~HistogramQueryWorker() {
365 unprepare();
366 mReady = false;
367 Exit();
368 }
369
stub_OnCreate(void *)370 void* stub_OnCreate(void*) {
371 return nullptr;
372 }
373
stub_OnDestroy(void *)374 void stub_OnDestroy(void*) {}
375
stub_OnTransact(AIBinder *,transaction_code_t,const AParcel *,AParcel *)376 binder_status_t stub_OnTransact(AIBinder*, transaction_code_t, const AParcel*, AParcel*) {
377 return STATUS_OK;
378 }
379
prepare()380 void ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::prepare() {
381 AIBinder_Class* binderClass = AIBinder_Class_define("disp_op_query_worker", stub_OnCreate,
382 stub_OnDestroy, stub_OnTransact);
383 AIBinder* aibinder = AIBinder_new(binderClass, nullptr);
384 mSpAIBinder.set(aibinder);
385
386 if (mSpAIBinder.get()) {
387 // assign (0, 0, 0, 0) to indicate full screen roi since display probably isn't ready
388 mConfig.roi.left = 0;
389 mConfig.roi.top = 0;
390 mConfig.roi.right = 0;
391 mConfig.roi.bottom = 0;
392 mConfig.weights.weightR = kHistogramConfigWeightR;
393 mConfig.weights.weightG = kHistogramConfigWeightG;
394 mConfig.weights.weightB = kHistogramConfigWeightB;
395 mConfig.samplePos = HistogramDevice::HistogramSamplePos::POST_POSTPROC;
396
397 HistogramDevice::HistogramErrorCode err = HistogramDevice::HistogramErrorCode::NONE;
398 ndk::ScopedAStatus status =
399 mOpRateManager->mDisplay->mHistogramController->registerHistogram(mSpAIBinder,
400 mConfig, &err);
401 if (!status.isOk()) {
402 OP_MANAGER_LOGE(mOpRateManager->mDisplay, "failed to register histogram (binder err)");
403 return;
404 }
405 if (err != HistogramDevice::HistogramErrorCode::NONE) {
406 OP_MANAGER_LOGE(mOpRateManager->mDisplay, "failed to register histogram (hist err)");
407 return;
408 }
409 } else {
410 OP_MANAGER_LOGE(mOpRateManager->mDisplay, "failed to get binder for histogram");
411 return;
412 }
413
414 // assign panel resolution for isRuntimeResolutionConfig()
415 mConfig.roi.right = mOpRateManager->mDisplay->mXres;
416 mConfig.roi.bottom = mOpRateManager->mDisplay->mYres;
417 mReady = true;
418 OP_MANAGER_LOGI(mOpRateManager->mDisplay, "register histogram successfully");
419 }
420
unprepare()421 void ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::unprepare() {
422 if (!mReady) return;
423
424 HistogramDevice::HistogramErrorCode err = HistogramDevice::HistogramErrorCode::NONE;
425 mOpRateManager->mDisplay->mHistogramController->unregisterHistogram(mSpAIBinder, &err);
426 if (err != HistogramDevice::HistogramErrorCode::NONE) {
427 OP_MANAGER_LOGE(mOpRateManager->mDisplay, "failed to unregister histogram");
428 }
429 }
430
431 bool ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::
isRuntimeResolutionConfig() const432 isRuntimeResolutionConfig() const {
433 if (!mReady) return false;
434
435 if (mConfig.roi.right == mOpRateManager->mDisplay->mXres ||
436 mConfig.roi.bottom == mOpRateManager->mDisplay->mYres) {
437 return false;
438 }
439
440 // histogram will change roi automatically, no need to reconfig
441 DISPLAY_STR_LOGD(DISP_STR(mOpRateManager->mDisplay), eDebugOperationRate,
442 "histogram %dx%d->%dx%d", mConfig.roi.right, mConfig.roi.bottom,
443 mOpRateManager->mDisplay->mXres, mOpRateManager->mDisplay->mYres);
444 return true;
445 }
446
updateConfig(uint32_t xres,uint32_t yres)447 void ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::updateConfig(
448 uint32_t xres, uint32_t yres) {
449 mConfig.roi.right = xres;
450 mConfig.roi.bottom = yres;
451 }
452
startQuery()453 void ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::startQuery() {
454 if (!mReady) return;
455
456 Signal();
457 }
458
stopQuery()459 void ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::stopQuery() {
460 mQueryMode = false;
461 }
462
Routine()463 void ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::Routine() {
464 if (!mOpRateManager->mDisplay->mHistogramController) return;
465
466 if (!mReady) {
467 prepare();
468 return;
469 }
470
471 int ret;
472 // WaitForSignalOrExitLocked() needs to be enclosed by Lock() and Unlock()
473 Lock();
474 if (!mQueryMode) {
475 DISPLAY_STR_LOGD(DISP_STR(mOpRateManager->mDisplay), eDebugOperationRate,
476 "histogram wait for signal");
477 ret = WaitForSignalOrExitLocked();
478 mQueryMode = true;
479 mPrevHistogramLuma = 0;
480 } else {
481 ret = WaitForSignalOrExitLocked(kQueryPeriodNanosecs);
482 }
483 if (ret == -EINTR) {
484 OP_MANAGER_LOGE(mOpRateManager->mDisplay, "histogram failed to wait for signal");
485 mQueryMode = false;
486 Unlock();
487 return;
488 }
489 Unlock();
490
491 HistogramDevice::HistogramErrorCode err = HistogramDevice::HistogramErrorCode::NONE;
492 std::vector<char16_t> data;
493 ndk::ScopedAStatus status =
494 mOpRateManager->mDisplay->mHistogramController->queryHistogram(mSpAIBinder, &data,
495 &err);
496 if (status.isOk() && err != HistogramDevice::HistogramErrorCode::BAD_TOKEN) {
497 if (!data.size()) {
498 OP_MANAGER_LOGW(mOpRateManager->mDisplay, "histogram data is empty");
499 return;
500 }
501
502 float lumaSum = 0;
503 int count = 0;
504 for (int i = 0; i < data.size(); i++) {
505 lumaSum += i * static_cast<int>(data[i]);
506 count += static_cast<int>(data[i]);
507 }
508 if (!count) {
509 OP_MANAGER_LOGW(mOpRateManager->mDisplay, "histogram count is 0");
510 return;
511 }
512
513 float luma = lumaSum / count;
514 float lumaDelta = abs(luma - mPrevHistogramLuma);
515 DISPLAY_STR_LOGD(DISP_STR(mOpRateManager->mDisplay), eDebugOperationRate,
516 "histogram luma %f, delta %f, th %f", luma, lumaDelta,
517 mHistogramLumaDeltaThreshold);
518 if (mPrevHistogramLuma && lumaDelta > mHistogramLumaDeltaThreshold) {
519 mQueryMode = false;
520 mOpRateManager->onHistogram();
521 mOpRateManager->mDisplay->handleTargetOperationRate();
522 }
523 mPrevHistogramLuma = luma;
524 } else {
525 OP_MANAGER_LOGE(mOpRateManager->mDisplay, "histogram failed to query");
526 }
527 }
528