blob: b4ddd675aebc262e8db2b6bca2fb447f3b5c72b6 [file] [log] [blame]
Christopher N. Hessede5e3c62015-12-21 21:28:23 +01001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 * Copyright (C) 2014 The CyanogenMod Project
4 * Copyright (C) 2014-2015 Andreas Schneider <asn@cryptomilk.org>
Christopher N. Hessef05f9022017-01-16 22:49:06 +01005 * Copyright (C) 2014-2017 Christopher N. Hesse <raymanfx@gmail.com>
Christopher N. Hessede5e3c62015-12-21 21:28:23 +01006 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
Christopher N. Hesse3360b092016-07-11 15:48:35 +020020#include <ctype.h>
Christopher N. Hessede5e3c62015-12-21 21:28:23 +010021#include <dirent.h>
22#include <errno.h>
23#include <fcntl.h>
24#include <malloc.h>
25#include <stdbool.h>
Christopher N. Hessee1434192016-11-18 18:56:17 +010026#include <stdlib.h>
Christopher N. Hessede5e3c62015-12-21 21:28:23 +010027#include <string.h>
28#include <unistd.h>
29
30#include <sys/types.h>
31#include <sys/stat.h>
32
33#define LOG_TAG "SamsungPowerHAL"
34/* #define LOG_NDEBUG 0 */
35#include <utils/Log.h>
36
37#include <hardware/hardware.h>
38#include <hardware/power.h>
Christopher N. Hesse12263502016-12-07 12:18:20 +010039#include <liblights/samsung_lights_helper.h>
Christopher N. Hessede5e3c62015-12-21 21:28:23 +010040
Christopher N. Hesse4139d852016-12-07 12:21:44 +010041#include "samsung_power.h"
Christopher N. Hessede5e3c62015-12-21 21:28:23 +010042
43struct samsung_power_module {
44 struct power_module base;
45 pthread_mutex_t lock;
46 int boostpulse_fd;
47 int boostpulse_warned;
48 char cpu0_hispeed_freq[10];
49 char cpu0_max_freq[10];
50 char cpu4_hispeed_freq[10];
51 char cpu4_max_freq[10];
52 char* touchscreen_power_path;
53 char* touchkey_power_path;
54 bool touchkey_blocked;
55};
56
Andreas Schneiderf15d7f42016-02-03 10:43:47 +010057enum power_profile_e {
58 PROFILE_POWER_SAVE = 0,
59 PROFILE_BALANCED,
Christopher N. Hesse58f2ca02017-01-17 00:01:15 +010060 PROFILE_HIGH_PERFORMANCE,
61 PROFILE_MAX
Andreas Schneiderf15d7f42016-02-03 10:43:47 +010062};
63static enum power_profile_e current_power_profile = PROFILE_BALANCED;
Christopher N. Hessede5e3c62015-12-21 21:28:23 +010064
65/**********************************************************
66 *** HELPER FUNCTIONS
67 **********************************************************/
68
69static int sysfs_read(char *path, char *s, int num_bytes)
70{
71 char errno_str[64];
72 int len;
73 int ret = 0;
74 int fd;
75
76 fd = open(path, O_RDONLY);
77 if (fd < 0) {
78 strerror_r(errno, errno_str, sizeof(errno_str));
79 ALOGE("Error opening %s: %s\n", path, errno_str);
80
81 return -1;
82 }
83
84 len = read(fd, s, num_bytes - 1);
85 if (len < 0) {
86 strerror_r(errno, errno_str, sizeof(errno_str));
87 ALOGE("Error reading from %s: %s\n", path, errno_str);
88
89 ret = -1;
90 } else {
91 s[len] = '\0';
92 }
93
94 close(fd);
95
96 return ret;
97}
98
99static void sysfs_write(const char *path, char *s)
100{
101 char errno_str[64];
102 int len;
103 int fd;
104
105 fd = open(path, O_WRONLY);
106 if (fd < 0) {
107 strerror_r(errno, errno_str, sizeof(errno_str));
108 ALOGE("Error opening %s: %s\n", path, errno_str);
109 return;
110 }
111
112 len = write(fd, s, strlen(s));
113 if (len < 0) {
114 strerror_r(errno, errno_str, sizeof(errno_str));
115 ALOGE("Error writing to %s: %s\n", path, errno_str);
116 }
117
118 close(fd);
119}
120
121/**********************************************************
122 *** POWER FUNCTIONS
123 **********************************************************/
124
125/* You need to request the powerhal lock before calling this function */
126static int boostpulse_open(struct samsung_power_module *samsung_pwr)
127{
128 char errno_str[64];
129
130 if (samsung_pwr->boostpulse_fd < 0) {
131 samsung_pwr->boostpulse_fd = open(BOOSTPULSE_PATH, O_WRONLY);
132 if (samsung_pwr->boostpulse_fd < 0) {
133 if (!samsung_pwr->boostpulse_warned) {
134 strerror_r(errno, errno_str, sizeof(errno_str));
135 ALOGE("Error opening %s: %s\n", BOOSTPULSE_PATH, errno_str);
136 samsung_pwr->boostpulse_warned = 1;
137 }
138 }
139 }
140
141 return samsung_pwr->boostpulse_fd;
142}
143
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100144static void set_power_profile(struct samsung_power_module *samsung_pwr,
Christopher N. Hesse58f2ca02017-01-17 00:01:15 +0100145 int profile)
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100146{
147 int rc;
148 struct stat sb;
149
Christopher N. Hesse58f2ca02017-01-17 00:01:15 +0100150 if (profile < 0 || profile >= PROFILE_MAX) {
151 return;
152 }
153
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100154 if (current_power_profile == profile) {
155 return;
156 }
157
158 ALOGV("%s: profile=%d", __func__, profile);
159
160 switch (profile) {
161 case PROFILE_POWER_SAVE:
162 // Limit to hispeed freq
163 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_hispeed_freq);
164 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
165 if (rc == 0) {
166 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_hispeed_freq);
167 }
Christopher N. Hessef05f9022017-01-16 22:49:06 +0100168 ALOGV("%s: set powersave mode", __func__);
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100169 break;
170 case PROFILE_BALANCED:
171 // Restore normal max freq
172 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq);
173 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
174 if (rc == 0) {
175 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq);
176 }
Christopher N. Hessef05f9022017-01-16 22:49:06 +0100177 ALOGV("%s: set balanced mode", __func__);
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100178 break;
179 case PROFILE_HIGH_PERFORMANCE:
180 // Restore normal max freq
181 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq);
182 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
183 if (rc == 0) {
184 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq);
185 }
Christopher N. Hessef05f9022017-01-16 22:49:06 +0100186 ALOGV("%s: set performance mode", __func__);
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100187 break;
188 }
189
190 current_power_profile = profile;
191}
192
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100193static void find_input_nodes(struct samsung_power_module *samsung_pwr, char *dir)
194{
195 const char filename[] = "name";
196 char errno_str[64];
197 struct dirent *de;
198 char file_content[20];
199 char *path = NULL;
200 char *node_path = NULL;
201 size_t pathsize;
202 size_t node_pathsize;
203 DIR *d;
204
205 d = opendir(dir);
206 if (d == NULL) {
207 return;
208 }
209
210 while ((de = readdir(d)) != NULL) {
211 if (strncmp(filename, de->d_name, sizeof(filename)) == 0) {
212 pathsize = strlen(dir) + strlen(de->d_name) + 2;
213 node_pathsize = strlen(dir) + strlen("enabled") + 2;
214
215 path = malloc(pathsize);
216 node_path = malloc(node_pathsize);
217 if (path == NULL || node_path == NULL) {
218 strerror_r(errno, errno_str, sizeof(errno_str));
219 ALOGE("Out of memory: %s\n", errno_str);
220 return;
221 }
222
223 snprintf(path, pathsize, "%s/%s", dir, filename);
224 sysfs_read(path, file_content, sizeof(file_content));
225
226 snprintf(node_path, node_pathsize, "%s/%s", dir, "enabled");
227
228 if (strncmp(file_content, "sec_touchkey", 12) == 0) {
229 ALOGV("%s: found touchkey path: %s\n", __func__, node_path);
230 samsung_pwr->touchkey_power_path = malloc(node_pathsize);
231 if (samsung_pwr->touchkey_power_path == NULL) {
232 strerror_r(errno, errno_str, sizeof(errno_str));
233 ALOGE("Out of memory: %s\n", errno_str);
234 return;
235 }
Christopher N. Hesse22da3132016-02-01 12:36:54 +0100236 snprintf(samsung_pwr->touchkey_power_path, node_pathsize,
237 "%s", node_path);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100238 }
239
240 if (strncmp(file_content, "sec_touchscreen", 15) == 0) {
241 ALOGV("%s: found touchscreen path: %s\n", __func__, node_path);
242 samsung_pwr->touchscreen_power_path = malloc(node_pathsize);
243 if (samsung_pwr->touchscreen_power_path == NULL) {
244 strerror_r(errno, errno_str, sizeof(errno_str));
245 ALOGE("Out of memory: %s\n", errno_str);
246 return;
247 }
Christopher N. Hesse22da3132016-02-01 12:36:54 +0100248 snprintf(samsung_pwr->touchscreen_power_path, node_pathsize,
249 "%s", node_path);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100250 }
251 }
252 }
253
254 if (path)
255 free(path);
256 if (node_path)
257 free(node_path);
258 closedir(d);
259}
260
261/**********************************************************
262 *** INIT FUNCTIONS
263 **********************************************************/
264
265static void init_cpufreqs(struct samsung_power_module *samsung_pwr)
266{
267 int rc;
268 struct stat sb;
269
270 sysfs_read(CPU0_HISPEED_FREQ_PATH, samsung_pwr->cpu0_hispeed_freq,
271 sizeof(samsung_pwr->cpu0_hispeed_freq));
272 sysfs_read(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq,
273 sizeof(samsung_pwr->cpu0_max_freq));
274 ALOGV("%s: CPU 0 hispeed freq: %s\n", __func__, samsung_pwr->cpu0_hispeed_freq);
275 ALOGV("%s: CPU 0 max freq: %s\n", __func__, samsung_pwr->cpu0_max_freq);
276
277 rc = stat(CPU4_HISPEED_FREQ_PATH, &sb);
278 if (rc == 0) {
279 sysfs_read(CPU4_HISPEED_FREQ_PATH, samsung_pwr->cpu4_hispeed_freq,
280 sizeof(samsung_pwr->cpu4_hispeed_freq));
281 sysfs_read(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq,
282 sizeof(samsung_pwr->cpu4_max_freq));
283 ALOGV("%s: CPU 4 hispeed freq: %s\n", __func__, samsung_pwr->cpu4_hispeed_freq);
284 ALOGV("%s: CPU 4 max freq: %s\n", __func__, samsung_pwr->cpu4_max_freq);
285 }
286}
287
288static void init_touch_input_power_path(struct samsung_power_module *samsung_pwr)
289{
290 char dir[1024];
291 char errno_str[64];
292 uint32_t i;
293
294 for (i = 0; i < 20; i++) {
295 snprintf(dir, sizeof(dir), "/sys/class/input/input%d", i);
296 find_input_nodes(samsung_pwr, dir);
297 }
298}
299
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100300static void samsung_power_init(struct power_module *module)
301{
302 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
303
304 init_cpufreqs(samsung_pwr);
305 init_touch_input_power_path(samsung_pwr);
306}
307
Christopher N. Hessef6296722017-01-16 22:47:11 +0100308/**********************************************************
309 *** API FUNCTIONS
310 ***
311 *** Refer to power.h for documentation.
312 **********************************************************/
313
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100314static void samsung_power_set_interactive(struct power_module *module, int on)
315{
316 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
317 struct stat sb;
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100318 char touchkey_node[2];
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100319 int rc;
320
321 ALOGV("power_set_interactive: %d\n", on);
322
Christopher N. Hesse12263502016-12-07 12:18:20 +0100323 // Get panel backlight brightness from lights HAL
Christopher N. Hesse3360b092016-07-11 15:48:35 +0200324 // Do not disable any input devices if the screen is on but we are in a non-interactive state
325 if (!on) {
Christopher N. Hesse12263502016-12-07 12:18:20 +0100326 if (get_cur_panel_brightness() > 0) {
Christopher N. Hesse3360b092016-07-11 15:48:35 +0200327 ALOGV("%s: Moving to non-interactive state, but screen is still on,"
328 " not disabling input devices\n", __func__);
329 goto out;
330 }
331 }
332
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100333 sysfs_write(samsung_pwr->touchscreen_power_path, on ? "1" : "0");
334
335 rc = stat(samsung_pwr->touchkey_power_path, &sb);
336 if (rc < 0) {
337 goto out;
338 }
339
340 if (!on) {
341 if (sysfs_read(samsung_pwr->touchkey_power_path, touchkey_node,
342 sizeof(touchkey_node)) == 0) {
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100343 /*
Christopher N. Hesse3360b092016-07-11 15:48:35 +0200344 * If touchkey_node is 0, the keys have been disabled by another component
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100345 * (for example cmhw), which means we don't want them to be enabled when resuming
346 * from suspend.
347 */
Christopher N. Hessed9285742017-01-16 23:59:20 +0100348 if (touchkey_node[0] == '0') {
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100349 samsung_pwr->touchkey_blocked = true;
350 } else {
351 samsung_pwr->touchkey_blocked = false;
352 sysfs_write(samsung_pwr->touchkey_power_path, "0");
353 }
354 }
355 } else if (!samsung_pwr->touchkey_blocked) {
356 sysfs_write(samsung_pwr->touchkey_power_path, "1");
357 }
358
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100359out:
Christopher N. Hesse2879c692016-07-11 15:49:49 +0200360 sysfs_write(IO_IS_BUSY_PATH, on ? "1" : "0");
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100361 ALOGV("power_set_interactive: %d done\n", on);
362}
363
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100364static void samsung_power_hint(struct power_module *module,
365 power_hint_t hint,
366 void *data)
367{
368 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
369 char errno_str[64];
370 int len;
371
372 switch (hint) {
373 case POWER_HINT_INTERACTION: {
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100374 if (current_power_profile == PROFILE_POWER_SAVE) {
375 return;
376 }
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100377
378 ALOGV("%s: POWER_HINT_INTERACTION", __func__);
379
380 if (boostpulse_open(samsung_pwr) >= 0) {
381 len = write(samsung_pwr->boostpulse_fd, "1", 1);
382
383 if (len < 0) {
384 strerror_r(errno, errno_str, sizeof(errno_str));
385 ALOGE("Error writing to %s: %s\n", BOOSTPULSE_PATH, errno_str);
386 }
387 }
388
389 break;
390 }
391 case POWER_HINT_VSYNC: {
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100392 ALOGV("%s: POWER_HINT_VSYNC", __func__);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100393 break;
394 }
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100395 case POWER_HINT_SET_PROFILE: {
396 int profile = *((intptr_t *)data);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100397
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100398 ALOGV("%s: POWER_HINT_SET_PROFILE", __func__);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100399
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100400 set_power_profile(samsung_pwr, profile);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100401 break;
402 }
403 default:
404 break;
405 }
406}
407
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100408static int samsung_get_feature(struct power_module *module __unused,
409 feature_t feature)
410{
411 if (feature == POWER_FEATURE_SUPPORTED_PROFILES) {
Christopher N. Hesse58f2ca02017-01-17 00:01:15 +0100412 return PROFILE_MAX;
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100413 }
414
415 return -1;
416}
417
Christopher N. Hesse1c474662016-11-18 18:59:06 +0100418static void samsung_set_feature(struct power_module *module, feature_t feature, int state __unused)
Christopher N. Hessee480d892016-06-22 23:04:39 +0200419{
420 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
421
422 switch (feature) {
ishantvivek987dcca2016-11-21 06:05:47 +0000423#ifdef TARGET_TAP_TO_WAKE_NODE
Christopher N. Hessee480d892016-06-22 23:04:39 +0200424 case POWER_FEATURE_DOUBLE_TAP_TO_WAKE:
425 ALOGV("%s: %s double tap to wake", __func__, state ? "enabling" : "disabling");
ishantvivek987dcca2016-11-21 06:05:47 +0000426 sysfs_write(TARGET_TAP_TO_WAKE_NODE, state > 0 ? "1" : "0");
Christopher N. Hessee480d892016-06-22 23:04:39 +0200427 break;
428#endif
429 default:
430 break;
431 }
432}
433
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100434static struct hw_module_methods_t power_module_methods = {
435 .open = NULL,
436};
437
438struct samsung_power_module HAL_MODULE_INFO_SYM = {
439 .base = {
440 .common = {
441 .tag = HARDWARE_MODULE_TAG,
442 .module_api_version = POWER_MODULE_API_VERSION_0_2,
443 .hal_api_version = HARDWARE_HAL_API_VERSION,
444 .id = POWER_HARDWARE_MODULE_ID,
445 .name = "Samsung Power HAL",
Christopher N. Hesseaa75be42017-01-16 22:47:59 +0100446 .author = "The LineageOS Project",
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100447 .methods = &power_module_methods,
448 },
449
450 .init = samsung_power_init,
451 .setInteractive = samsung_power_set_interactive,
452 .powerHint = samsung_power_hint,
Christopher N. Hessee480d892016-06-22 23:04:39 +0200453 .getFeature = samsung_get_feature,
454 .setFeature = samsung_set_feature
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100455 },
456
457 .lock = PTHREAD_MUTEX_INITIALIZER,
458 .boostpulse_fd = -1,
459 .boostpulse_warned = 0,
460};