blob: 5a0b116a95ea1882b28ec8f03e06b377af95b019 [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
61/* POWER_HINT_LOW_POWER */
62static bool low_power_mode = false;
63
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
143static void find_input_nodes(struct samsung_power_module *samsung_pwr, char *dir)
144{
145 const char filename[] = "name";
146 char errno_str[64];
147 struct dirent *de;
148 char file_content[20];
149 char *path = NULL;
150 char *node_path = NULL;
151 size_t pathsize;
152 size_t node_pathsize;
153 DIR *d;
154
155 d = opendir(dir);
156 if (d == NULL) {
157 return;
158 }
159
160 while ((de = readdir(d)) != NULL) {
161 if (strncmp(filename, de->d_name, sizeof(filename)) == 0) {
162 pathsize = strlen(dir) + strlen(de->d_name) + 2;
163 node_pathsize = strlen(dir) + strlen("enabled") + 2;
164
165 path = malloc(pathsize);
166 node_path = malloc(node_pathsize);
167 if (path == NULL || node_path == NULL) {
168 strerror_r(errno, errno_str, sizeof(errno_str));
169 ALOGE("Out of memory: %s\n", errno_str);
170 return;
171 }
172
173 snprintf(path, pathsize, "%s/%s", dir, filename);
174 sysfs_read(path, file_content, sizeof(file_content));
175
176 snprintf(node_path, node_pathsize, "%s/%s", dir, "enabled");
177
178 if (strncmp(file_content, "sec_touchkey", 12) == 0) {
179 ALOGV("%s: found touchkey path: %s\n", __func__, node_path);
180 samsung_pwr->touchkey_power_path = malloc(node_pathsize);
181 if (samsung_pwr->touchkey_power_path == NULL) {
182 strerror_r(errno, errno_str, sizeof(errno_str));
183 ALOGE("Out of memory: %s\n", errno_str);
184 return;
185 }
Christopher N. Hesse22da3132016-02-01 12:36:54 +0100186 snprintf(samsung_pwr->touchkey_power_path, node_pathsize,
187 "%s", node_path);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100188 }
189
190 if (strncmp(file_content, "sec_touchscreen", 15) == 0) {
191 ALOGV("%s: found touchscreen path: %s\n", __func__, node_path);
192 samsung_pwr->touchscreen_power_path = malloc(node_pathsize);
193 if (samsung_pwr->touchscreen_power_path == NULL) {
194 strerror_r(errno, errno_str, sizeof(errno_str));
195 ALOGE("Out of memory: %s\n", errno_str);
196 return;
197 }
Christopher N. Hesse22da3132016-02-01 12:36:54 +0100198 snprintf(samsung_pwr->touchscreen_power_path, node_pathsize,
199 "%s", node_path);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100200 }
201 }
202 }
203
204 if (path)
205 free(path);
206 if (node_path)
207 free(node_path);
208 closedir(d);
209}
210
211/**********************************************************
212 *** INIT FUNCTIONS
213 **********************************************************/
214
215static void init_cpufreqs(struct samsung_power_module *samsung_pwr)
216{
217 int rc;
218 struct stat sb;
219
220 sysfs_read(CPU0_HISPEED_FREQ_PATH, samsung_pwr->cpu0_hispeed_freq,
221 sizeof(samsung_pwr->cpu0_hispeed_freq));
222 sysfs_read(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq,
223 sizeof(samsung_pwr->cpu0_max_freq));
224 ALOGV("%s: CPU 0 hispeed freq: %s\n", __func__, samsung_pwr->cpu0_hispeed_freq);
225 ALOGV("%s: CPU 0 max freq: %s\n", __func__, samsung_pwr->cpu0_max_freq);
226
227 rc = stat(CPU4_HISPEED_FREQ_PATH, &sb);
228 if (rc == 0) {
229 sysfs_read(CPU4_HISPEED_FREQ_PATH, samsung_pwr->cpu4_hispeed_freq,
230 sizeof(samsung_pwr->cpu4_hispeed_freq));
231 sysfs_read(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq,
232 sizeof(samsung_pwr->cpu4_max_freq));
233 ALOGV("%s: CPU 4 hispeed freq: %s\n", __func__, samsung_pwr->cpu4_hispeed_freq);
234 ALOGV("%s: CPU 4 max freq: %s\n", __func__, samsung_pwr->cpu4_max_freq);
235 }
236}
237
238static void init_touch_input_power_path(struct samsung_power_module *samsung_pwr)
239{
240 char dir[1024];
241 char errno_str[64];
242 uint32_t i;
243
244 for (i = 0; i < 20; i++) {
245 snprintf(dir, sizeof(dir), "/sys/class/input/input%d", i);
246 find_input_nodes(samsung_pwr, dir);
247 }
248}
249
250/*
251 * The init function performs power management setup actions at runtime
252 * startup, such as to set default cpufreq parameters. This is called only by
253 * the Power HAL instance loaded by PowerManagerService.
254 */
255static void samsung_power_init(struct power_module *module)
256{
257 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
258
259 init_cpufreqs(samsung_pwr);
260 init_touch_input_power_path(samsung_pwr);
261}
262
263/*
264 * The setInteractive function performs power management actions upon the
265 * system entering interactive state (that is, the system is awake and ready
266 * for interaction, often with UI devices such as display and touchscreen
267 * enabled) or non-interactive state (the system appears asleep, display
268 * usually turned off). The non-interactive state is usually entered after a
269 * period of inactivity, in order to conserve battery power during such
270 * inactive periods.
271 *
272 * Typical actions are to turn on or off devices and adjust cpufreq parameters.
273 * This function may also call the appropriate interfaces to allow the kernel
274 * to suspend the system to low-power sleep state when entering non-interactive
275 * state, and to disallow low-power suspend when the system is in interactive
276 * state. When low-power suspend state is allowed, the kernel may suspend the
277 * system whenever no wakelocks are held.
278 *
279 * on is non-zero when the system is transitioning to an interactive / awake
280 * state, and zero when transitioning to a non-interactive / asleep state.
281 *
282 * This function is called to enter non-interactive state after turning off the
283 * screen (if present), and called to enter interactive state prior to turning
284 * on the screen.
285 */
286static void samsung_power_set_interactive(struct power_module *module, int on)
287{
288 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
289 struct stat sb;
290 char buf[80];
291 char touchkey_node[2];
292 int touchkey_enabled;
293 int rc;
294
295 ALOGV("power_set_interactive: %d\n", on);
296
297 sysfs_write(samsung_pwr->touchscreen_power_path, on ? "1" : "0");
298
299 rc = stat(samsung_pwr->touchkey_power_path, &sb);
300 if (rc < 0) {
301 goto out;
302 }
303
304 if (!on) {
305 if (sysfs_read(samsung_pwr->touchkey_power_path, touchkey_node,
306 sizeof(touchkey_node)) == 0) {
307 touchkey_enabled = touchkey_node[0] - '0';
308 /*
309 * If touchkey_enabled is 0, the keys have been disabled by another component
310 * (for example cmhw), which means we don't want them to be enabled when resuming
311 * from suspend.
312 */
313 if (touchkey_enabled == 0) {
314 samsung_pwr->touchkey_blocked = true;
315 } else {
316 samsung_pwr->touchkey_blocked = false;
317 sysfs_write(samsung_pwr->touchkey_power_path, "0");
318 }
319 }
320 } else if (!samsung_pwr->touchkey_blocked) {
321 sysfs_write(samsung_pwr->touchkey_power_path, "1");
322 }
323
324 sysfs_write(IO_IS_BUSY_PATH, on ? "1" : "0");
325
326out:
327 ALOGV("power_set_interactive: %d done\n", on);
328}
329
330/*
331 * The powerHint function is called to pass hints on power requirements, which
332 * may result in adjustment of power/performance parameters of the cpufreq
333 * governor and other controls.
334 *
335 * The possible hints are:
336 *
337 * POWER_HINT_VSYNC
338 *
339 * Foreground app has started or stopped requesting a VSYNC pulse
340 * from SurfaceFlinger. If the app has started requesting VSYNC
341 * then CPU and GPU load is expected soon, and it may be appropriate
342 * to raise speeds of CPU, memory bus, etc. The data parameter is
343 * non-zero to indicate VSYNC pulse is now requested, or zero for
344 * VSYNC pulse no longer requested.
345 *
346 * POWER_HINT_INTERACTION
347 *
348 * User is interacting with the device, for example, touchscreen
349 * events are incoming. CPU and GPU load may be expected soon,
350 * and it may be appropriate to raise speeds of CPU, memory bus,
351 * etc. The data parameter is unused.
352 *
353 * POWER_HINT_LOW_POWER
354 *
355 * Low power mode is activated or deactivated. Low power mode
356 * is intended to save battery at the cost of performance. The data
357 * parameter is non-zero when low power mode is activated, and zero
358 * when deactivated.
359 *
360 * POWER_HINT_CPU_BOOST
361 *
362 * An operation is happening where it would be ideal for the CPU to
363 * be boosted for a specific duration. The data parameter is an
364 * integer value of the boost duration in microseconds.
365 */
366static void samsung_power_hint(struct power_module *module,
367 power_hint_t hint,
368 void *data)
369{
370 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
371 char errno_str[64];
372 int len;
373
374 switch (hint) {
375 case POWER_HINT_INTERACTION: {
376
377 ALOGV("%s: POWER_HINT_INTERACTION", __func__);
378
379 if (boostpulse_open(samsung_pwr) >= 0) {
380 len = write(samsung_pwr->boostpulse_fd, "1", 1);
381
382 if (len < 0) {
383 strerror_r(errno, errno_str, sizeof(errno_str));
384 ALOGE("Error writing to %s: %s\n", BOOSTPULSE_PATH, errno_str);
385 }
386 }
387
388 break;
389 }
390 case POWER_HINT_VSYNC: {
391
392 ALOGV("%s: POWER_HINT_VSYNC", __func__);
393
394 break;
395 }
396 case POWER_HINT_LOW_POWER: {
397 int rc;
398 struct stat sb;
399
400 ALOGV("%s: POWER_HINT_LOW_POWER", __func__);
401
402 pthread_mutex_lock(&samsung_pwr->lock);
403
404 /*
405 * TODO: We fail to restore the max freqs after low power mode has been
406 * disabled for some reason (big.LITTLE specific issue?)
407 *
408 if (data) {
409 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_hispeed_freq);
410 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
411 if (rc == 0) {
412 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_hispeed_freq);
413 }
414 } else {
415 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq);
416 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
417 if (rc == 0) {
418 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq);
419 }
420 }
421 */
422 low_power_mode = data;
423
424 pthread_mutex_unlock(&samsung_pwr->lock);
425 break;
426 }
427 default:
428 break;
429 }
430}
431
432static struct hw_module_methods_t power_module_methods = {
433 .open = NULL,
434};
435
436struct samsung_power_module HAL_MODULE_INFO_SYM = {
437 .base = {
438 .common = {
439 .tag = HARDWARE_MODULE_TAG,
440 .module_api_version = POWER_MODULE_API_VERSION_0_2,
441 .hal_api_version = HARDWARE_HAL_API_VERSION,
442 .id = POWER_HARDWARE_MODULE_ID,
443 .name = "Samsung Power HAL",
444 .author = "The CyanogenMod Project",
445 .methods = &power_module_methods,
446 },
447
448 .init = samsung_power_init,
449 .setInteractive = samsung_power_set_interactive,
450 .powerHint = samsung_power_hint,
451 },
452
453 .lock = PTHREAD_MUTEX_INITIALIZER,
454 .boostpulse_fd = -1,
455 .boostpulse_warned = 0,
456};