blob: b24251e78b6440d8d1c5c634a397b8dd16063498 [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
20#include <dirent.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <malloc.h>
24#include <stdbool.h>
25#include <string.h>
26#include <unistd.h>
27
28#include <sys/types.h>
29#include <sys/stat.h>
30
31#define LOG_TAG "SamsungPowerHAL"
32/* #define LOG_NDEBUG 0 */
33#include <utils/Log.h>
34
35#include <hardware/hardware.h>
36#include <hardware/power.h>
37
38#define BOOSTPULSE_PATH "/sys/devices/system/cpu/cpu0/cpufreq/interactive/boostpulse"
39
40#define IO_IS_BUSY_PATH "/sys/devices/system/cpu/cpu0/cpufreq/interactive/io_is_busy"
41
42#define CPU0_HISPEED_FREQ_PATH "/sys/devices/system/cpu/cpu0/cpufreq/interactive/hispeed_freq"
43#define CPU0_MAX_FREQ_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
44#define CPU4_HISPEED_FREQ_PATH "/sys/devices/system/cpu/cpu4/cpufreq/interactive/hispeed_freq"
45#define CPU4_MAX_FREQ_PATH "/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq"
46
47struct samsung_power_module {
48 struct power_module base;
49 pthread_mutex_t lock;
50 int boostpulse_fd;
51 int boostpulse_warned;
52 char cpu0_hispeed_freq[10];
53 char cpu0_max_freq[10];
54 char cpu4_hispeed_freq[10];
55 char cpu4_max_freq[10];
56 char* touchscreen_power_path;
57 char* touchkey_power_path;
58 bool touchkey_blocked;
59};
60
Andreas Schneiderf15d7f42016-02-03 10:43:47 +010061enum power_profile_e {
62 PROFILE_POWER_SAVE = 0,
63 PROFILE_BALANCED,
64 PROFILE_HIGH_PERFORMANCE
65};
66static enum power_profile_e current_power_profile = PROFILE_BALANCED;
Christopher N. Hessede5e3c62015-12-21 21:28:23 +010067
68/**********************************************************
69 *** HELPER FUNCTIONS
70 **********************************************************/
71
72static int sysfs_read(char *path, char *s, int num_bytes)
73{
74 char errno_str[64];
75 int len;
76 int ret = 0;
77 int fd;
78
79 fd = open(path, O_RDONLY);
80 if (fd < 0) {
81 strerror_r(errno, errno_str, sizeof(errno_str));
82 ALOGE("Error opening %s: %s\n", path, errno_str);
83
84 return -1;
85 }
86
87 len = read(fd, s, num_bytes - 1);
88 if (len < 0) {
89 strerror_r(errno, errno_str, sizeof(errno_str));
90 ALOGE("Error reading from %s: %s\n", path, errno_str);
91
92 ret = -1;
93 } else {
94 s[len] = '\0';
95 }
96
97 close(fd);
98
99 return ret;
100}
101
102static void sysfs_write(const char *path, char *s)
103{
104 char errno_str[64];
105 int len;
106 int fd;
107
108 fd = open(path, O_WRONLY);
109 if (fd < 0) {
110 strerror_r(errno, errno_str, sizeof(errno_str));
111 ALOGE("Error opening %s: %s\n", path, errno_str);
112 return;
113 }
114
115 len = write(fd, s, strlen(s));
116 if (len < 0) {
117 strerror_r(errno, errno_str, sizeof(errno_str));
118 ALOGE("Error writing to %s: %s\n", path, errno_str);
119 }
120
121 close(fd);
122}
123
124/**********************************************************
125 *** POWER FUNCTIONS
126 **********************************************************/
127
128/* You need to request the powerhal lock before calling this function */
129static int boostpulse_open(struct samsung_power_module *samsung_pwr)
130{
131 char errno_str[64];
132
133 if (samsung_pwr->boostpulse_fd < 0) {
134 samsung_pwr->boostpulse_fd = open(BOOSTPULSE_PATH, O_WRONLY);
135 if (samsung_pwr->boostpulse_fd < 0) {
136 if (!samsung_pwr->boostpulse_warned) {
137 strerror_r(errno, errno_str, sizeof(errno_str));
138 ALOGE("Error opening %s: %s\n", BOOSTPULSE_PATH, errno_str);
139 samsung_pwr->boostpulse_warned = 1;
140 }
141 }
142 }
143
144 return samsung_pwr->boostpulse_fd;
145}
146
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100147static void set_power_profile(struct samsung_power_module *samsung_pwr,
148 enum power_profile_e profile)
149{
150 int rc;
151 struct stat sb;
152
153 if (current_power_profile == profile) {
154 return;
155 }
156
157 ALOGV("%s: profile=%d", __func__, profile);
158
159 switch (profile) {
160 case PROFILE_POWER_SAVE:
161 // Limit to hispeed freq
162 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_hispeed_freq);
163 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
164 if (rc == 0) {
165 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_hispeed_freq);
166 }
167 ALOGD("%s: set powersave mode", __func__);
168 break;
169 case PROFILE_BALANCED:
170 // Restore normal max freq
171 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq);
172 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
173 if (rc == 0) {
174 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq);
175 }
176 ALOGD("%s: set balanced mode", __func__);
177 break;
178 case PROFILE_HIGH_PERFORMANCE:
179 // Restore normal max freq
180 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq);
181 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
182 if (rc == 0) {
183 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq);
184 }
185 ALOGD("%s: set performance mode", __func__);
186 break;
187 }
188
189 current_power_profile = profile;
190}
191
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100192static void find_input_nodes(struct samsung_power_module *samsung_pwr, char *dir)
193{
194 const char filename[] = "name";
195 char errno_str[64];
196 struct dirent *de;
197 char file_content[20];
198 char *path = NULL;
199 char *node_path = NULL;
200 size_t pathsize;
201 size_t node_pathsize;
202 DIR *d;
203
204 d = opendir(dir);
205 if (d == NULL) {
206 return;
207 }
208
209 while ((de = readdir(d)) != NULL) {
210 if (strncmp(filename, de->d_name, sizeof(filename)) == 0) {
211 pathsize = strlen(dir) + strlen(de->d_name) + 2;
212 node_pathsize = strlen(dir) + strlen("enabled") + 2;
213
214 path = malloc(pathsize);
215 node_path = malloc(node_pathsize);
216 if (path == NULL || node_path == NULL) {
217 strerror_r(errno, errno_str, sizeof(errno_str));
218 ALOGE("Out of memory: %s\n", errno_str);
219 return;
220 }
221
222 snprintf(path, pathsize, "%s/%s", dir, filename);
223 sysfs_read(path, file_content, sizeof(file_content));
224
225 snprintf(node_path, node_pathsize, "%s/%s", dir, "enabled");
226
227 if (strncmp(file_content, "sec_touchkey", 12) == 0) {
228 ALOGV("%s: found touchkey path: %s\n", __func__, node_path);
229 samsung_pwr->touchkey_power_path = malloc(node_pathsize);
230 if (samsung_pwr->touchkey_power_path == NULL) {
231 strerror_r(errno, errno_str, sizeof(errno_str));
232 ALOGE("Out of memory: %s\n", errno_str);
233 return;
234 }
Christopher N. Hesse22da3132016-02-01 12:36:54 +0100235 snprintf(samsung_pwr->touchkey_power_path, node_pathsize,
236 "%s", node_path);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100237 }
238
239 if (strncmp(file_content, "sec_touchscreen", 15) == 0) {
240 ALOGV("%s: found touchscreen path: %s\n", __func__, node_path);
241 samsung_pwr->touchscreen_power_path = malloc(node_pathsize);
242 if (samsung_pwr->touchscreen_power_path == NULL) {
243 strerror_r(errno, errno_str, sizeof(errno_str));
244 ALOGE("Out of memory: %s\n", errno_str);
245 return;
246 }
Christopher N. Hesse22da3132016-02-01 12:36:54 +0100247 snprintf(samsung_pwr->touchscreen_power_path, node_pathsize,
248 "%s", node_path);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100249 }
250 }
251 }
252
253 if (path)
254 free(path);
255 if (node_path)
256 free(node_path);
257 closedir(d);
258}
259
260/**********************************************************
261 *** INIT FUNCTIONS
262 **********************************************************/
263
264static void init_cpufreqs(struct samsung_power_module *samsung_pwr)
265{
266 int rc;
267 struct stat sb;
268
269 sysfs_read(CPU0_HISPEED_FREQ_PATH, samsung_pwr->cpu0_hispeed_freq,
270 sizeof(samsung_pwr->cpu0_hispeed_freq));
271 sysfs_read(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq,
272 sizeof(samsung_pwr->cpu0_max_freq));
273 ALOGV("%s: CPU 0 hispeed freq: %s\n", __func__, samsung_pwr->cpu0_hispeed_freq);
274 ALOGV("%s: CPU 0 max freq: %s\n", __func__, samsung_pwr->cpu0_max_freq);
275
276 rc = stat(CPU4_HISPEED_FREQ_PATH, &sb);
277 if (rc == 0) {
278 sysfs_read(CPU4_HISPEED_FREQ_PATH, samsung_pwr->cpu4_hispeed_freq,
279 sizeof(samsung_pwr->cpu4_hispeed_freq));
280 sysfs_read(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq,
281 sizeof(samsung_pwr->cpu4_max_freq));
282 ALOGV("%s: CPU 4 hispeed freq: %s\n", __func__, samsung_pwr->cpu4_hispeed_freq);
283 ALOGV("%s: CPU 4 max freq: %s\n", __func__, samsung_pwr->cpu4_max_freq);
284 }
285}
286
287static void init_touch_input_power_path(struct samsung_power_module *samsung_pwr)
288{
289 char dir[1024];
290 char errno_str[64];
291 uint32_t i;
292
293 for (i = 0; i < 20; i++) {
294 snprintf(dir, sizeof(dir), "/sys/class/input/input%d", i);
295 find_input_nodes(samsung_pwr, dir);
296 }
297}
298
299/*
300 * The init function performs power management setup actions at runtime
301 * startup, such as to set default cpufreq parameters. This is called only by
302 * the Power HAL instance loaded by PowerManagerService.
303 */
304static void samsung_power_init(struct power_module *module)
305{
306 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
307
308 init_cpufreqs(samsung_pwr);
309 init_touch_input_power_path(samsung_pwr);
310}
311
312/*
313 * The setInteractive function performs power management actions upon the
314 * system entering interactive state (that is, the system is awake and ready
315 * for interaction, often with UI devices such as display and touchscreen
316 * enabled) or non-interactive state (the system appears asleep, display
317 * usually turned off). The non-interactive state is usually entered after a
318 * period of inactivity, in order to conserve battery power during such
319 * inactive periods.
320 *
321 * Typical actions are to turn on or off devices and adjust cpufreq parameters.
322 * This function may also call the appropriate interfaces to allow the kernel
323 * to suspend the system to low-power sleep state when entering non-interactive
324 * state, and to disallow low-power suspend when the system is in interactive
325 * state. When low-power suspend state is allowed, the kernel may suspend the
326 * system whenever no wakelocks are held.
327 *
328 * on is non-zero when the system is transitioning to an interactive / awake
329 * state, and zero when transitioning to a non-interactive / asleep state.
330 *
331 * This function is called to enter non-interactive state after turning off the
332 * screen (if present), and called to enter interactive state prior to turning
333 * on the screen.
334 */
335static 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;
339 char buf[80];
340 char touchkey_node[2];
341 int touchkey_enabled;
342 int rc;
343
344 ALOGV("power_set_interactive: %d\n", on);
345
346 sysfs_write(samsung_pwr->touchscreen_power_path, on ? "1" : "0");
347
348 rc = stat(samsung_pwr->touchkey_power_path, &sb);
349 if (rc < 0) {
350 goto out;
351 }
352
353 if (!on) {
354 if (sysfs_read(samsung_pwr->touchkey_power_path, touchkey_node,
355 sizeof(touchkey_node)) == 0) {
356 touchkey_enabled = touchkey_node[0] - '0';
357 /*
358 * If touchkey_enabled is 0, the keys have been disabled by another component
359 * (for example cmhw), which means we don't want them to be enabled when resuming
360 * from suspend.
361 */
362 if (touchkey_enabled == 0) {
363 samsung_pwr->touchkey_blocked = true;
364 } else {
365 samsung_pwr->touchkey_blocked = false;
366 sysfs_write(samsung_pwr->touchkey_power_path, "0");
367 }
368 }
369 } else if (!samsung_pwr->touchkey_blocked) {
370 sysfs_write(samsung_pwr->touchkey_power_path, "1");
371 }
372
373 sysfs_write(IO_IS_BUSY_PATH, on ? "1" : "0");
374
375out:
376 ALOGV("power_set_interactive: %d done\n", on);
377}
378
379/*
380 * The powerHint function is called to pass hints on power requirements, which
381 * may result in adjustment of power/performance parameters of the cpufreq
382 * governor and other controls.
383 *
384 * The possible hints are:
385 *
386 * POWER_HINT_VSYNC
387 *
388 * Foreground app has started or stopped requesting a VSYNC pulse
389 * from SurfaceFlinger. If the app has started requesting VSYNC
390 * then CPU and GPU load is expected soon, and it may be appropriate
391 * to raise speeds of CPU, memory bus, etc. The data parameter is
392 * non-zero to indicate VSYNC pulse is now requested, or zero for
393 * VSYNC pulse no longer requested.
394 *
395 * POWER_HINT_INTERACTION
396 *
397 * User is interacting with the device, for example, touchscreen
398 * events are incoming. CPU and GPU load may be expected soon,
399 * and it may be appropriate to raise speeds of CPU, memory bus,
400 * etc. The data parameter is unused.
401 *
402 * POWER_HINT_LOW_POWER
403 *
404 * Low power mode is activated or deactivated. Low power mode
405 * is intended to save battery at the cost of performance. The data
406 * parameter is non-zero when low power mode is activated, and zero
407 * when deactivated.
408 *
409 * POWER_HINT_CPU_BOOST
410 *
411 * An operation is happening where it would be ideal for the CPU to
412 * be boosted for a specific duration. The data parameter is an
413 * integer value of the boost duration in microseconds.
414 */
415static void samsung_power_hint(struct power_module *module,
416 power_hint_t hint,
417 void *data)
418{
419 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
420 char errno_str[64];
421 int len;
422
423 switch (hint) {
424 case POWER_HINT_INTERACTION: {
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100425 char errno_str[64];
426 ssize_t len;
427 int fd;
428
429 if (current_power_profile == PROFILE_POWER_SAVE) {
430 return;
431 }
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100432
433 ALOGV("%s: POWER_HINT_INTERACTION", __func__);
434
435 if (boostpulse_open(samsung_pwr) >= 0) {
436 len = write(samsung_pwr->boostpulse_fd, "1", 1);
437
438 if (len < 0) {
439 strerror_r(errno, errno_str, sizeof(errno_str));
440 ALOGE("Error writing to %s: %s\n", BOOSTPULSE_PATH, errno_str);
441 }
442 }
443
444 break;
445 }
446 case POWER_HINT_VSYNC: {
447
448 ALOGV("%s: POWER_HINT_VSYNC", __func__);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100449 break;
450 }
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100451 case POWER_HINT_SET_PROFILE: {
452 int profile = *((intptr_t *)data);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100453
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100454 ALOGV("%s: POWER_HINT_SET_PROFILE", __func__);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100455
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100456 set_power_profile(samsung_pwr, profile);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100457 break;
458 }
459 default:
460 break;
461 }
462}
463
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100464static int samsung_get_feature(struct power_module *module __unused,
465 feature_t feature)
466{
467 if (feature == POWER_FEATURE_SUPPORTED_PROFILES) {
468 return 3;
469 }
470
471 return -1;
472}
473
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100474static struct hw_module_methods_t power_module_methods = {
475 .open = NULL,
476};
477
478struct samsung_power_module HAL_MODULE_INFO_SYM = {
479 .base = {
480 .common = {
481 .tag = HARDWARE_MODULE_TAG,
482 .module_api_version = POWER_MODULE_API_VERSION_0_2,
483 .hal_api_version = HARDWARE_HAL_API_VERSION,
484 .id = POWER_HARDWARE_MODULE_ID,
485 .name = "Samsung Power HAL",
486 .author = "The CyanogenMod Project",
487 .methods = &power_module_methods,
488 },
489
490 .init = samsung_power_init,
491 .setInteractive = samsung_power_set_interactive,
492 .powerHint = samsung_power_hint,
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100493 .getFeature = samsung_get_feature
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100494 },
495
496 .lock = PTHREAD_MUTEX_INITIALIZER,
497 .boostpulse_fd = -1,
498 .boostpulse_warned = 0,
499};