blob: b8418f1789ac54e8eea099225c48f1fe72b3e11b [file] [log] [blame]
Jan Altensene0dd3292020-02-01 06:21:33 +01001/*
2 * Copyright (C) 2020 The LineageOS 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 LOG_TAG "android.hardware.power@1.0-service.exynos"
18
19#include "Power.h"
20#include <android-base/logging.h>
21#include <filesystem>
22#include <fstream>
23#include <iostream>
24#include "samsung_lights.h"
25#include "samsung_power.h"
26
27namespace android {
28namespace hardware {
29namespace power {
30namespace V1_0 {
31namespace implementation {
32
33/*
34 * Write value to path and close file.
35 */
36template <typename T>
37static void set(const std::string& path, const T& value) {
38 std::ofstream file(path);
39 file << value << std::endl;
40}
41
42template <typename T>
43static T get(const std::string& path, const T& def) {
44 std::ifstream file(path);
45 T result;
46
47 file >> result;
48 return file.fail() ? def : result;
49}
50
51Return<void> Power::setInteractive(bool interactive) {
52 if (!initialized) {
53 initialize();
54 }
55
56 if (!interactive) {
57 int32_t panel_brightness = get(PANEL_BRIGHTNESS_NODE, -1);
58
59 if (panel_brightness > 0) {
60 LOG(VERBOSE) << "Moving to non-interactive state, but screen is still on,"
61 << "not disabling input devices";
Jan Altensen47c00d72020-03-27 16:40:28 +010062 goto out;
Jan Altensene0dd3292020-02-01 06:21:33 +010063 }
64 }
65
66 if (!sec_touchscreen.empty()) {
67 set(sec_touchscreen, interactive ? "1" : "0");
68 }
69
70 if (!sec_touchkey.empty()) {
71 if (!interactive) {
72 int button_state = get(sec_touchkey, -1);
73
74 if (button_state < 0) {
75 LOG(ERROR) << "Failed to read touchkey state";
76 goto out;
77 }
78
79 /*
80 * If button_state is 0, the keys have been disabled by another component
81 * (for example lineagehw), which means we don't want them to be enabled when resuming
82 * from suspend.
83 */
84 if (button_state == 0) {
85 touchkeys_blocked = true;
86 }
87 }
88
89 if (!touchkeys_blocked) {
90 set(sec_touchkey, interactive ? "1" : "0");
91 }
92 }
93
94out:
95 for (const std::string& interactivePath : cpuInteractivePaths) {
96 set(interactivePath + "/io_is_busy", interactive ? "1" : "0");
97 }
98
99 return Void();
100}
101
102Return<void> Power::powerHint(PowerHint hint, int32_t data) {
103 if (!initialized) {
104 initialize();
105 }
106
107 /* Bail out if low-power mode is active */
108 if (current_profile == PowerProfile::POWER_SAVE && hint != PowerHint::LOW_POWER &&
109 hint != static_cast<PowerHint>(LineagePowerHint::SET_PROFILE)) {
110 LOG(VERBOSE) << "PROFILE_POWER_SAVE active, ignoring hint " << static_cast<int32_t>(hint);
111 return Void();
112 }
113
114 switch (hint) {
115 case PowerHint::INTERACTION:
116 case PowerHint::LAUNCH:
117 sendBoostpulse();
118 break;
119 case PowerHint::LOW_POWER:
120 setProfile(data ? PowerProfile::POWER_SAVE : PowerProfile::BALANCED);
121 break;
122 default:
123 if (hint == static_cast<PowerHint>(LineagePowerHint::SET_PROFILE)) {
124 setProfile(static_cast<PowerProfile>(data));
125 } else if (hint == static_cast<PowerHint>(LineagePowerHint::CPU_BOOST)) {
126 sendBoost(data);
127 } else {
128 LOG(INFO) << "Unknown power hint: " << static_cast<int32_t>(hint);
129 }
130 break;
131 }
132 return Void();
133}
134
135Return<void> Power::setFeature(Feature feature __unused, bool activate __unused) {
136 if (!initialized) {
137 initialize();
138 }
139
140#ifdef TAP_TO_WAKE_NODE
141 if (feature == Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE) {
142 set(TAP_TO_WAKE_NODE, activate ? "1" : "0");
143 }
144#endif
145
146 return Void();
147}
148
149Return<void> Power::getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) {
150 _hidl_cb({}, Status::SUCCESS);
151 return Void();
152}
153
154Return<int32_t> Power::getFeature(LineageFeature feature) {
155 switch (feature) {
156 case LineageFeature::SUPPORTED_PROFILES:
157 return static_cast<int32_t>(PowerProfile::MAX);
158 default:
159 return -1;
160 }
161}
162
163void Power::initialize() {
164 findInputNodes();
165
166 current_profile = PowerProfile::BALANCED;
167
168 for (const std::string& interactivePath : cpuInteractivePaths) {
169 hispeed_freqs.emplace_back(get<std::string>(interactivePath + "/hispeed_freq", ""));
170 }
171
172 for (const std::string& sysfsPath : cpuSysfsPaths) {
173 max_freqs.emplace_back(get<std::string>(sysfsPath + "/cpufreq/scaling_max_freq", ""));
174 }
175
176 initialized = true;
177}
178
179void Power::findInputNodes() {
180 std::error_code ec;
181 for (auto& de : std::filesystem::directory_iterator("/sys/class/input/", ec)) {
182 /* we are only interested in the input devices that we can access */
183 if (ec || de.path().string().find("/sys/class/input/input") == std::string::npos) {
184 continue;
185 }
186
187 for (auto& de2 : std::filesystem::directory_iterator(de.path(), ec)) {
188 if (!ec && de2.path().string().find("/name") != std::string::npos) {
189 std::string content = get<std::string>(de2.path(), "");
190 if (content == "sec_touchkey") {
191 sec_touchkey = de.path().string().append("/enabled");
192 LOG(INFO) << "found sec_touchkey: " << sec_touchkey;
193 } else if (content == "sec_touchscreen") {
194 sec_touchscreen = de.path().string().append("/enabled");
195 LOG(INFO) << "found sec_touchscreen: " << sec_touchscreen;
196 }
197 }
198 }
199 }
200}
201
202void Power::setProfile(PowerProfile profile) {
203 if (current_profile == profile) {
204 return;
205 }
206
207 switch (profile) {
208 case PowerProfile::POWER_SAVE:
209 // Limit to hispeed freq
210 for (int i = 0; i < cpuSysfsPaths.size(); i++) {
211 if (hispeed_freqs.size() > i && !hispeed_freqs.at(i).empty()) {
212 set(cpuSysfsPaths.at(i) + "/cpufreq/scaling_max_freq", hispeed_freqs.at(i));
213 }
214 }
215 break;
216 case PowerProfile::BALANCED:
217 case PowerProfile::HIGH_PERFORMANCE:
218 // Restore normal max freq
219 for (int i = 0; i < cpuSysfsPaths.size(); i++) {
220 if (max_freqs.size() > i && !max_freqs.at(i).empty()) {
221 set(cpuSysfsPaths.at(i) + "/cpufreq/scaling_max_freq", max_freqs.at(i));
222 }
223 }
224 break;
225 default:
226 break;
227 }
228}
229
230void Power::sendBoostpulse() {
231 // the boostpulse node is only valid for the LITTLE cluster
232 set(cpuInteractivePaths.front() + "/boostpulse", "1");
233}
234
235void Power::sendBoost(int duration_us) {
236 set(cpuInteractivePaths.front() + "/boost", "1");
237
238 usleep(duration_us);
239
240 set(cpuInteractivePaths.front() + "/boost", "0");
241}
242
243} // namespace implementation
244} // namespace V1_0
245} // namespace power
246} // namespace hardware
247} // namespace android