You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

546 lines
17 KiB

/*
* Copyright (c) 2020 Rockchip Electronics Co., Ltd
*/
#include "Power.h"
#include "PowerHintSession.h"
#include <aidl/android/hardware/power/Boost.h>
#define LOG_TAG "PowerAIDL"
#include <android-base/logging.h>
#include <cutils/properties.h>
#include <string>
#include <log/log.h>
#include <dirent.h>
#include <inttypes.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#define BUFFER_LENGTH 64
#define DEV_FREQ_PATH "/sys/class/devfreq"
#define CPU_CLUST_INFO_PATH "/sys/devices/system/cpu/cpufreq"
#define CPU_CORE_NUM 8
#define VERSION "version 13.0, add support for UFS/VOP"
static int is_inited = 0;
static int is_performance = 0;
static bool kDebug = false;
using ::android::base::StringPrintf;
namespace aidl {
namespace android {
namespace hardware {
namespace power {
namespace impl {
namespace rockchip {
using namespace std::chrono_literals;
using ndk::ScopedAStatus;
#define PW_LOG_DEBUG(...) if (kDebug) ALOGD(__VA_ARGS__)
static void sysfs_read(const std::string& path, std::string *buf) {
if (!::android::base::ReadFileToString(path, buf)) {
ALOGE("Error to open %s", path.c_str());
std::string realpath;
if (!::android::base::Realpath(path, &realpath)) {
ALOGE("Realpath is not exist!");
} else {
ALOGI("Trying read from realpath: %s", realpath.c_str());
sysfs_read(realpath, buf);
}
} else {
PW_LOG_DEBUG("read from %s value %s", path.c_str(), buf->c_str());
}
}
static void sysfs_write(const char *path, const char *s) {
PW_LOG_DEBUG("write %s %s", path, s);
if (access(path, F_OK) < 0) return;
char buf[80];
int len;
int fd = open(path, O_WRONLY);
if (fd < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error opening %s: %s\n", path, buf);
return;
}
len = write(fd, s, strlen(s));
if (len < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error writing to %s: %s\n", path, buf);
}
close(fd);
}
static void set_cpuidle(const char *sate) {
std::string cpu_core_path;
for (int i = 0; i < CPU_CORE_NUM; i ++) {
cpu_core_path = "/sys/devices/system/cpu/cpu" + std::to_string(i) + "/cpuidle/state1/disable";
sysfs_write(cpu_core_path.c_str(), sate);
}
}
ClusterInfo::ClusterInfo(const ClusterType type, const std::string& clust) : _type(type) {
std::string minPath, maxPath;
switch (type) {
case ClusterType::CPU:
_minFreqPath = StringPrintf("%s/%s/scaling_min_freq",
CPU_CLUST_INFO_PATH, clust.c_str());
_maxFreqPath = StringPrintf("%s/%s/scaling_max_freq",
CPU_CLUST_INFO_PATH, clust.c_str());
_govPath = StringPrintf("%s/%s/scaling_governor",
CPU_CLUST_INFO_PATH, clust.c_str());
minPath = StringPrintf("%s/%s/cpuinfo_min_freq",
CPU_CLUST_INFO_PATH, clust.c_str());
maxPath = StringPrintf("%s/%s/cpuinfo_max_freq",
CPU_CLUST_INFO_PATH, clust.c_str());
break;
case ClusterType::UFS:
[[fallthrough]];
case ClusterType::VOP:
[[fallthrough]];
case ClusterType::GPU:
_minFreqPath = StringPrintf("%s/min_freq", clust.c_str());
_maxFreqPath = StringPrintf("%s/max_freq", clust.c_str());
_govPath = StringPrintf("%s/governor", clust.c_str());
minPath = _minFreqPath;
maxPath = _maxFreqPath;
break;
case ClusterType::DDR:
_minFreqPath = StringPrintf("%s/min_freq", clust.c_str());
_maxFreqPath = StringPrintf("%s/max_freq", clust.c_str());
_govPath = StringPrintf("%s/system_status", clust.c_str());
minPath = _minFreqPath;
maxPath = _maxFreqPath;
break;
default:
break;
}
if (minPath == "" || maxPath == "") {
ALOGW("Failed to register, minPath/maxPath is null!");
return;
}
sysfs_read(minPath, &_minFreq);
sysfs_read(maxPath, &_maxFreq);
sysfs_read(_govPath, &_govDefault);
ALOGI("Registered %s as %s", clust.c_str(), toString().c_str());
}
std::string ClusterInfo::toString() {
std::string type;
switch (_type) {
case ClusterType::CPU:
type = "CPU";
break;
case ClusterType::GPU:
type = "GPU";
break;
case ClusterType::DDR:
type = "DDR";
break;
case ClusterType::VOP:
type = "VOP";
break;
case ClusterType::UFS:
type = "UFS";
break;
default:
type = "unknown";
break;
}
return StringPrintf("%s \nmin: %smax: %s",
type.c_str(), _minFreq.c_str(), _maxFreq.c_str());
}
void ClusterInfo::setMinFreq(const std::string& freq) {
sysfs_write(_minFreqPath.c_str(), freq.c_str());
}
void ClusterInfo::setMaxFreq(const std::string& freq) {
sysfs_write(_maxFreqPath.c_str(), freq.c_str());
}
void ClusterInfo::setPerformance(bool on) {
if (on) {
set_cpuidle("1");
setMinFreq(_maxFreq);
} else {
set_cpuidle("0");
setMinFreq(_minFreq);
}
}
void ClusterInfo::setGov(const std::string& governor) {
sysfs_write(_govPath.c_str(), governor.c_str());
}
void ClusterInfo::setPowerSave(bool on) {
switch (getType()) {
case ClusterType::CPU:
[[fallthrough]];
case ClusterType::VOP:
[[fallthrough]];
case ClusterType::UFS:
[[fallthrough]];
case ClusterType::GPU:
setGov(on ? "powersave" : _govDefault);
break;
case ClusterType::DDR:
setGov(on ? "l" : "L");
break;
default:
break;
}
}
void ClusterInfo::setInteractive() {
switch (getType()) {
case ClusterType::CPU:
setGov(_govDefault);
break;
default:
break;
}
}
void Power::initPlatform() {
if (is_inited || (_boot_complete <= 0)) return;
ALOGI(VERSION);
auto findWithPath = [&](const char *path, ClusterType type) {
std::unique_ptr<DIR, decltype(&closedir)>dir(opendir(path), closedir);
if (!dir) return;
std::string name;
dirent* dp;
while ((dp = readdir(dir.get())) != nullptr) {
if (type == ClusterType::GPU) {
name = dp->d_name;
if (strstr(name.c_str(), "gpu") != NULL) {
ClusterInfo gpu = ClusterInfo(ClusterType::GPU,
StringPrintf("%s/%s", DEV_FREQ_PATH, dp->d_name));
clusterList.push_back(gpu);
break;
}
} else if (type == ClusterType::VOP) {
name = dp->d_name;
if (strstr(name.c_str(), "vop") != NULL) {
ClusterInfo vop = ClusterInfo(ClusterType::VOP,
StringPrintf("%s/%s", DEV_FREQ_PATH, dp->d_name));
clusterList.push_back(vop);
break;
}
} else if (type == ClusterType::UFS) {
name = dp->d_name;
if (strstr(name.c_str(), "ufs") != NULL) {
ClusterInfo ufs = ClusterInfo(ClusterType::UFS,
StringPrintf("%s/%s", DEV_FREQ_PATH, dp->d_name));
clusterList.push_back(ufs);
break;
}
} else if (type == ClusterType::CPU) {
if (dp->d_name[0] == '.') {
continue;
}
ClusterInfo cpu = ClusterInfo(ClusterType::CPU, dp->d_name);
clusterList.push_back(cpu);
}
}
};
findWithPath(CPU_CLUST_INFO_PATH, ClusterType::CPU);
findWithPath(DEV_FREQ_PATH, ClusterType::GPU);
findWithPath(DEV_FREQ_PATH, ClusterType::VOP);
findWithPath(DEV_FREQ_PATH, ClusterType::UFS);
ClusterInfo ddr = ClusterInfo(ClusterType::DDR, "/sys/class/devfreq/dmc");
clusterList.push_back(ddr);
is_inited = 1;
}
void Power::getSupportedPlatform() {
if (_mode_support_int < 0) {
_boost_support_int = property_get_int64("ro.vendor.power.boost_support", 0x003F);
// Disable power save by default.
_mode_support_int = property_get_int64("ro.vendor.power.mode_support", 0x7FFF & 0xDF9F);
ALOGI("Initial with boost: %" PRId64", mode: %" PRId64,
_boost_support_int, _mode_support_int);
}
if (_boot_complete <= 0) {
_boot_complete = property_get_bool("vendor.boot_completed", 0);
PW_LOG_DEBUG("Boot complete: %s", _boot_complete?"true":"false");
}
initPlatform();
}
ScopedAStatus Power::setMode(Mode type, bool enabled) {
PW_LOG_DEBUG("Power setMode: %s to: %s", toString(type).c_str(), (enabled?"on":"off"));
getSupportedPlatform();
switch (type) {
case Mode::DOUBLE_TAP_TO_WAKE:
break;
case Mode::LOW_POWER:
powerSave(enabled);
break;
case Mode::SUSTAINED_PERFORMANCE:
break;
case Mode::FIXED_PERFORMANCE:
performanceBoost(enabled);
break;
case Mode::VR:
break;
case Mode::LAUNCH:
performanceBoost(enabled);
break;
case Mode::EXPENSIVE_RENDERING:
break;
case Mode::INTERACTIVE:
if (enabled) interactive();
break;
case Mode::DEVICE_IDLE:
powerSave(enabled);
break;
case Mode::DISPLAY_INACTIVE:
for (auto gpu : clusterList) {
if (gpu.getType() == ClusterType::GPU) {
gpu.setPowerSave(enabled);
}
}
break;
case Mode::AUDIO_STREAMING_LOW_LATENCY:
break;
case Mode::CAMERA_STREAMING_SECURE:
break;
case Mode::CAMERA_STREAMING_LOW:
break;
case Mode::CAMERA_STREAMING_MID:
break;
case Mode::CAMERA_STREAMING_HIGH:
break;
case Mode::GAME:
break;
case Mode::GAME_LOADING:
break;
default:
break;
}
return ScopedAStatus::ok();
}
ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
PW_LOG_DEBUG("Power setBoost: %s, duration: %d", toString(type).c_str(), durationMs);
getSupportedPlatform();
switch (type) {
// Touch screen
case Boost::INTERACTION:
case Boost::DISPLAY_UPDATE_IMMINENT:
case Boost::ML_ACC:
case Boost::AUDIO_LAUNCH:
case Boost::CAMERA_LAUNCH:
case Boost::CAMERA_SHOT:
default:
break;
}
return ScopedAStatus::ok();
}
/**
* _PLACEHOLDER_, DOUBLE_TAP_TO_WAKE, LOW_POWER, SUSTAINED_PERFORMANCE,
* FIXED_PERFORMANCE, VR, LAUNCH, EXPENSIVE_RENDERING,
* INTERACTIVE, DEVICE_IDLE, DISPLAY_INACTIVE, AUDIO_STREAMING_LOW_LATENCY,
* CAMERA_STREAMING_SECURE, CAMERA_STREAMING_LOW, CAMERA_STREAMING_MID, CAMERA_STREAMING_HIGH,
* GAME, GAME_LOADING
*/
ScopedAStatus Power::isModeSupported(Mode type, bool* _aidl_return) {
PW_LOG_DEBUG("Power isModeSupported: %d", static_cast<int32_t>(type));
getSupportedPlatform();
switch (type) {
case Mode::DOUBLE_TAP_TO_WAKE:
*_aidl_return = 0x4000 & _mode_support_int;
break;
case Mode::LOW_POWER:
*_aidl_return = 0x2000 & _mode_support_int;
break;
case Mode::SUSTAINED_PERFORMANCE:
*_aidl_return = 0x1000 & _mode_support_int;
break;
case Mode::FIXED_PERFORMANCE:
*_aidl_return = 0x0800 & _mode_support_int;
break;
case Mode::VR:
*_aidl_return = 0x0400 & _mode_support_int;
break;
case Mode::LAUNCH:
*_aidl_return = 0x0200 & _mode_support_int;
break;
case Mode::EXPENSIVE_RENDERING:
*_aidl_return = 0x0100 & _mode_support_int;
break;
case Mode::INTERACTIVE:
*_aidl_return = 0x0080 & _mode_support_int;
break;
case Mode::DEVICE_IDLE:
*_aidl_return = 0x0040 & _mode_support_int;
break;
case Mode::DISPLAY_INACTIVE:
*_aidl_return = 0x0020 & _mode_support_int;
break;
case Mode::AUDIO_STREAMING_LOW_LATENCY:
*_aidl_return = 0x0010 & _mode_support_int;
break;
case Mode::CAMERA_STREAMING_SECURE:
*_aidl_return = 0x0008 & _mode_support_int;
break;
case Mode::CAMERA_STREAMING_LOW:
*_aidl_return = 0x0004 & _mode_support_int;
break;
case Mode::CAMERA_STREAMING_MID:
*_aidl_return = 0x0002 & _mode_support_int;
break;
case Mode::CAMERA_STREAMING_HIGH:
*_aidl_return = 0x0001 & _mode_support_int;
break;
default:
*_aidl_return = false;
break;
}
return ScopedAStatus::ok();
}
/**
* Boost type defined from:
* hardware/interfaces/power/aidl/android/hardware/power/Boost.aidl
*
* platform : _PLACEHOLDER_, _PLACEHOLDER_, INTERACTION, DISPLAY_UPDATE_IMMINENT,
* ML_AAC, AUDIO_LAUNCH, CAMERA_LUNCH, CAMERA_SHOT
*
* rk3399 : 0x003F
* rk3326 : 0x003F
* ...
*/
ScopedAStatus Power::isBoostSupported(Boost type, bool* _aidl_return) {
PW_LOG_DEBUG("Power isBoostSupported: %d", static_cast<int32_t>(type));
getSupportedPlatform();
switch (type) {
// Touch screen
case Boost::INTERACTION:
*_aidl_return = 0x0020 & _boost_support_int;
break;
// Refresh screen
case Boost::DISPLAY_UPDATE_IMMINENT:
*_aidl_return = 0x0010 & _boost_support_int;
break;
// ML accelerator
case Boost::ML_ACC:
*_aidl_return = 0x0008 & _boost_support_int;
break;
case Boost::AUDIO_LAUNCH:
*_aidl_return = 0x0004 & _boost_support_int;
break;
case Boost::CAMERA_LAUNCH:
*_aidl_return = 0x0002 & _boost_support_int;
break;
case Boost::CAMERA_SHOT:
*_aidl_return = 0x0001 & _boost_support_int;
break;
default:
*_aidl_return = false;
break;
}
return ScopedAStatus::ok();
}
ScopedAStatus Power::createHintSession(int32_t, int32_t, const std::vector<int32_t>& tids, int64_t,
std::shared_ptr<IPowerHintSession>* _aidl_return) {
if (tids.size() == 0) {
*_aidl_return = nullptr;
return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
std::shared_ptr<IPowerHintSession> powerHintSession =
ndk::SharedRefBase::make<PowerHintSession>();
mPowerHintSessions.push_back(powerHintSession);
*_aidl_return = powerHintSession;
return ScopedAStatus::ok();
}
ScopedAStatus Power::getHintSessionPreferredRate(int64_t* outNanoseconds) {
*outNanoseconds = std::chrono::nanoseconds(1ms).count();
return ScopedAStatus::ok();
}
void Power::performanceBoost(bool on) {
if (!_boot_complete) {
PW_LOG_DEBUG("RK performance_boost skiped during boot!");
return;
}
if(property_get_bool("vendor.sculptor.enable", 0)) {
PW_LOG_DEBUG("RK performance_boost skiped during aipq mode!");
return;
}
if (!on) is_performance = 0;
if (is_performance == 0) {
PW_LOG_DEBUG("RK performance_boost Entered! on=%d",on);
for (auto cluster : clusterList) {
cluster.setPerformance(on);
}
if (on) is_performance = 1;
}
}
void Power::powerSave(bool on) {
PW_LOG_DEBUG("RK powersave Entered!");
for (auto cluster : clusterList) {
cluster.setPowerSave(on);
}
}
void Power::interactive() {
if (!_boot_complete) {
PW_LOG_DEBUG("RK interactive skiped during boot!");
return;
}
PW_LOG_DEBUG("RK interactive Entered!");
for (auto cluster : clusterList) {
cluster.setInteractive();
}
}
binder_status_t Power::dump(int fd, const char **args, uint32_t argc) {
kDebug = (argc == 1) && (std::string(args[0]) == "debug");
::android::base::WriteStringToFd("Power aidl dump:\n", fd);
::android::base::WriteStringToFd(::android::base::StringPrintf("Version: %s\n", VERSION), fd);
::android::base::WriteStringToFd(::android::base::StringPrintf("DEBUG: %s\n", (kDebug?"ON":"OFF")), fd);
::android::base::WriteStringToFd(::android::base::StringPrintf("Performance: %s\n", (is_performance?"ON":"OFF")), fd);
for (auto cluster : clusterList) {
::android::base::WriteStringToFd("*************************\n", fd);
::android::base::WriteStringToFd(cluster.toString(), fd);
::android::base::WriteStringToFd("*************************\n", fd);
}
return STATUS_OK;
}
} // namespace rockchip
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace android
} // namespace aidl