Home | History | Annotate | Download | only in power
      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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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