blob: 6a24a8eb6648761faa5002eaa24a4e9c1e4d6122 [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>
5 * Copyright (C) 2014-2015 Christopher N. Hesse <raymanfx@gmail.com>
6 *
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,
60 PROFILE_HIGH_PERFORMANCE
61};
62static enum power_profile_e current_power_profile = PROFILE_BALANCED;
Christopher N. Hessede5e3c62015-12-21 21:28:23 +010063
64/**********************************************************
65 *** HELPER FUNCTIONS
66 **********************************************************/
67
68static int sysfs_read(char *path, char *s, int num_bytes)
69{
70 char errno_str[64];
71 int len;
72 int ret = 0;
73 int fd;
74
75 fd = open(path, O_RDONLY);
76 if (fd < 0) {
77 strerror_r(errno, errno_str, sizeof(errno_str));
78 ALOGE("Error opening %s: %s\n", path, errno_str);
79
80 return -1;
81 }
82
83 len = read(fd, s, num_bytes - 1);
84 if (len < 0) {
85 strerror_r(errno, errno_str, sizeof(errno_str));
86 ALOGE("Error reading from %s: %s\n", path, errno_str);
87
88 ret = -1;
89 } else {
90 s[len] = '\0';
91 }
92
93 close(fd);
94
95 return ret;
96}
97
98static void sysfs_write(const char *path, char *s)
99{
100 char errno_str[64];
101 int len;
102 int fd;
103
104 fd = open(path, O_WRONLY);
105 if (fd < 0) {
106 strerror_r(errno, errno_str, sizeof(errno_str));
107 ALOGE("Error opening %s: %s\n", path, errno_str);
108 return;
109 }
110
111 len = write(fd, s, strlen(s));
112 if (len < 0) {
113 strerror_r(errno, errno_str, sizeof(errno_str));
114 ALOGE("Error writing to %s: %s\n", path, errno_str);
115 }
116
117 close(fd);
118}
119
120/**********************************************************
121 *** POWER FUNCTIONS
122 **********************************************************/
123
124/* You need to request the powerhal lock before calling this function */
125static int boostpulse_open(struct samsung_power_module *samsung_pwr)
126{
127 char errno_str[64];
128
129 if (samsung_pwr->boostpulse_fd < 0) {
130 samsung_pwr->boostpulse_fd = open(BOOSTPULSE_PATH, O_WRONLY);
131 if (samsung_pwr->boostpulse_fd < 0) {
132 if (!samsung_pwr->boostpulse_warned) {
133 strerror_r(errno, errno_str, sizeof(errno_str));
134 ALOGE("Error opening %s: %s\n", BOOSTPULSE_PATH, errno_str);
135 samsung_pwr->boostpulse_warned = 1;
136 }
137 }
138 }
139
140 return samsung_pwr->boostpulse_fd;
141}
142
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100143static void set_power_profile(struct samsung_power_module *samsung_pwr,
144 enum power_profile_e profile)
145{
146 int rc;
147 struct stat sb;
148
149 if (current_power_profile == profile) {
150 return;
151 }
152
153 ALOGV("%s: profile=%d", __func__, profile);
154
155 switch (profile) {
156 case PROFILE_POWER_SAVE:
157 // Limit to hispeed freq
158 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_hispeed_freq);
159 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
160 if (rc == 0) {
161 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_hispeed_freq);
162 }
163 ALOGD("%s: set powersave mode", __func__);
164 break;
165 case PROFILE_BALANCED:
166 // Restore normal max freq
167 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq);
168 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
169 if (rc == 0) {
170 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq);
171 }
172 ALOGD("%s: set balanced mode", __func__);
173 break;
174 case PROFILE_HIGH_PERFORMANCE:
175 // Restore normal max freq
176 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq);
177 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
178 if (rc == 0) {
179 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq);
180 }
181 ALOGD("%s: set performance mode", __func__);
182 break;
183 }
184
185 current_power_profile = profile;
186}
187
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100188static void find_input_nodes(struct samsung_power_module *samsung_pwr, char *dir)
189{
190 const char filename[] = "name";
191 char errno_str[64];
192 struct dirent *de;
193 char file_content[20];
194 char *path = NULL;
195 char *node_path = NULL;
196 size_t pathsize;
197 size_t node_pathsize;
198 DIR *d;
199
200 d = opendir(dir);
201 if (d == NULL) {
202 return;
203 }
204
205 while ((de = readdir(d)) != NULL) {
206 if (strncmp(filename, de->d_name, sizeof(filename)) == 0) {
207 pathsize = strlen(dir) + strlen(de->d_name) + 2;
208 node_pathsize = strlen(dir) + strlen("enabled") + 2;
209
210 path = malloc(pathsize);
211 node_path = malloc(node_pathsize);
212 if (path == NULL || node_path == NULL) {
213 strerror_r(errno, errno_str, sizeof(errno_str));
214 ALOGE("Out of memory: %s\n", errno_str);
215 return;
216 }
217
218 snprintf(path, pathsize, "%s/%s", dir, filename);
219 sysfs_read(path, file_content, sizeof(file_content));
220
221 snprintf(node_path, node_pathsize, "%s/%s", dir, "enabled");
222
223 if (strncmp(file_content, "sec_touchkey", 12) == 0) {
224 ALOGV("%s: found touchkey path: %s\n", __func__, node_path);
225 samsung_pwr->touchkey_power_path = malloc(node_pathsize);
226 if (samsung_pwr->touchkey_power_path == NULL) {
227 strerror_r(errno, errno_str, sizeof(errno_str));
228 ALOGE("Out of memory: %s\n", errno_str);
229 return;
230 }
Christopher N. Hesse22da3132016-02-01 12:36:54 +0100231 snprintf(samsung_pwr->touchkey_power_path, node_pathsize,
232 "%s", node_path);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100233 }
234
235 if (strncmp(file_content, "sec_touchscreen", 15) == 0) {
236 ALOGV("%s: found touchscreen path: %s\n", __func__, node_path);
237 samsung_pwr->touchscreen_power_path = malloc(node_pathsize);
238 if (samsung_pwr->touchscreen_power_path == NULL) {
239 strerror_r(errno, errno_str, sizeof(errno_str));
240 ALOGE("Out of memory: %s\n", errno_str);
241 return;
242 }
Christopher N. Hesse22da3132016-02-01 12:36:54 +0100243 snprintf(samsung_pwr->touchscreen_power_path, node_pathsize,
244 "%s", node_path);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100245 }
246 }
247 }
248
249 if (path)
250 free(path);
251 if (node_path)
252 free(node_path);
253 closedir(d);
254}
255
256/**********************************************************
257 *** INIT FUNCTIONS
258 **********************************************************/
259
260static void init_cpufreqs(struct samsung_power_module *samsung_pwr)
261{
262 int rc;
263 struct stat sb;
264
265 sysfs_read(CPU0_HISPEED_FREQ_PATH, samsung_pwr->cpu0_hispeed_freq,
266 sizeof(samsung_pwr->cpu0_hispeed_freq));
267 sysfs_read(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq,
268 sizeof(samsung_pwr->cpu0_max_freq));
269 ALOGV("%s: CPU 0 hispeed freq: %s\n", __func__, samsung_pwr->cpu0_hispeed_freq);
270 ALOGV("%s: CPU 0 max freq: %s\n", __func__, samsung_pwr->cpu0_max_freq);
271
272 rc = stat(CPU4_HISPEED_FREQ_PATH, &sb);
273 if (rc == 0) {
274 sysfs_read(CPU4_HISPEED_FREQ_PATH, samsung_pwr->cpu4_hispeed_freq,
275 sizeof(samsung_pwr->cpu4_hispeed_freq));
276 sysfs_read(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq,
277 sizeof(samsung_pwr->cpu4_max_freq));
278 ALOGV("%s: CPU 4 hispeed freq: %s\n", __func__, samsung_pwr->cpu4_hispeed_freq);
279 ALOGV("%s: CPU 4 max freq: %s\n", __func__, samsung_pwr->cpu4_max_freq);
280 }
281}
282
283static void init_touch_input_power_path(struct samsung_power_module *samsung_pwr)
284{
285 char dir[1024];
286 char errno_str[64];
287 uint32_t i;
288
289 for (i = 0; i < 20; i++) {
290 snprintf(dir, sizeof(dir), "/sys/class/input/input%d", i);
291 find_input_nodes(samsung_pwr, dir);
292 }
293}
294
295/*
296 * The init function performs power management setup actions at runtime
297 * startup, such as to set default cpufreq parameters. This is called only by
298 * the Power HAL instance loaded by PowerManagerService.
299 */
300static 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
308/*
309 * The setInteractive function performs power management actions upon the
310 * system entering interactive state (that is, the system is awake and ready
311 * for interaction, often with UI devices such as display and touchscreen
312 * enabled) or non-interactive state (the system appears asleep, display
313 * usually turned off). The non-interactive state is usually entered after a
314 * period of inactivity, in order to conserve battery power during such
315 * inactive periods.
316 *
317 * Typical actions are to turn on or off devices and adjust cpufreq parameters.
318 * This function may also call the appropriate interfaces to allow the kernel
319 * to suspend the system to low-power sleep state when entering non-interactive
320 * state, and to disallow low-power suspend when the system is in interactive
321 * state. When low-power suspend state is allowed, the kernel may suspend the
322 * system whenever no wakelocks are held.
323 *
324 * on is non-zero when the system is transitioning to an interactive / awake
325 * state, and zero when transitioning to a non-interactive / asleep state.
326 *
327 * This function is called to enter non-interactive state after turning off the
328 * screen (if present), and called to enter interactive state prior to turning
329 * on the screen.
330 */
331static void samsung_power_set_interactive(struct power_module *module, int on)
332{
333 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
334 struct stat sb;
335 char buf[80];
336 char touchkey_node[2];
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100337 int rc;
338
339 ALOGV("power_set_interactive: %d\n", on);
340
Christopher N. Hesse12263502016-12-07 12:18:20 +0100341 // Get panel backlight brightness from lights HAL
Christopher N. Hesse3360b092016-07-11 15:48:35 +0200342 // Do not disable any input devices if the screen is on but we are in a non-interactive state
343 if (!on) {
Christopher N. Hesse12263502016-12-07 12:18:20 +0100344 if (get_cur_panel_brightness() > 0) {
Christopher N. Hesse3360b092016-07-11 15:48:35 +0200345 ALOGV("%s: Moving to non-interactive state, but screen is still on,"
346 " not disabling input devices\n", __func__);
347 goto out;
348 }
349 }
350
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100351 sysfs_write(samsung_pwr->touchscreen_power_path, on ? "1" : "0");
352
353 rc = stat(samsung_pwr->touchkey_power_path, &sb);
354 if (rc < 0) {
355 goto out;
356 }
357
358 if (!on) {
359 if (sysfs_read(samsung_pwr->touchkey_power_path, touchkey_node,
360 sizeof(touchkey_node)) == 0) {
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100361 /*
Christopher N. Hesse3360b092016-07-11 15:48:35 +0200362 * If touchkey_node is 0, the keys have been disabled by another component
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100363 * (for example cmhw), which means we don't want them to be enabled when resuming
364 * from suspend.
365 */
Christopher N. Hesse3360b092016-07-11 15:48:35 +0200366 if ((touchkey_node[0] - '0') == 0) {
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100367 samsung_pwr->touchkey_blocked = true;
368 } else {
369 samsung_pwr->touchkey_blocked = false;
370 sysfs_write(samsung_pwr->touchkey_power_path, "0");
371 }
372 }
373 } else if (!samsung_pwr->touchkey_blocked) {
374 sysfs_write(samsung_pwr->touchkey_power_path, "1");
375 }
376
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100377out:
Christopher N. Hesse2879c692016-07-11 15:49:49 +0200378 sysfs_write(IO_IS_BUSY_PATH, on ? "1" : "0");
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100379 ALOGV("power_set_interactive: %d done\n", on);
380}
381
382/*
383 * The powerHint function is called to pass hints on power requirements, which
384 * may result in adjustment of power/performance parameters of the cpufreq
385 * governor and other controls.
386 *
387 * The possible hints are:
388 *
389 * POWER_HINT_VSYNC
390 *
391 * Foreground app has started or stopped requesting a VSYNC pulse
392 * from SurfaceFlinger. If the app has started requesting VSYNC
393 * then CPU and GPU load is expected soon, and it may be appropriate
394 * to raise speeds of CPU, memory bus, etc. The data parameter is
395 * non-zero to indicate VSYNC pulse is now requested, or zero for
396 * VSYNC pulse no longer requested.
397 *
398 * POWER_HINT_INTERACTION
399 *
400 * User is interacting with the device, for example, touchscreen
401 * events are incoming. CPU and GPU load may be expected soon,
402 * and it may be appropriate to raise speeds of CPU, memory bus,
403 * etc. The data parameter is unused.
404 *
405 * POWER_HINT_LOW_POWER
406 *
407 * Low power mode is activated or deactivated. Low power mode
408 * is intended to save battery at the cost of performance. The data
409 * parameter is non-zero when low power mode is activated, and zero
410 * when deactivated.
411 *
412 * POWER_HINT_CPU_BOOST
413 *
414 * An operation is happening where it would be ideal for the CPU to
415 * be boosted for a specific duration. The data parameter is an
416 * integer value of the boost duration in microseconds.
417 */
418static void samsung_power_hint(struct power_module *module,
419 power_hint_t hint,
420 void *data)
421{
422 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
423 char errno_str[64];
424 int len;
425
426 switch (hint) {
427 case POWER_HINT_INTERACTION: {
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100428 char errno_str[64];
429 ssize_t len;
430 int fd;
431
432 if (current_power_profile == PROFILE_POWER_SAVE) {
433 return;
434 }
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100435
436 ALOGV("%s: POWER_HINT_INTERACTION", __func__);
437
438 if (boostpulse_open(samsung_pwr) >= 0) {
439 len = write(samsung_pwr->boostpulse_fd, "1", 1);
440
441 if (len < 0) {
442 strerror_r(errno, errno_str, sizeof(errno_str));
443 ALOGE("Error writing to %s: %s\n", BOOSTPULSE_PATH, errno_str);
444 }
445 }
446
447 break;
448 }
449 case POWER_HINT_VSYNC: {
450
451 ALOGV("%s: POWER_HINT_VSYNC", __func__);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100452 break;
453 }
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100454 case POWER_HINT_SET_PROFILE: {
455 int profile = *((intptr_t *)data);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100456
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100457 ALOGV("%s: POWER_HINT_SET_PROFILE", __func__);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100458
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100459 set_power_profile(samsung_pwr, profile);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100460 break;
461 }
462 default:
463 break;
464 }
465}
466
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100467static int samsung_get_feature(struct power_module *module __unused,
468 feature_t feature)
469{
470 if (feature == POWER_FEATURE_SUPPORTED_PROFILES) {
471 return 3;
472 }
473
474 return -1;
475}
476
Christopher N. Hesse1c474662016-11-18 18:59:06 +0100477static void samsung_set_feature(struct power_module *module, feature_t feature, int state __unused)
Christopher N. Hessee480d892016-06-22 23:04:39 +0200478{
479 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
480
481 switch (feature) {
ishantvivek987dcca2016-11-21 06:05:47 +0000482#ifdef TARGET_TAP_TO_WAKE_NODE
Christopher N. Hessee480d892016-06-22 23:04:39 +0200483 case POWER_FEATURE_DOUBLE_TAP_TO_WAKE:
484 ALOGV("%s: %s double tap to wake", __func__, state ? "enabling" : "disabling");
ishantvivek987dcca2016-11-21 06:05:47 +0000485 sysfs_write(TARGET_TAP_TO_WAKE_NODE, state > 0 ? "1" : "0");
Christopher N. Hessee480d892016-06-22 23:04:39 +0200486 break;
487#endif
488 default:
489 break;
490 }
491}
492
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100493static struct hw_module_methods_t power_module_methods = {
494 .open = NULL,
495};
496
497struct samsung_power_module HAL_MODULE_INFO_SYM = {
498 .base = {
499 .common = {
500 .tag = HARDWARE_MODULE_TAG,
501 .module_api_version = POWER_MODULE_API_VERSION_0_2,
502 .hal_api_version = HARDWARE_HAL_API_VERSION,
503 .id = POWER_HARDWARE_MODULE_ID,
504 .name = "Samsung Power HAL",
505 .author = "The CyanogenMod Project",
506 .methods = &power_module_methods,
507 },
508
509 .init = samsung_power_init,
510 .setInteractive = samsung_power_set_interactive,
511 .powerHint = samsung_power_hint,
Christopher N. Hessee480d892016-06-22 23:04:39 +0200512 .getFeature = samsung_get_feature,
513 .setFeature = samsung_set_feature
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100514 },
515
516 .lock = PTHREAD_MUTEX_INITIALIZER,
517 .boostpulse_fd = -1,
518 .boostpulse_warned = 0,
519};