1 /*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <dirent.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <linux/time.h>
26 #include <stdbool.h>
27 //#define LOG_NDEBUG 0
28
29 #define LOG_TAG "MantaPowerHAL"
30 #include <utils/Log.h>
31
32 #include <hardware/hardware.h>
33 #include <hardware/power.h>
34
35 #define BOOSTPULSE_PATH "/sys/devices/system/cpu/cpufreq/interactive/boostpulse"
36 #define BOOST_PATH "/sys/devices/system/cpu/cpufreq/interactive/boost"
37 #define CPU_MAX_FREQ_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
38 //BOOST_PULSE_DURATION and BOOT_PULSE_DURATION_STR should always be in sync
39 #define BOOST_PULSE_DURATION 1000000
40 #define BOOST_PULSE_DURATION_STR "1000000"
41 #define NSEC_PER_SEC 1000000000
42 #define USEC_PER_SEC 1000000
43 #define NSEC_PER_USEC 100
44 #define LOW_POWER_MAX_FREQ "800000"
45 #define NORMAL_MAX_FREQ "1700000"
46
47 struct manta_power_module {
48 struct power_module base;
49 pthread_mutex_t lock;
50 int boostpulse_fd;
51 int boostpulse_warned;
52 const char *touchscreen_power_path;
53 };
54
55 static unsigned int vsync_count;
56 static struct timespec last_touch_boost;
57 static bool touch_boost;
58 static bool low_power_mode = false;
59
sysfs_write(const char * path,char * s)60 static void sysfs_write(const char *path, char *s)
61 {
62 char buf[80];
63 int len;
64 int fd = open(path, O_WRONLY);
65
66 if (fd < 0) {
67 strerror_r(errno, buf, sizeof(buf));
68 ALOGE("Error opening %s: %s\n", path, buf);
69 return;
70 }
71
72 len = write(fd, s, strlen(s));
73 if (len < 0) {
74 strerror_r(errno, buf, sizeof(buf));
75 ALOGE("Error writing to %s: %s\n", path, buf);
76 }
77
78 close(fd);
79 }
80
init_touchscreen_power_path(struct manta_power_module * manta)81 static void init_touchscreen_power_path(struct manta_power_module *manta)
82 {
83 char buf[80];
84 const char dir[] = "/sys/devices/platform/s3c2440-i2c.3/i2c-3/3-004a/input";
85 const char filename[] = "enabled";
86 DIR *d;
87 struct dirent *de;
88 char *path;
89 int pathsize;
90
91 d = opendir(dir);
92 if (d == NULL) {
93 strerror_r(errno, buf, sizeof(buf));
94 ALOGE("Error opening directory %s: %s\n", dir, buf);
95 return;
96 }
97 while ((de = readdir(d)) != NULL) {
98 if (strncmp("input", de->d_name, 5) == 0) {
99 pathsize = strlen(dir) + strlen(de->d_name) + sizeof(filename) + 2;
100 path = malloc(pathsize);
101 if (path == NULL) {
102 strerror_r(errno, buf, sizeof(buf));
103 ALOGE("Out of memory: %s\n", buf);
104 return;
105 }
106 snprintf(path, pathsize, "%s/%s/%s", dir, de->d_name, filename);
107 manta->touchscreen_power_path = path;
108 goto done;
109 }
110 }
111 ALOGE("Error failed to find input dir in %s\n", dir);
112 done:
113 closedir(d);
114 }
115
power_init(struct power_module * module)116 static void power_init(struct power_module *module)
117 {
118 struct manta_power_module *manta = (struct manta_power_module *) module;
119 struct dirent **namelist;
120 int n;
121
122 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_rate",
123 "20000");
124 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_slack",
125 "20000");
126 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/min_sample_time",
127 "40000");
128 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/hispeed_freq",
129 "1000000");
130 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load",
131 "99");
132 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/target_loads", "70 1200000:70 1300000:75 1400000:80 1500000:99");
133 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/above_hispeed_delay",
134 "80000");
135 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/boostpulse_duration",
136 BOOST_PULSE_DURATION_STR);
137 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/io_is_busy", "1");
138
139 init_touchscreen_power_path(manta);
140 }
141
power_set_interactive(struct power_module * module,int on)142 static void power_set_interactive(struct power_module *module, int on)
143 {
144 struct manta_power_module *manta = (struct manta_power_module *) module;
145 char buf[80];
146 int ret;
147
148 ALOGV("power_set_interactive: %d\n", on);
149
150 /*
151 * Lower maximum frequency when screen is off. CPU 0 and 1 share a
152 * cpufreq policy.
153 */
154 sysfs_write(CPU_MAX_FREQ_PATH,
155 (!on || low_power_mode) ? LOW_POWER_MAX_FREQ : NORMAL_MAX_FREQ);
156
157 sysfs_write(manta->touchscreen_power_path, on ? "Y" : "N");
158
159 ALOGV("power_set_interactive: %d done\n", on);
160 }
161
boostpulse_open(struct manta_power_module * manta)162 static int boostpulse_open(struct manta_power_module *manta)
163 {
164 char buf[80];
165
166 pthread_mutex_lock(&manta->lock);
167
168 if (manta->boostpulse_fd < 0) {
169 manta->boostpulse_fd = open(BOOSTPULSE_PATH, O_WRONLY);
170
171 if (manta->boostpulse_fd < 0) {
172 if (!manta->boostpulse_warned) {
173 strerror_r(errno, buf, sizeof(buf));
174 ALOGE("Error opening %s: %s\n", BOOSTPULSE_PATH, buf);
175 manta->boostpulse_warned = 1;
176 }
177 }
178 }
179
180 pthread_mutex_unlock(&manta->lock);
181 return manta->boostpulse_fd;
182 }
183
timespec_diff(struct timespec lhs,struct timespec rhs)184 static struct timespec timespec_diff(struct timespec lhs, struct timespec rhs)
185 {
186 struct timespec result;
187 if (rhs.tv_nsec > lhs.tv_nsec) {
188 result.tv_sec = lhs.tv_sec - rhs.tv_sec - 1;
189 result.tv_nsec = NSEC_PER_SEC + lhs.tv_nsec - rhs.tv_nsec;
190 } else {
191 result.tv_sec = lhs.tv_sec - rhs.tv_sec;
192 result.tv_nsec = lhs.tv_nsec - rhs.tv_nsec;
193 }
194 return result;
195 }
196
check_boostpulse_on(struct timespec diff)197 static int check_boostpulse_on(struct timespec diff)
198 {
199 long boost_ns = (BOOST_PULSE_DURATION * NSEC_PER_USEC) % NSEC_PER_SEC;
200 long boost_s = BOOST_PULSE_DURATION / USEC_PER_SEC;
201
202 if (diff.tv_sec == boost_s)
203 return (diff.tv_nsec < boost_ns);
204 return (diff.tv_sec < boost_s);
205 }
206
manta_power_hint(struct power_module * module,power_hint_t hint,void * data)207 static void manta_power_hint(struct power_module *module, power_hint_t hint,
208 void *data)
209 {
210 struct manta_power_module *manta = (struct manta_power_module *) module;
211 struct timespec now, diff;
212 char buf[80];
213 int len;
214
215 switch (hint) {
216 case POWER_HINT_INTERACTION:
217 if (boostpulse_open(manta) >= 0) {
218 pthread_mutex_lock(&manta->lock);
219 len = write(manta->boostpulse_fd, "1", 1);
220
221 if (len < 0) {
222 strerror_r(errno, buf, sizeof(buf));
223 ALOGE("Error writing to %s: %s\n", BOOSTPULSE_PATH, buf);
224 } else {
225 clock_gettime(CLOCK_MONOTONIC, &last_touch_boost);
226 touch_boost = true;
227 }
228 pthread_mutex_unlock(&manta->lock);
229 }
230
231 break;
232
233 case POWER_HINT_VSYNC:
234 pthread_mutex_lock(&manta->lock);
235 if (data) {
236 if (vsync_count < UINT_MAX)
237 vsync_count++;
238 } else {
239 if (vsync_count)
240 vsync_count--;
241 if (vsync_count == 0 && touch_boost) {
242 touch_boost = false;
243 clock_gettime(CLOCK_MONOTONIC, &now);
244 diff = timespec_diff(now, last_touch_boost);
245 if (check_boostpulse_on(diff)) {
246 sysfs_write(BOOST_PATH, "0");
247 }
248 }
249 }
250 pthread_mutex_unlock(&manta->lock);
251 break;
252
253 case POWER_HINT_LOW_POWER:
254 pthread_mutex_lock(&manta->lock);
255 if (data)
256 sysfs_write(CPU_MAX_FREQ_PATH, LOW_POWER_MAX_FREQ);
257 else
258 sysfs_write(CPU_MAX_FREQ_PATH, NORMAL_MAX_FREQ);
259 low_power_mode = data;
260 pthread_mutex_unlock(&manta->lock);
261 break;
262 default:
263 break;
264 }
265 }
266
267 static struct hw_module_methods_t power_module_methods = {
268 .open = NULL,
269 };
270
271 struct manta_power_module HAL_MODULE_INFO_SYM = {
272 .base = {
273 .common = {
274 .tag = HARDWARE_MODULE_TAG,
275 .module_api_version = POWER_MODULE_API_VERSION_0_2,
276 .hal_api_version = HARDWARE_HAL_API_VERSION,
277 .id = POWER_HARDWARE_MODULE_ID,
278 .name = "Manta Power HAL",
279 .author = "The Android Open Source Project",
280 .methods = &power_module_methods,
281 },
282
283 .init = power_init,
284 .setInteractive = power_set_interactive,
285 .powerHint = manta_power_hint,
286 },
287
288 .lock = PTHREAD_MUTEX_INITIALIZER,
289 .boostpulse_fd = -1,
290 .boostpulse_warned = 0,
291 };
292
293