• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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