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