blob: d75b4057857fed96a63df4f87c9a6f0a0efb9ba4 [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
122/**********************************************************
123 *** POWER FUNCTIONS
124 **********************************************************/
125
126/* You need to request the powerhal lock before calling this function */
127static int boostpulse_open(struct samsung_power_module *samsung_pwr)
128{
129 char errno_str[64];
130
131 if (samsung_pwr->boostpulse_fd < 0) {
132 samsung_pwr->boostpulse_fd = open(BOOSTPULSE_PATH, O_WRONLY);
133 if (samsung_pwr->boostpulse_fd < 0) {
Christopher N. Hesseccd970d2017-01-25 22:41:05 +0100134 if (!boostpulse_warned) {
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100135 strerror_r(errno, errno_str, sizeof(errno_str));
136 ALOGE("Error opening %s: %s\n", BOOSTPULSE_PATH, errno_str);
Christopher N. Hesseccd970d2017-01-25 22:41:05 +0100137 boostpulse_warned = true;
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100138 }
139 }
140 }
141
142 return samsung_pwr->boostpulse_fd;
143}
144
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100145static void set_power_profile(struct samsung_power_module *samsung_pwr,
Christopher N. Hesse58f2ca02017-01-17 00:01:15 +0100146 int profile)
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100147{
148 int rc;
149 struct stat sb;
150
Christopher N. Hesse58f2ca02017-01-17 00:01:15 +0100151 if (profile < 0 || profile >= PROFILE_MAX) {
152 return;
153 }
154
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100155 if (current_power_profile == profile) {
156 return;
157 }
158
159 ALOGV("%s: profile=%d", __func__, profile);
160
161 switch (profile) {
162 case PROFILE_POWER_SAVE:
163 // Limit to hispeed freq
164 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_hispeed_freq);
165 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
166 if (rc == 0) {
167 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_hispeed_freq);
168 }
Christopher N. Hessef05f9022017-01-16 22:49:06 +0100169 ALOGV("%s: set powersave mode", __func__);
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100170 break;
171 case PROFILE_BALANCED:
172 // Restore normal max freq
173 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq);
174 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
175 if (rc == 0) {
176 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq);
177 }
Christopher N. Hessef05f9022017-01-16 22:49:06 +0100178 ALOGV("%s: set balanced mode", __func__);
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100179 break;
180 case PROFILE_HIGH_PERFORMANCE:
181 // Restore normal max freq
182 sysfs_write(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq);
183 rc = stat(CPU4_MAX_FREQ_PATH, &sb);
184 if (rc == 0) {
185 sysfs_write(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq);
186 }
Christopher N. Hessef05f9022017-01-16 22:49:06 +0100187 ALOGV("%s: set performance mode", __func__);
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100188 break;
189 }
190
191 current_power_profile = profile;
192}
193
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100194static void find_input_nodes(struct samsung_power_module *samsung_pwr, char *dir)
195{
196 const char filename[] = "name";
197 char errno_str[64];
198 struct dirent *de;
199 char file_content[20];
200 char *path = NULL;
201 char *node_path = NULL;
202 size_t pathsize;
203 size_t node_pathsize;
204 DIR *d;
205
206 d = opendir(dir);
207 if (d == NULL) {
208 return;
209 }
210
211 while ((de = readdir(d)) != NULL) {
212 if (strncmp(filename, de->d_name, sizeof(filename)) == 0) {
213 pathsize = strlen(dir) + strlen(de->d_name) + 2;
214 node_pathsize = strlen(dir) + strlen("enabled") + 2;
215
216 path = malloc(pathsize);
217 node_path = malloc(node_pathsize);
218 if (path == NULL || node_path == NULL) {
219 strerror_r(errno, errno_str, sizeof(errno_str));
220 ALOGE("Out of memory: %s\n", errno_str);
221 return;
222 }
223
224 snprintf(path, pathsize, "%s/%s", dir, filename);
225 sysfs_read(path, file_content, sizeof(file_content));
226
227 snprintf(node_path, node_pathsize, "%s/%s", dir, "enabled");
228
229 if (strncmp(file_content, "sec_touchkey", 12) == 0) {
230 ALOGV("%s: found touchkey path: %s\n", __func__, node_path);
231 samsung_pwr->touchkey_power_path = malloc(node_pathsize);
232 if (samsung_pwr->touchkey_power_path == NULL) {
233 strerror_r(errno, errno_str, sizeof(errno_str));
234 ALOGE("Out of memory: %s\n", errno_str);
235 return;
236 }
Christopher N. Hesse22da3132016-02-01 12:36:54 +0100237 snprintf(samsung_pwr->touchkey_power_path, node_pathsize,
238 "%s", node_path);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100239 }
240
241 if (strncmp(file_content, "sec_touchscreen", 15) == 0) {
242 ALOGV("%s: found touchscreen path: %s\n", __func__, node_path);
243 samsung_pwr->touchscreen_power_path = malloc(node_pathsize);
244 if (samsung_pwr->touchscreen_power_path == NULL) {
245 strerror_r(errno, errno_str, sizeof(errno_str));
246 ALOGE("Out of memory: %s\n", errno_str);
247 return;
248 }
Christopher N. Hesse22da3132016-02-01 12:36:54 +0100249 snprintf(samsung_pwr->touchscreen_power_path, node_pathsize,
250 "%s", node_path);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100251 }
252 }
253 }
254
255 if (path)
256 free(path);
257 if (node_path)
258 free(node_path);
259 closedir(d);
260}
261
262/**********************************************************
263 *** INIT FUNCTIONS
264 **********************************************************/
265
266static void init_cpufreqs(struct samsung_power_module *samsung_pwr)
267{
268 int rc;
269 struct stat sb;
270
271 sysfs_read(CPU0_HISPEED_FREQ_PATH, samsung_pwr->cpu0_hispeed_freq,
272 sizeof(samsung_pwr->cpu0_hispeed_freq));
273 sysfs_read(CPU0_MAX_FREQ_PATH, samsung_pwr->cpu0_max_freq,
274 sizeof(samsung_pwr->cpu0_max_freq));
275 ALOGV("%s: CPU 0 hispeed freq: %s\n", __func__, samsung_pwr->cpu0_hispeed_freq);
276 ALOGV("%s: CPU 0 max freq: %s\n", __func__, samsung_pwr->cpu0_max_freq);
277
278 rc = stat(CPU4_HISPEED_FREQ_PATH, &sb);
279 if (rc == 0) {
280 sysfs_read(CPU4_HISPEED_FREQ_PATH, samsung_pwr->cpu4_hispeed_freq,
281 sizeof(samsung_pwr->cpu4_hispeed_freq));
282 sysfs_read(CPU4_MAX_FREQ_PATH, samsung_pwr->cpu4_max_freq,
283 sizeof(samsung_pwr->cpu4_max_freq));
284 ALOGV("%s: CPU 4 hispeed freq: %s\n", __func__, samsung_pwr->cpu4_hispeed_freq);
285 ALOGV("%s: CPU 4 max freq: %s\n", __func__, samsung_pwr->cpu4_max_freq);
286 }
287}
288
289static void init_touch_input_power_path(struct samsung_power_module *samsung_pwr)
290{
291 char dir[1024];
292 char errno_str[64];
293 uint32_t i;
294
295 for (i = 0; i < 20; i++) {
296 snprintf(dir, sizeof(dir), "/sys/class/input/input%d", i);
297 find_input_nodes(samsung_pwr, dir);
298 }
299}
300
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100301static void samsung_power_init(struct power_module *module)
302{
303 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
304
305 init_cpufreqs(samsung_pwr);
306 init_touch_input_power_path(samsung_pwr);
307}
308
Christopher N. Hessef6296722017-01-16 22:47:11 +0100309/**********************************************************
310 *** API FUNCTIONS
311 ***
312 *** Refer to power.h for documentation.
313 **********************************************************/
314
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100315static void samsung_power_set_interactive(struct power_module *module, int on)
316{
317 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
318 struct stat sb;
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100319 char touchkey_node[2];
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100320 int rc;
321
322 ALOGV("power_set_interactive: %d\n", on);
323
Christopher N. Hesse12263502016-12-07 12:18:20 +0100324 // Get panel backlight brightness from lights HAL
Christopher N. Hesse3360b092016-07-11 15:48:35 +0200325 // Do not disable any input devices if the screen is on but we are in a non-interactive state
326 if (!on) {
Christopher N. Hesse12263502016-12-07 12:18:20 +0100327 if (get_cur_panel_brightness() > 0) {
Christopher N. Hesse3360b092016-07-11 15:48:35 +0200328 ALOGV("%s: Moving to non-interactive state, but screen is still on,"
329 " not disabling input devices\n", __func__);
330 goto out;
331 }
332 }
333
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100334 sysfs_write(samsung_pwr->touchscreen_power_path, on ? "1" : "0");
335
336 rc = stat(samsung_pwr->touchkey_power_path, &sb);
337 if (rc < 0) {
338 goto out;
339 }
340
341 if (!on) {
342 if (sysfs_read(samsung_pwr->touchkey_power_path, touchkey_node,
343 sizeof(touchkey_node)) == 0) {
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100344 /*
Christopher N. Hesse3360b092016-07-11 15:48:35 +0200345 * If touchkey_node is 0, the keys have been disabled by another component
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100346 * (for example cmhw), which means we don't want them to be enabled when resuming
347 * from suspend.
348 */
Christopher N. Hessed9285742017-01-16 23:59:20 +0100349 if (touchkey_node[0] == '0') {
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100350 samsung_pwr->touchkey_blocked = true;
351 } else {
352 samsung_pwr->touchkey_blocked = false;
353 sysfs_write(samsung_pwr->touchkey_power_path, "0");
354 }
355 }
356 } else if (!samsung_pwr->touchkey_blocked) {
357 sysfs_write(samsung_pwr->touchkey_power_path, "1");
358 }
359
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100360out:
Christopher N. Hesse2879c692016-07-11 15:49:49 +0200361 sysfs_write(IO_IS_BUSY_PATH, on ? "1" : "0");
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100362 ALOGV("power_set_interactive: %d done\n", on);
363}
364
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100365static void samsung_power_hint(struct power_module *module,
366 power_hint_t hint,
367 void *data)
368{
369 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
370 char errno_str[64];
371 int len;
372
373 switch (hint) {
374 case POWER_HINT_INTERACTION: {
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100375 if (current_power_profile == PROFILE_POWER_SAVE) {
376 return;
377 }
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100378
379 ALOGV("%s: POWER_HINT_INTERACTION", __func__);
380
381 if (boostpulse_open(samsung_pwr) >= 0) {
382 len = write(samsung_pwr->boostpulse_fd, "1", 1);
383
384 if (len < 0) {
385 strerror_r(errno, errno_str, sizeof(errno_str));
386 ALOGE("Error writing to %s: %s\n", BOOSTPULSE_PATH, errno_str);
387 }
388 }
389
390 break;
391 }
392 case POWER_HINT_VSYNC: {
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100393 ALOGV("%s: POWER_HINT_VSYNC", __func__);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100394 break;
395 }
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100396 case POWER_HINT_SET_PROFILE: {
397 int profile = *((intptr_t *)data);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100398
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100399 ALOGV("%s: POWER_HINT_SET_PROFILE", __func__);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100400
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100401 set_power_profile(samsung_pwr, profile);
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100402 break;
403 }
404 default:
405 break;
406 }
407}
408
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100409static int samsung_get_feature(struct power_module *module __unused,
410 feature_t feature)
411{
412 if (feature == POWER_FEATURE_SUPPORTED_PROFILES) {
Christopher N. Hesse58f2ca02017-01-17 00:01:15 +0100413 return PROFILE_MAX;
Andreas Schneiderf15d7f42016-02-03 10:43:47 +0100414 }
415
416 return -1;
417}
418
Christopher N. Hesse1c474662016-11-18 18:59:06 +0100419static void samsung_set_feature(struct power_module *module, feature_t feature, int state __unused)
Christopher N. Hessee480d892016-06-22 23:04:39 +0200420{
421 struct samsung_power_module *samsung_pwr = (struct samsung_power_module *) module;
422
423 switch (feature) {
ishantvivek987dcca2016-11-21 06:05:47 +0000424#ifdef TARGET_TAP_TO_WAKE_NODE
Christopher N. Hessee480d892016-06-22 23:04:39 +0200425 case POWER_FEATURE_DOUBLE_TAP_TO_WAKE:
426 ALOGV("%s: %s double tap to wake", __func__, state ? "enabling" : "disabling");
ishantvivek987dcca2016-11-21 06:05:47 +0000427 sysfs_write(TARGET_TAP_TO_WAKE_NODE, state > 0 ? "1" : "0");
Christopher N. Hessee480d892016-06-22 23:04:39 +0200428 break;
429#endif
430 default:
431 break;
432 }
433}
434
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100435static struct hw_module_methods_t power_module_methods = {
436 .open = NULL,
437};
438
439struct samsung_power_module HAL_MODULE_INFO_SYM = {
440 .base = {
441 .common = {
442 .tag = HARDWARE_MODULE_TAG,
443 .module_api_version = POWER_MODULE_API_VERSION_0_2,
444 .hal_api_version = HARDWARE_HAL_API_VERSION,
445 .id = POWER_HARDWARE_MODULE_ID,
446 .name = "Samsung Power HAL",
Christopher N. Hesseaa75be42017-01-16 22:47:59 +0100447 .author = "The LineageOS Project",
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100448 .methods = &power_module_methods,
449 },
450
451 .init = samsung_power_init,
452 .setInteractive = samsung_power_set_interactive,
453 .powerHint = samsung_power_hint,
Christopher N. Hessee480d892016-06-22 23:04:39 +0200454 .getFeature = samsung_get_feature,
455 .setFeature = samsung_set_feature
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100456 },
457
458 .lock = PTHREAD_MUTEX_INITIALIZER,
459 .boostpulse_fd = -1,
Christopher N. Hessede5e3c62015-12-21 21:28:23 +0100460};