blob: 5856e1f33a14e436c81d5d64a4a63ac62943d71d [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;
Christopher N. Hessede5e3c62015-12-21 21:28:23 +010047 char cpu0_hispeed_freq[10];
48 char cpu0_max_freq[10];
49 char cpu4_hispeed_freq[10];
50 char cpu4_max_freq[10];
51 char* touchscreen_power_path;
52 char* touchkey_power_path;
53 bool touchkey_blocked;
54};
55
Andreas Schneiderf15d7f42016-02-03 10:43:47 +010056enum power_profile_e {
57 PROFILE_POWER_SAVE = 0,
58 PROFILE_BALANCED,
Christopher N. Hesse58f2ca02017-01-17 00:01:15 +010059 PROFILE_HIGH_PERFORMANCE,
60 PROFILE_MAX
Andreas Schneiderf15d7f42016-02-03 10:43:47 +010061};
Christopher N. Hesseccd970d2017-01-25 22:41:05 +010062
Andreas Schneiderf15d7f42016-02-03 10:43:47 +010063static enum power_profile_e current_power_profile = PROFILE_BALANCED;
Christopher N. Hesseccd970d2017-01-25 22:41:05 +010064static bool boostpulse_warned = false;
Christopher N. Hessede5e3c62015-12-21 21:28:23 +010065
66/**********************************************************
67 *** HELPER FUNCTIONS
68 **********************************************************/
69
70static int sysfs_read(char *path, char *s, int num_bytes)
71{
72 char errno_str[64];
73 int len;
74 int ret = 0;
75 int fd;
76
77 fd = open(path, O_RDONLY);
78 if (fd < 0) {
79 strerror_r(errno, errno_str, sizeof(errno_str));
80 ALOGE("Error opening %s: %s\n", path, errno_str);
81
82 return -1;
83 }
84
85 len = read(fd, s, num_bytes - 1);
86 if (len < 0) {
87 strerror_r(errno, errno_str, sizeof(errno_str));
88 ALOGE("Error reading from %s: %s\n", path, errno_str);
89
90 ret = -1;
91 } else {
92 s[len] = '\0';
93 }
94
95 close(fd);
96
97 return ret;
98}
99
100static void sysfs_write(const char *path, char *s)
101{
102 char errno_str[64];
103 int len;
104 int fd;
105
106 fd = open(path, O_WRONLY);
107 if (fd < 0) {
108 strerror_r(errno, errno_str, sizeof(errno_str));
109 ALOGE("Error opening %s: %s\n", path, errno_str);
110 return;
111 }
112
113 len = write(fd, s, strlen(s));
114 if (len < 0) {
115 strerror_r(errno, errno_str, sizeof(errno_str));
116 ALOGE("Error writing to %s: %s\n", path, errno_str);
117 }
118
119 close(fd);
120}
121
Christopher N. Hesse5fada9b2017-01-16 23:39:48 +0100122static void boost(int32_t duration_us)
123{
124 int fd;
125
126 if (duration_us <= 0)
127 return;
128
129 fd = open(BOOST_PATH, O_WRONLY);
130 if (fd < 0) {
131 ALOGE("Error opening %s\n", BOOST_PATH);
132 return;
133 }
134
135 write(fd, "1", 1);
136 usleep(duration_us);
137 write(fd, "0", 1);
138
139 close(fd);
140}
141
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100142/**********************************************************
143 *** POWER FUNCTIONS
144 **********************************************************/
145
146/* You need to request the powerhal lock before calling this function */
147static int boostpulse_open(struct samsung_power_module *samsung_pwr)
148{
149 char errno_str[64];
150
151 if (samsung_pwr->boostpulse_fd < 0) {
152 samsung_pwr->boostpulse_fd = open(BOOSTPULSE_PATH, O_WRONLY);
153 if (samsung_pwr->boostpulse_fd < 0) {
Christopher N. Hesseccd970d2017-01-25 22:41:05 +0100154 if (!boostpulse_warned) {
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100155 strerror_r(errno, errno_str, sizeof(errno_str));
156 ALOGE("Error opening %s: %s\n", BOOSTPULSE_PATH, errno_str);
Christopher N. Hesseccd970d2017-01-25 22:41:05 +0100157 boostpulse_warned = true;
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100158 }
159 }
160 }
161
162 return samsung_pwr->boostpulse_fd;
163}
164
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100165static void set_power_profile(struct samsung_power_module *samsung_pwr,
Christopher N. Hesse58f2ca02017-01-17 00:01:15 +0100166 int profile)
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100167{
168 int rc;
169 struct stat sb;
170
Christopher N. Hesse58f2ca02017-01-17 00:01:15 +0100171 if (profile < 0 || profile >= PROFILE_MAX) {
172 return;
173 }
174
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100175 if (current_power_profile == profile) {
176 return;
177 }
178
179 ALOGV("%s: profile=%d", __func__, profile);
180
181 switch (profile) {
182 case PROFILE_POWER_SAVE:
183 // Limit to hispeed freq
184 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_hispeed_freq);
185 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
186 if (rc == 0) {
187 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_hispeed_freq);
188 }
Christopher N. Hessef05f9022017-01-16 22:49:06 +0100189 ALOGV("%s: set powersave mode", __func__);
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100190 break;
191 case PROFILE_BALANCED:
192 // Restore normal max freq
193 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq);
194 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
195 if (rc == 0) {
196 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq);
197 }
Christopher N. Hessef05f9022017-01-16 22:49:06 +0100198 ALOGV("%s: set balanced mode", __func__);
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100199 break;
200 case PROFILE_HIGH_PERFORMANCE:
201 // Restore normal max freq
202 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq);
203 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
204 if (rc == 0) {
205 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq);
206 }
Christopher N. Hessef05f9022017-01-16 22:49:06 +0100207 ALOGV("%s: set performance mode", __func__);
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100208 break;
209 }
210
211 current_power_profile = profile;
212}
213
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100214static void find_input_nodes(struct samsung_power_module *samsung_pwr, char *dir)
215{
216 const char filename[] = "name";
217 char errno_str[64];
218 struct dirent *de;
219 char file_content[20];
220 char *path = NULL;
221 char *node_path = NULL;
222 size_t pathsize;
223 size_t node_pathsize;
224 DIR *d;
225
226 d = opendir(dir);
227 if (d == NULL) {
228 return;
229 }
230
231 while ((de = readdir(d)) != NULL) {
232 if (strncmp(filename, de->d_name, sizeof(filename)) == 0) {
233 pathsize = strlen(dir) + strlen(de->d_name) + 2;
234 node_pathsize = strlen(dir) + strlen("enabled") + 2;
235
236 path = malloc(pathsize);
237 node_path = malloc(node_pathsize);
238 if (path == NULL || node_path == NULL) {
239 strerror_r(errno, errno_str, sizeof(errno_str));
240 ALOGE("Out of memory: %s\n", errno_str);
241 return;
242 }
243
244 snprintf(path, pathsize, "%s/%s", dir, filename);
245 sysfs_read(path, file_content, sizeof(file_content));
246
247 snprintf(node_path, node_pathsize, "%s/%s", dir, "enabled");
248
249 if (strncmp(file_content, "sec_touchkey", 12) == 0) {
250 ALOGV("%s: found touchkey path: %s\n", __func__, node_path);
251 samsung_pwr->touchkey_power_path = malloc(node_pathsize);
252 if (samsung_pwr->touchkey_power_path == NULL) {
253 strerror_r(errno, errno_str, sizeof(errno_str));
254 ALOGE("Out of memory: %s\n", errno_str);
255 return;
256 }
Christopher N. Hesse22da3132016-02-01 12:36:54 +0100257 snprintf(samsung_pwr->touchkey_power_path, node_pathsize,
258 "%s", node_path);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100259 }
260
261 if (strncmp(file_content, "sec_touchscreen", 15) == 0) {
262 ALOGV("%s: found touchscreen path: %s\n", __func__, node_path);
263 samsung_pwr->touchscreen_power_path = malloc(node_pathsize);
264 if (samsung_pwr->touchscreen_power_path == NULL) {
265 strerror_r(errno, errno_str, sizeof(errno_str));
266 ALOGE("Out of memory: %s\n", errno_str);
267 return;
268 }
Christopher N. Hesse22da3132016-02-01 12:36:54 +0100269 snprintf(samsung_pwr->touchscreen_power_path, node_pathsize,
270 "%s", node_path);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100271 }
272 }
273 }
274
275 if (path)
276 free(path);
277 if (node_path)
278 free(node_path);
279 closedir(d);
280}
281
282/**********************************************************
283 *** INIT FUNCTIONS
284 **********************************************************/
285
286static void init_cpufreqs(struct samsung_power_module *samsung_pwr)
287{
288 int rc;
289 struct stat sb;
290
291 sysfs_read(CPU0_HISPEED_FREQ_PATH, samsung_pwr->cpu0_hispeed_freq,
292 sizeof(samsung_pwr->cpu0_hispeed_freq));
293 sysfs_read(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq,
294 sizeof(samsung_pwr->cpu0_max_freq));
295 ALOGV("%s: CPU 0 hispeed freq: %s\n", __func__, samsung_pwr->cpu0_hispeed_freq);
296 ALOGV("%s: CPU 0 max freq: %s\n", __func__, samsung_pwr->cpu0_max_freq);
297
298 rc = stat(CPU4_HISPEED_FREQ_PATH, &sb);
299 if (rc == 0) {
300 sysfs_read(CPU4_HISPEED_FREQ_PATH, samsung_pwr->cpu4_hispeed_freq,
301 sizeof(samsung_pwr->cpu4_hispeed_freq));
302 sysfs_read(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq,
303 sizeof(samsung_pwr->cpu4_max_freq));
304 ALOGV("%s: CPU 4 hispeed freq: %s\n", __func__, samsung_pwr->cpu4_hispeed_freq);
305 ALOGV("%s: CPU 4 max freq: %s\n", __func__, samsung_pwr->cpu4_max_freq);
306 }
307}
308
309static void init_touch_input_power_path(struct samsung_power_module *samsung_pwr)
310{
311 char dir[1024];
312 char errno_str[64];
313 uint32_t i;
314
315 for (i = 0; i < 20; i++) {
316 snprintf(dir, sizeof(dir), "/sys/class/input/input%d", i);
317 find_input_nodes(samsung_pwr, dir);
318 }
319}
320
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100321static void samsung_power_init(struct power_module *module)
322{
323 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
324
325 init_cpufreqs(samsung_pwr);
326 init_touch_input_power_path(samsung_pwr);
327}
328
Christopher N. Hessef6296722017-01-16 22:47:11 +0100329/**********************************************************
330 *** API FUNCTIONS
331 ***
332 *** Refer to power.h for documentation.
333 **********************************************************/
334
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100335static void samsung_power_set_interactive(struct power_module *module, int on)
336{
337 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
338 struct stat sb;
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100339 char touchkey_node[2];
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100340 int rc;
341
342 ALOGV("power_set_interactive: %d\n", on);
343
Christopher N. Hesse12263502016-12-07 12:18:20 +0100344 // Get panel backlight brightness from lights HAL
Christopher N. Hesse3360b092016-07-11 15:48:35 +0200345 // Do not disable any input devices if the screen is on but we are in a non-interactive state
346 if (!on) {
Christopher N. Hesse12263502016-12-07 12:18:20 +0100347 if (get_cur_panel_brightness() > 0) {
Christopher N. Hesse3360b092016-07-11 15:48:35 +0200348 ALOGV("%s: Moving to non-interactive state, but screen is still on,"
349 " not disabling input devices\n", __func__);
350 goto out;
351 }
352 }
353
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100354 sysfs_write(samsung_pwr->touchscreen_power_path, on ? "1" : "0");
355
356 rc = stat(samsung_pwr->touchkey_power_path, &sb);
357 if (rc < 0) {
358 goto out;
359 }
360
361 if (!on) {
362 if (sysfs_read(samsung_pwr->touchkey_power_path, touchkey_node,
363 sizeof(touchkey_node)) == 0) {
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100364 /*
Christopher N. Hesse3360b092016-07-11 15:48:35 +0200365 * If touchkey_node is 0, the keys have been disabled by another component
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100366 * (for example cmhw), which means we don't want them to be enabled when resuming
367 * from suspend.
368 */
Christopher N. Hessed9285742017-01-16 23:59:20 +0100369 if (touchkey_node[0] == '0') {
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100370 samsung_pwr->touchkey_blocked = true;
371 } else {
372 samsung_pwr->touchkey_blocked = false;
373 sysfs_write(samsung_pwr->touchkey_power_path, "0");
374 }
375 }
376 } else if (!samsung_pwr->touchkey_blocked) {
377 sysfs_write(samsung_pwr->touchkey_power_path, "1");
378 }
379
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100380out:
Christopher N. Hesse2879c692016-07-11 15:49:49 +0200381 sysfs_write(IO_IS_BUSY_PATH, on ? "1" : "0");
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100382 ALOGV("power_set_interactive: %d done\n", on);
383}
384
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100385static void samsung_power_hint(struct power_module *module,
386 power_hint_t hint,
387 void *data)
388{
389 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
390 char errno_str[64];
391 int len;
392
393 switch (hint) {
394 case POWER_HINT_INTERACTION: {
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100395 if (current_power_profile == PROFILE_POWER_SAVE) {
396 return;
397 }
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100398
399 ALOGV("%s: POWER_HINT_INTERACTION", __func__);
400
401 if (boostpulse_open(samsung_pwr) >= 0) {
402 len = write(samsung_pwr->boostpulse_fd, "1", 1);
403
404 if (len < 0) {
405 strerror_r(errno, errno_str, sizeof(errno_str));
406 ALOGE("Error writing to %s: %s\n", BOOSTPULSE_PATH, errno_str);
407 }
408 }
409
410 break;
411 }
412 case POWER_HINT_VSYNC: {
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100413 ALOGV("%s: POWER_HINT_VSYNC", __func__);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100414 break;
415 }
Christopher N. Hesse5fada9b2017-01-16 23:39:48 +0100416#ifdef POWER_HINT_CPU_BOOST
417 case POWER_HINT_CPU_BOOST:
418 boost((*(int32_t *)data));
419 break;
420#endif
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100421 case POWER_HINT_SET_PROFILE: {
422 int profile = *((intptr_t *)data);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100423
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100424 ALOGV("%s: POWER_HINT_SET_PROFILE", __func__);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100425
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100426 set_power_profile(samsung_pwr, profile);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100427 break;
428 }
429 default:
430 break;
431 }
432}
433
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100434static int samsung_get_feature(struct power_module *module __unused,
435 feature_t feature)
436{
437 if (feature == POWER_FEATURE_SUPPORTED_PROFILES) {
Christopher N. Hesse58f2ca02017-01-17 00:01:15 +0100438 return PROFILE_MAX;
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100439 }
440
441 return -1;
442}
443
Christopher N. Hesse1c474662016-11-18 18:59:06 +0100444static void samsung_set_feature(struct power_module *module, feature_t feature, int state __unused)
Christopher N. Hessee480d892016-06-22 23:04:39 +0200445{
446 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
447
448 switch (feature) {
ishantvivek987dcca2016-11-21 06:05:47 +0000449#ifdef TARGET_TAP_TO_WAKE_NODE
Christopher N. Hessee480d892016-06-22 23:04:39 +0200450 case POWER_FEATURE_DOUBLE_TAP_TO_WAKE:
451 ALOGV("%s: %s double tap to wake", __func__, state ? "enabling" : "disabling");
ishantvivek987dcca2016-11-21 06:05:47 +0000452 sysfs_write(TARGET_TAP_TO_WAKE_NODE, state > 0 ? "1" : "0");
Christopher N. Hessee480d892016-06-22 23:04:39 +0200453 break;
454#endif
455 default:
456 break;
457 }
458}
459
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100460static struct hw_module_methods_t power_module_methods = {
461 .open = NULL,
462};
463
464struct samsung_power_module HAL_MODULE_INFO_SYM = {
465 .base = {
466 .common = {
467 .tag = HARDWARE_MODULE_TAG,
468 .module_api_version = POWER_MODULE_API_VERSION_0_2,
469 .hal_api_version = HARDWARE_HAL_API_VERSION,
470 .id = POWER_HARDWARE_MODULE_ID,
471 .name = "Samsung Power HAL",
Christopher N. Hesseaa75be42017-01-16 22:47:59 +0100472 .author = "The LineageOS Project",
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100473 .methods = &power_module_methods,
474 },
475
476 .init = samsung_power_init,
477 .setInteractive = samsung_power_set_interactive,
478 .powerHint = samsung_power_hint,
Christopher N. Hessee480d892016-06-22 23:04:39 +0200479 .getFeature = samsung_get_feature,
480 .setFeature = samsung_set_feature
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100481 },
482
483 .lock = PTHREAD_MUTEX_INITIALIZER,
484 .boostpulse_fd = -1,
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100485};