1 /*
2 * Copyright (C) 2012 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 //#define LOG_NDEBUG 0
17
18 #define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
19
20 #include "ExynosPrimaryDisplay.h"
21
22 #include <linux/fb.h>
23 #include <poll.h>
24
25 #include <chrono>
26 #include <fstream>
27
28 #include "ExynosDevice.h"
29 #include "ExynosDisplayDrmInterface.h"
30 #include "ExynosDisplayDrmInterfaceModule.h"
31 #include "ExynosExternalDisplay.h"
32 #include "ExynosHWCDebug.h"
33 #include "ExynosHWCHelper.h"
34
35 extern struct exynos_hwc_control exynosHWCControl;
36
37 using namespace SOC_VERSION;
38
39 static const std::map<const DisplayType, const std::string> panelSysfsPath =
40 {{DisplayType::DISPLAY_PRIMARY, "/sys/devices/platform/exynos-drm/primary-panel/"},
41 {DisplayType::DISPLAY_SECONDARY, "/sys/devices/platform/exynos-drm/secondary-panel/"}};
42
loadPanelGammaCalibration(const std::string & file)43 static std::string loadPanelGammaCalibration(const std::string &file) {
44 std::ifstream ifs(file);
45
46 if (!ifs.is_open()) {
47 ALOGW("Unable to open gamma calibration '%s', error = %s", file.c_str(), strerror(errno));
48 return {};
49 }
50
51 std::string raw_data, gamma;
52 char ch;
53 while (std::getline(ifs, raw_data, '\r')) {
54 gamma.append(raw_data);
55 gamma.append(1, ' ');
56 ifs.get(ch);
57 if (ch != '\n') {
58 gamma.append(1, ch);
59 }
60 }
61 ifs.close();
62
63 /* eliminate space character in the last byte */
64 if (!gamma.empty()) {
65 gamma.pop_back();
66 }
67
68 return gamma;
69 }
70
ExynosPrimaryDisplay(uint32_t index,ExynosDevice * device)71 ExynosPrimaryDisplay::ExynosPrimaryDisplay(uint32_t index, ExynosDevice *device)
72 : ExynosDisplay(index, device),
73 mMinIdleRefreshRate(0),
74 mRefreshRateDelayNanos(0),
75 mLastRefreshRateAppliedNanos(0),
76 mAppliedActiveConfig(0) {
77 // TODO : Hard coded here
78 mNumMaxPriorityAllowed = 5;
79
80 /* Initialization */
81 mType = HWC_DISPLAY_PRIMARY;
82 mIndex = index;
83 mDisplayId = getDisplayId(mType, mIndex);
84
85 // Prepare multi resolution
86 // Will be exynosHWCControl.multiResoultion
87 mResolutionInfo.nNum = 1;
88 mResolutionInfo.nResolution[0].w = 1440;
89 mResolutionInfo.nResolution[0].h = 2960;
90 mResolutionInfo.nDSCYSliceSize[0] = 40;
91 mResolutionInfo.nDSCXSliceSize[0] = 1440 / 2;
92 mResolutionInfo.nPanelType[0] = PANEL_DSC;
93 mResolutionInfo.nResolution[1].w = 1080;
94 mResolutionInfo.nResolution[1].h = 2220;
95 mResolutionInfo.nDSCYSliceSize[1] = 30;
96 mResolutionInfo.nDSCXSliceSize[1] = 1080 / 2;
97 mResolutionInfo.nPanelType[1] = PANEL_DSC;
98 mResolutionInfo.nResolution[2].w = 720;
99 mResolutionInfo.nResolution[2].h = 1480;
100 mResolutionInfo.nDSCYSliceSize[2] = 74;
101 mResolutionInfo.nDSCXSliceSize[2] = 720;
102 mResolutionInfo.nPanelType[2] = PANEL_LEGACY;
103
104 static_assert(sizeof(BRIGHTNESS_NODE_0_BASE) != 0 && sizeof(MAX_BRIGHTNESS_NODE_0_BASE) != 0,
105 "Invalid brightness 0 node");
106 static_assert(sizeof(BRIGHTNESS_NODE_1_BASE) != 0 && sizeof(MAX_BRIGHTNESS_NODE_1_BASE) != 0,
107 "Invalid brightness 1 node");
108 std::string brightness_node;
109 std::string max_brightness_node;
110 switch (mIndex) {
111 case 0:
112 max_brightness_node = MAX_BRIGHTNESS_NODE_0_BASE;
113 brightness_node = BRIGHTNESS_NODE_0_BASE;
114 break;
115 case 1:
116 max_brightness_node = MAX_BRIGHTNESS_NODE_1_BASE;
117 brightness_node = BRIGHTNESS_NODE_1_BASE;
118 break;
119 default:
120 ALOGE("assgin brightness node failed (mIndex: %d)", mIndex);
121 break;
122 }
123
124 FILE *maxBrightnessFd = fopen(max_brightness_node.c_str(), "r");
125 ALOGI("Trying %s open for get max brightness", max_brightness_node.c_str());
126
127 if (maxBrightnessFd != NULL) {
128 char val[MAX_BRIGHTNESS_LEN] = {0};
129 size_t size = fread(val, 1, MAX_BRIGHTNESS_LEN, maxBrightnessFd);
130 if (size) {
131 mMaxBrightness = atoi(val);
132 ALOGI("Max brightness : %d", mMaxBrightness);
133
134 mBrightnessFd = fopen(brightness_node.c_str(), "w+");
135 ALOGI("Trying %s open for brightness control", brightness_node.c_str());
136
137 if (mBrightnessFd == NULL)
138 ALOGE("%s open failed! %s", brightness_node.c_str(), strerror(errno));
139
140 } else {
141 ALOGE("Max brightness read failed (size: %zu)", size);
142 if (ferror(maxBrightnessFd)) {
143 ALOGE("An error occurred");
144 clearerr(maxBrightnessFd);
145 }
146 }
147 fclose(maxBrightnessFd);
148 } else {
149 ALOGE("Brightness node is not opened");
150 }
151
152 #if defined EARLY_WAKUP_NODE_BASE
153 mEarlyWakeupFd = fopen(EARLY_WAKUP_NODE_BASE, "w");
154 if (mEarlyWakeupFd == NULL)
155 ALOGE("%s open failed! %s", EARLY_WAKUP_NODE_BASE, strerror(errno));
156 #endif
157
158 mLhbmFd = fopen(kLocalHbmModeFileNode, "w+");
159 if (mLhbmFd == nullptr) ALOGE("local hbm mode node open failed! %s", strerror(errno));
160
161 mWakeupDispFd = fopen(kWakeupDispFilePath, "w");
162 if (mWakeupDispFd == nullptr) ALOGE("wake up display node open failed! %s", strerror(errno));
163 }
164
~ExynosPrimaryDisplay()165 ExynosPrimaryDisplay::~ExynosPrimaryDisplay()
166 {
167 if (mWakeupDispFd) {
168 fclose(mWakeupDispFd);
169 mWakeupDispFd = nullptr;
170 }
171
172 if (mLhbmFd) {
173 fclose(mLhbmFd);
174 mLhbmFd = nullptr;
175 }
176
177 if (mBrightnessFd) {
178 fclose(mBrightnessFd);
179 mBrightnessFd = nullptr;
180 }
181 }
182
setDDIScalerEnable(int width,int height)183 void ExynosPrimaryDisplay::setDDIScalerEnable(int width, int height) {
184
185 if (exynosHWCControl.setDDIScaler == false) return;
186
187 ALOGI("DDISCALER Info : setDDIScalerEnable(w=%d,h=%d)", width, height);
188 mNewScaledWidth = width;
189 mNewScaledHeight = height;
190 mXres = width;
191 mYres = height;
192 }
193
getDDIScalerMode(int width,int height)194 int ExynosPrimaryDisplay::getDDIScalerMode(int width, int height) {
195
196 if (exynosHWCControl.setDDIScaler == false) return 1;
197
198 // Check if panel support support resolution or not.
199 for (uint32_t i=0; i < mResolutionInfo.nNum; i++) {
200 if (mResolutionInfo.nResolution[i].w * mResolutionInfo.nResolution[i].h ==
201 static_cast<uint32_t>(width * height))
202 return i + 1;
203 }
204
205 return 1; // WQHD
206 }
207
doDisplayConfigInternal(hwc2_config_t config)208 int32_t ExynosPrimaryDisplay::doDisplayConfigInternal(hwc2_config_t config) {
209 if (mPowerModeState != HWC2_POWER_MODE_ON) {
210 mPendActiveConfig = config;
211 mConfigRequestState = hwc_request_state_t::SET_CONFIG_STATE_NONE;
212 DISPLAY_LOGI("%s:: Pending desired Config: %d", __func__, config);
213 return NO_ERROR;
214 }
215 return ExynosDisplay::doDisplayConfigInternal(config);
216 }
217
getActiveConfigInternal(hwc2_config_t * outConfig)218 int32_t ExynosPrimaryDisplay::getActiveConfigInternal(hwc2_config_t *outConfig) {
219 if (outConfig && mPendActiveConfig != UINT_MAX) {
220 *outConfig = mPendActiveConfig;
221 return HWC2_ERROR_NONE;
222 }
223 return ExynosDisplay::getActiveConfigInternal(outConfig);
224 }
225
setActiveConfigInternal(hwc2_config_t config,bool force)226 int32_t ExynosPrimaryDisplay::setActiveConfigInternal(hwc2_config_t config, bool force) {
227 hwc2_config_t cur_config;
228
229 getActiveConfigInternal(&cur_config);
230 if (cur_config == config) {
231 ALOGI("%s:: Same display config is set", __func__);
232 return HWC2_ERROR_NONE;
233 }
234 if (mPowerModeState != HWC2_POWER_MODE_ON) {
235 mPendActiveConfig = config;
236 return HWC2_ERROR_NONE;
237 }
238 return ExynosDisplay::setActiveConfigInternal(config, force);
239 }
240
applyPendingConfig()241 int32_t ExynosPrimaryDisplay::applyPendingConfig() {
242 hwc2_config_t config;
243
244 if (mPendActiveConfig != UINT_MAX) {
245 config = mPendActiveConfig;
246 mPendActiveConfig = UINT_MAX;
247 } else {
248 getActiveConfigInternal(&config);
249 }
250
251 return ExynosDisplay::setActiveConfigInternal(config, true);
252 }
253
setPowerOn()254 int32_t ExynosPrimaryDisplay::setPowerOn() {
255 ATRACE_CALL();
256 updateAppliedActiveConfig(0, 0);
257 int ret = applyPendingConfig();
258
259 if (mPowerModeState == HWC2_POWER_MODE_OFF) {
260 // check the dynamic recomposition thread by following display
261 mDevice->checkDynamicRecompositionThread();
262 if (ret) {
263 mDisplayInterface->setPowerMode(HWC2_POWER_MODE_ON);
264 }
265 setGeometryChanged(GEOMETRY_DISPLAY_POWER_ON);
266 }
267
268 mPowerModeState = HWC2_POWER_MODE_ON;
269
270 if (mFirstPowerOn) {
271 firstPowerOn();
272 }
273
274 return HWC2_ERROR_NONE;
275 }
276
setPowerOff()277 int32_t ExynosPrimaryDisplay::setPowerOff() {
278 ATRACE_CALL();
279
280 clearDisplay(true);
281
282 // check the dynamic recomposition thread by following display
283 mDevice->checkDynamicRecompositionThread();
284
285 mDisplayInterface->setPowerMode(HWC2_POWER_MODE_OFF);
286 mPowerModeState = HWC2_POWER_MODE_OFF;
287
288 /* It should be called from validate() when the screen is on */
289 mSkipFrame = true;
290 setGeometryChanged(GEOMETRY_DISPLAY_POWER_OFF);
291 if ((mRenderingState >= RENDERING_STATE_VALIDATED) &&
292 (mRenderingState < RENDERING_STATE_PRESENTED))
293 closeFencesForSkipFrame(RENDERING_STATE_VALIDATED);
294 mRenderingState = RENDERING_STATE_NONE;
295
296 return HWC2_ERROR_NONE;
297 }
298
setPowerDoze(hwc2_power_mode_t mode)299 int32_t ExynosPrimaryDisplay::setPowerDoze(hwc2_power_mode_t mode) {
300 ATRACE_CALL();
301
302 if (!mDisplayInterface->isDozeModeAvailable()) {
303 return HWC2_ERROR_UNSUPPORTED;
304 }
305
306 if ((mPowerModeState == HWC2_POWER_MODE_OFF) || (mPowerModeState == HWC2_POWER_MODE_ON)) {
307 if (mDisplayInterface->setLowPowerMode()) {
308 ALOGI("Not support LP mode.");
309 return HWC2_ERROR_UNSUPPORTED;
310 }
311 }
312
313 mPowerModeState = mode;
314
315 ExynosDisplay::updateRefreshRateHint();
316
317 return HWC2_ERROR_NONE;
318 }
319
setPowerMode(int32_t mode)320 int32_t ExynosPrimaryDisplay::setPowerMode(int32_t mode) {
321 Mutex::Autolock lock(mDisplayMutex);
322
323 if (mode == static_cast<int32_t>(ext_hwc2_power_mode_t::PAUSE)) {
324 mode = HWC2_POWER_MODE_OFF;
325 mPauseDisplay = true;
326 } else if (mode == static_cast<int32_t>(ext_hwc2_power_mode_t::RESUME)) {
327 mode = HWC2_POWER_MODE_ON;
328 mPauseDisplay = false;
329 }
330
331 if (mode == static_cast<int32_t>(mPowerModeState)) {
332 ALOGI("Skip power mode transition due to the same power state.");
333 return HWC2_ERROR_NONE;
334 }
335
336 int fb_blank = (mode != HWC2_POWER_MODE_OFF) ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
337 ALOGD("%s:: FBIOBLANK mode(%d), blank(%d)", __func__, mode, fb_blank);
338
339 if (fb_blank == FB_BLANK_POWERDOWN)
340 mDREnable = false;
341 else
342 mDREnable = mDRDefault;
343
344 switch (mode) {
345 case HWC2_POWER_MODE_DOZE_SUSPEND:
346 case HWC2_POWER_MODE_DOZE:
347 return setPowerDoze(static_cast<hwc2_power_mode_t>(mode));
348 case HWC2_POWER_MODE_OFF:
349 setPowerOff();
350 break;
351 case HWC2_POWER_MODE_ON:
352 setPowerOn();
353 break;
354 default:
355 return HWC2_ERROR_BAD_PARAMETER;
356 }
357
358 ExynosDisplay::updateRefreshRateHint();
359
360 return HWC2_ERROR_NONE;
361 }
362
firstPowerOn()363 void ExynosPrimaryDisplay::firstPowerOn() {
364 SetCurrentPanelGammaSource(DisplayType::DISPLAY_PRIMARY, PanelGammaSource::GAMMA_CALIBRATION);
365 mFirstPowerOn = false;
366 }
367
getHDRException(ExynosLayer * __unused layer)368 bool ExynosPrimaryDisplay::getHDRException(ExynosLayer* __unused layer)
369 {
370 return false;
371 }
372
initDisplayInterface(uint32_t interfaceType)373 void ExynosPrimaryDisplay::initDisplayInterface(uint32_t interfaceType)
374 {
375 if (interfaceType == INTERFACE_TYPE_DRM)
376 mDisplayInterface = std::make_unique<ExynosPrimaryDisplayDrmInterfaceModule>((ExynosDisplay *)this);
377 else
378 LOG_ALWAYS_FATAL("%s::Unknown interface type(%d)",
379 __func__, interfaceType);
380 mDisplayInterface->init(this);
381 }
382
getPanelSysfsPath(const DisplayType & type)383 std::string ExynosPrimaryDisplay::getPanelSysfsPath(const DisplayType &type) {
384 if ((type < DisplayType::DISPLAY_PRIMARY) || (type >= DisplayType::DISPLAY_MAX)) {
385 ALOGE("Invalid display panel type %d", type);
386 return {};
387 }
388
389 auto iter = panelSysfsPath.find(type);
390 if (iter == panelSysfsPath.end()) {
391 return {};
392 }
393
394 return iter->second;
395 }
396
SetCurrentPanelGammaSource(const DisplayType type,const PanelGammaSource & source)397 int32_t ExynosPrimaryDisplay::SetCurrentPanelGammaSource(const DisplayType type,
398 const PanelGammaSource &source) {
399 std::string &&panel_sysfs_path = getPanelSysfsPath(type);
400 if (panel_sysfs_path.empty()) {
401 return HWC2_ERROR_UNSUPPORTED;
402 }
403
404 std::ifstream ifs;
405 std::string &&path = panel_sysfs_path + "panel_name";
406 ifs.open(path, std::ifstream::in);
407 if (!ifs.is_open()) {
408 ALOGW("Unable to access panel name path '%s' (%s)", path.c_str(), strerror(errno));
409 return HWC2_ERROR_UNSUPPORTED;
410 }
411 std::string panel_name;
412 std::getline(ifs, panel_name);
413 ifs.close();
414
415 path = panel_sysfs_path + "serial_number";
416 ifs.open(path, std::ifstream::in);
417 if (!ifs.is_open()) {
418 ALOGW("Unable to access panel id path '%s' (%s)", path.c_str(), strerror(errno));
419 return HWC2_ERROR_UNSUPPORTED;
420 }
421 std::string panel_id;
422 std::getline(ifs, panel_id);
423 ifs.close();
424
425 std::string gamma_node = panel_sysfs_path + "gamma";
426 if (access(gamma_node.c_str(), W_OK)) {
427 ALOGW("Unable to access panel gamma calibration node '%s' (%s)", gamma_node.c_str(),
428 strerror(errno));
429 return HWC2_ERROR_UNSUPPORTED;
430 }
431
432 std::string &&gamma_data = "default";
433 if (source == PanelGammaSource::GAMMA_CALIBRATION) {
434 std::string gamma_cal_file(kDisplayCalFilePath);
435 gamma_cal_file.append(kPanelGammaCalFilePrefix)
436 .append(1, '_')
437 .append(panel_name)
438 .append(1, '_')
439 .append(panel_id)
440 .append(".cal");
441 if (access(gamma_cal_file.c_str(), R_OK)) {
442 ALOGI("Fail to access `%s` (%s), try golden gamma calibration", gamma_cal_file.c_str(),
443 strerror(errno));
444 gamma_cal_file = kDisplayCalFilePath;
445 gamma_cal_file.append(kPanelGammaCalFilePrefix)
446 .append(1, '_')
447 .append(panel_name)
448 .append(".cal");
449 }
450 gamma_data = loadPanelGammaCalibration(gamma_cal_file);
451 }
452
453 if (gamma_data.empty()) {
454 return HWC2_ERROR_UNSUPPORTED;
455 }
456
457 std::ofstream ofs(gamma_node);
458 if (!ofs.is_open()) {
459 ALOGW("Unable to open gamma node '%s', error = %s", gamma_node.c_str(), strerror(errno));
460 return HWC2_ERROR_UNSUPPORTED;
461 }
462 ofs.write(gamma_data.c_str(), gamma_data.size());
463 ofs.close();
464
465 currentPanelGammaSource = source;
466 return HWC2_ERROR_NONE;
467 }
468
469 // Both setDisplayBrightness and setLhbmState will change display brightness and
470 // each goes different path (sysfs and drm/kms)
471 //
472 // case 1: setDisplayBrightness happens before setLhbmState
473 // Don't care. brightness change by setLhbmState will happen after brightness
474 // change by setDisplayBrightness.
475 //
476 // case 2: setLhbmState happends before setDisplayBrightness
477 // block current call until brightness change by setLhbmState completes.
setDisplayBrightness(float brightness)478 int32_t ExynosPrimaryDisplay::setDisplayBrightness(float brightness) {
479 if (mLhbmStatusPending) {
480 // This could be done in setLhbmState and block this call on
481 // mLhbmStatusPending. But it may increase the time for UDFPS path
482 checkLhbmMode(mLastRequestedLhbm, ms2ns(200));
483 mLhbmStatusPending = false;
484 }
485 return ExynosDisplay::setDisplayBrightness(brightness);
486 }
487
setLhbmState(bool enabled)488 int32_t ExynosPrimaryDisplay::setLhbmState(bool enabled) {
489 ATRACE_CALL();
490 requestLhbm(enabled);
491 ALOGI("setLhbmState =%d", enabled);
492
493 std::unique_lock<std::mutex> lk(lhbm_mutex_);
494 mLhbmChanged = false;
495
496 mLhbmStatusPending = true;
497 mLastRequestedLhbm = enabled;
498
499 if (!lhbm_cond_.wait_for(lk, std::chrono::milliseconds(1000),
500 [this] { return mLhbmChanged; })) {
501 ALOGI("setLhbmState =%d timeout !", enabled);
502 return TIMED_OUT;
503 } else {
504 if (enabled)
505 mDisplayInterface->waitVBlank();
506 return NO_ERROR;
507 }
508 }
509
510 // return immediately if it's already in the status. Otherwise poll the status
checkLhbmMode(bool status,nsecs_t timeoutNs)511 bool ExynosPrimaryDisplay::checkLhbmMode(bool status, nsecs_t timeoutNs) {
512 ATRACE_CALL();
513 char buf[1];
514 auto startTime = systemTime(SYSTEM_TIME_MONOTONIC);
515
516 UniqueFd fd = open(kLocalHbmModeFileNode, O_RDONLY);
517
518 int size = read(fd.get(), buf, 1);
519 if (size != 1) {
520 ALOGE("%s failed to read from %s", __func__, kLocalHbmModeFileNode);
521 return false;
522 }
523
524 if (buf[0] == (status ? '1' : '0')) {
525 return true;
526 }
527
528 struct pollfd pfds[1];
529 int ret = EINVAL;
530
531 pfds[0].fd = fd.get();
532 pfds[0].events = POLLPRI;
533 while (true) {
534 auto currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
535 // int64_t for nsecs_t
536 auto remainTimeNs = timeoutNs - (currentTime - startTime);
537 if (remainTimeNs <= 0) {
538 remainTimeNs = ms2ns(1);
539 }
540 int pollRet = poll(&pfds[0], 1, ns2ms(remainTimeNs));
541 if (pollRet == 0) {
542 ALOGW("%s poll timeout", __func__);
543 // time out
544 ret = ETIMEDOUT;
545 break;
546 } else if (pollRet > 0) {
547 if (!(pfds[0].revents & POLLPRI)) {
548 continue;
549 }
550
551 lseek(fd.get(), 0, SEEK_SET);
552 size = read(fd.get(), buf, 1);
553 if (size == 1) {
554 if (buf[0] == (status ? '1' : '0')) {
555 ret = 0;
556 } else {
557 ALOGE("%s status %d expected %d after notified", __func__, buf[0], status);
558 ret = EINVAL;
559 }
560 } else {
561 ret = EIO;
562 ALOGE("%s failed to read after notified %d", __func__, errno);
563 }
564 break;
565 } else {
566 if (errno == EAGAIN || errno == EINTR) {
567 continue;
568 }
569
570 ALOGE("%s poll failed %d", __func__, errno);
571 ret = errno;
572 break;
573 }
574 };
575
576 return ret == NO_ERROR;
577 }
578
getLhbmState()579 bool ExynosPrimaryDisplay::getLhbmState() {
580 return mLhbmOn;
581 }
582
notifyLhbmState(bool enabled)583 void ExynosPrimaryDisplay::notifyLhbmState(bool enabled) {
584 std::lock_guard<std::mutex> lk(lhbm_mutex_);
585 mLhbmChanged = true;
586 lhbm_cond_.notify_one();
587 mLhbmOn = enabled;
588 }
589
setWakeupDisplay()590 void ExynosPrimaryDisplay::setWakeupDisplay() {
591 if (mWakeupDispFd) {
592 writeFileNode(mWakeupDispFd, 1);
593 }
594 }
595
setMinIdleRefreshRate(const int fps)596 int ExynosPrimaryDisplay::setMinIdleRefreshRate(const int fps) {
597 mMinIdleRefreshRate = fps;
598
599 const std::string path = getPanelSysfsPath(DisplayType::DISPLAY_PRIMARY) + "min_vrefresh";
600 std::ofstream ofs(path);
601 if (!ofs.is_open()) {
602 ALOGW("Unable to open node '%s', error = %s", path.c_str(), strerror(errno));
603 return errno;
604 } else {
605 ofs << mMinIdleRefreshRate;
606 ofs.close();
607 ALOGI("ExynosPrimaryDisplay::%s() writes min_vrefresh(%d) to the sysfs node", __func__,
608 fps);
609 }
610 return NO_ERROR;
611 }
612
setRefreshRateThrottleNanos(const int64_t delayNanos)613 int ExynosPrimaryDisplay::setRefreshRateThrottleNanos(const int64_t delayNanos) {
614 mRefreshRateDelayNanos = delayNanos;
615
616 const int32_t refreshRateDelayMs = std::chrono::duration_cast<std::chrono::milliseconds>(
617 std::chrono::nanoseconds(mRefreshRateDelayNanos))
618 .count();
619 const std::string path = getPanelSysfsPath(DisplayType::DISPLAY_PRIMARY) + "idle_delay_ms";
620 std::ofstream ofs(path);
621 if (!ofs.is_open()) {
622 ALOGW("Unable to open node '%s', error = %s", path.c_str(), strerror(errno));
623 return errno;
624 } else {
625 ofs << refreshRateDelayMs;
626 ofs.close();
627 ALOGI("ExynosPrimaryDisplay::%s() writes idle_delay_ms(%d) to the sysfs node", __func__,
628 refreshRateDelayMs);
629 }
630
631 return NO_ERROR;
632 }
633
dump(String8 & result)634 void ExynosPrimaryDisplay::dump(String8 &result) {
635 ExynosDisplay::dump(result);
636 result.appendFormat("Min idle refresh rate: %d\n", mMinIdleRefreshRate);
637 result.appendFormat("Refresh rate delay: %" PRId64 "ns\n\n", mRefreshRateDelayNanos);
638 }
639
calculateTimeline(hwc2_config_t config,hwc_vsync_period_change_constraints_t * vsyncPeriodChangeConstraints,hwc_vsync_period_change_timeline_t * outTimeline)640 void ExynosPrimaryDisplay::calculateTimeline(
641 hwc2_config_t config, hwc_vsync_period_change_constraints_t *vsyncPeriodChangeConstraints,
642 hwc_vsync_period_change_timeline_t *outTimeline) {
643 int64_t desiredUpdateTime = vsyncPeriodChangeConstraints->desiredTimeNanos;
644 const int64_t origDesiredUpdateTime = desiredUpdateTime;
645 const int64_t threshold = mRefreshRateDelayNanos;
646 int64_t lastUpdateDelta = 0;
647 int64_t actualChangeTime = 0;
648 bool isDelayed = false;
649
650 /* actualChangeTime includes transient duration */
651 mDisplayInterface->getVsyncAppliedTime(config, &actualChangeTime);
652
653 outTimeline->refreshRequired = true;
654
655 /* when refresh rate is from high to low */
656 if (threshold != 0 && mLastRefreshRateAppliedNanos != 0 &&
657 mDisplayConfigs[mActiveConfig].vsyncPeriod < mDisplayConfigs[config].vsyncPeriod) {
658 lastUpdateDelta = desiredUpdateTime - mLastRefreshRateAppliedNanos;
659 if (lastUpdateDelta < threshold) {
660 /* in this case, the active config change needs to be delayed */
661 isDelayed = true;
662 desiredUpdateTime += threshold - lastUpdateDelta;
663 }
664 }
665 mVsyncPeriodChangeConstraints.desiredTimeNanos = desiredUpdateTime;
666
667 getConfigAppliedTime(mVsyncPeriodChangeConstraints.desiredTimeNanos, actualChangeTime,
668 outTimeline->newVsyncAppliedTimeNanos, outTimeline->refreshTimeNanos);
669
670 if (isDelayed) {
671 DISPLAY_LOGD(eDebugDisplayConfig,
672 "requested config : %d(%d)->%d(%d) is delayed! "
673 "delta %" PRId64 ", delay %" PRId64 ", threshold %" PRId64 ", "
674 "desired %" PRId64 "->%" PRId64 ", newVsyncAppliedTimeNanos : %" PRId64
675 ", refreshTimeNanos:%" PRId64,
676 mActiveConfig, mDisplayConfigs[mActiveConfig].vsyncPeriod, config,
677 mDisplayConfigs[config].vsyncPeriod, lastUpdateDelta,
678 threshold - lastUpdateDelta, threshold, origDesiredUpdateTime,
679 mVsyncPeriodChangeConstraints.desiredTimeNanos,
680 outTimeline->newVsyncAppliedTimeNanos, outTimeline->refreshTimeNanos);
681 } else {
682 DISPLAY_LOGD(eDebugDisplayConfig,
683 "requested config : %d(%d)->%d(%d), "
684 "lastUpdateDelta %" PRId64 ", threshold %" PRId64 ", "
685 "desired %" PRId64 ", newVsyncAppliedTimeNanos : %" PRId64 "",
686 mActiveConfig, mDisplayConfigs[mActiveConfig].vsyncPeriod, config,
687 mDisplayConfigs[config].vsyncPeriod, lastUpdateDelta, threshold,
688 mVsyncPeriodChangeConstraints.desiredTimeNanos,
689 outTimeline->newVsyncAppliedTimeNanos);
690 }
691 }
692
updateAppliedActiveConfig(const hwc2_config_t newConfig,const int64_t ts)693 void ExynosPrimaryDisplay::updateAppliedActiveConfig(const hwc2_config_t newConfig,
694 const int64_t ts) {
695 if (mAppliedActiveConfig == 0 ||
696 getDisplayVsyncPeriodFromConfig(mAppliedActiveConfig) !=
697 getDisplayVsyncPeriodFromConfig(newConfig)) {
698 DISPLAY_LOGD(eDebugDisplayConfig,
699 "%s mAppliedActiveConfig(%d->%d), mLastRefreshRateAppliedNanos(%" PRIu64
700 " -> %" PRIu64 ")",
701 __func__, mAppliedActiveConfig, newConfig, mLastRefreshRateAppliedNanos, ts);
702 mLastRefreshRateAppliedNanos = ts;
703 }
704
705 mAppliedActiveConfig = newConfig;
706 }
707