1 /* 2 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above 10 * copyright notice, this list of conditions and the following 11 * disclaimer in the documentation and/or other materials provided 12 * with the distribution. 13 * * Neither the name of The Linux Foundation nor the names of its 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED 18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #define LOG_NIDEBUG 0 31 32 #include <errno.h> 33 #include <inttypes.h> 34 #include <string.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <fcntl.h> 38 #include <dlfcn.h> 39 #include <stdlib.h> 40 41 #define LOG_TAG "QCOM PowerHAL" 42 #include <utils/Log.h> 43 #include <hardware/power.h> 44 45 #include "utils.h" 46 #include "metadata-defs.h" 47 #include "hint-data.h" 48 #include "performance.h" 49 #include "power-common.h" 50 #include "power-helper.h" 51 52 #ifndef RPM_SYSTEM_STAT 53 #define RPM_SYSTEM_STAT "/d/system_stats" 54 #endif 55 56 #ifndef WLAN_POWER_STAT 57 #define WLAN_POWER_STAT "/d/wlan0/power_stats" 58 #endif 59 60 #define ARRAY_SIZE(x) (sizeof((x))/sizeof((x)[0])) 61 #define LINE_SIZE 128 62 63 const char *rpm_stat_params[MAX_RPM_PARAMS] = { 64 "count", 65 "actual last sleep(msec)", 66 }; 67 68 const char *master_stat_params[MAX_RPM_PARAMS] = { 69 "Accumulated XO duration", 70 "XO Count", 71 }; 72 73 struct stat_pair rpm_stat_map[] = { 74 { RPM_MODE_XO, "RPM Mode:vlow", rpm_stat_params, ARRAY_SIZE(rpm_stat_params) }, 75 { RPM_MODE_VMIN, "RPM Mode:vmin", rpm_stat_params, ARRAY_SIZE(rpm_stat_params) }, 76 { VOTER_APSS, "APSS", master_stat_params, ARRAY_SIZE(master_stat_params) }, 77 { VOTER_MPSS, "MPSS", master_stat_params, ARRAY_SIZE(master_stat_params) }, 78 { VOTER_ADSP, "ADSP", master_stat_params, ARRAY_SIZE(master_stat_params) }, 79 { VOTER_SLPI, "SLPI", master_stat_params, ARRAY_SIZE(master_stat_params) }, 80 }; 81 82 83 const char *wlan_power_stat_params[] = { 84 "cumulative_sleep_time_ms", 85 "cumulative_total_on_time_ms", 86 "deep_sleep_enter_counter", 87 "last_deep_sleep_enter_tstamp_ms" 88 }; 89 90 struct stat_pair wlan_stat_map[] = { 91 { WLAN_POWER_DEBUG_STATS, "POWER DEBUG STATS", wlan_power_stat_params, ARRAY_SIZE(wlan_power_stat_params) }, 92 }; 93 94 static int saved_dcvs_cpu0_slack_max = -1; 95 static int saved_dcvs_cpu0_slack_min = -1; 96 static int saved_mpdecision_slack_max = -1; 97 static int saved_mpdecision_slack_min = -1; 98 static int saved_interactive_mode = -1; 99 static int slack_node_rw_failed = 0; 100 static int display_hint_sent; 101 102 void power_init(void) 103 { 104 ALOGI("QCOM power HAL initing."); 105 } 106 107 int __attribute__ ((weak)) power_hint_override(power_hint_t UNUSED(hint), 108 void *UNUSED(data)) 109 { 110 return HINT_NONE; 111 } 112 113 /* Declare function before use */ 114 void interaction(int duration, int num_args, int opt_list[]); 115 116 void power_hint(power_hint_t hint, void *data) 117 { 118 /* Check if this hint has been overridden. */ 119 if (power_hint_override(hint, data) == HINT_HANDLED) { 120 /* The power_hint has been handled. We can skip the rest. */ 121 return; 122 } 123 124 switch(hint) { 125 case POWER_HINT_VSYNC: 126 break; 127 case POWER_HINT_SUSTAINED_PERFORMANCE: 128 ALOGD("Sustained perf power hint not handled in power_hint_override"); 129 break; 130 case POWER_HINT_VR_MODE: 131 ALOGD("VR mode power hint not handled in power_hint_override"); 132 break; 133 case POWER_HINT_INTERACTION: 134 { 135 int resources[] = {0x702, 0x20F, 0x30F}; 136 int duration = 3000; 137 138 interaction(duration, sizeof(resources)/sizeof(resources[0]), resources); 139 } 140 break; 141 default: 142 break; 143 } 144 } 145 146 int __attribute__ ((weak)) is_perf_hint_active(int UNUSED(hint)) 147 { 148 return 0; 149 } 150 151 int __attribute__ ((weak)) set_interactive_override(int UNUSED(on)) 152 { 153 return HINT_NONE; 154 } 155 156 void power_set_interactive(int on) 157 { 158 char governor[80]; 159 char tmp_str[NODE_MAX]; 160 struct video_encode_metadata_t video_encode_metadata; 161 int rc = 0; 162 163 if (set_interactive_override(on) == HINT_HANDLED) { 164 return; 165 } 166 167 ALOGD("Got set_interactive hint"); 168 169 if (get_scaling_governor(governor, sizeof(governor)) == -1) { 170 ALOGE("Can't obtain scaling governor."); 171 172 return; 173 } 174 175 if (!on) { 176 /* Display off. */ 177 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && 178 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { 179 int resource_values[] = {DISPLAY_OFF, MS_500, THREAD_MIGRATION_SYNC_OFF}; 180 181 if (!display_hint_sent) { 182 perform_hint_action(DISPLAY_STATE_HINT_ID, 183 resource_values, sizeof(resource_values)/sizeof(resource_values[0])); 184 display_hint_sent = 1; 185 } 186 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && 187 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { 188 int resource_values[] = {TR_MS_50, THREAD_MIGRATION_SYNC_OFF}; 189 190 if (!display_hint_sent) { 191 perform_hint_action(DISPLAY_STATE_HINT_ID, 192 resource_values, sizeof(resource_values)/sizeof(resource_values[0])); 193 display_hint_sent = 1; 194 } 195 } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) && 196 (strlen(governor) == strlen(MSMDCVS_GOVERNOR))) { 197 if (saved_interactive_mode == 1){ 198 /* Display turned off. */ 199 if (sysfs_read(DCVS_CPU0_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) { 200 if (!slack_node_rw_failed) { 201 ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MAX_NODE); 202 } 203 204 rc = 1; 205 } else { 206 saved_dcvs_cpu0_slack_max = atoi(tmp_str); 207 } 208 209 if (sysfs_read(DCVS_CPU0_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) { 210 if (!slack_node_rw_failed) { 211 ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MIN_NODE); 212 } 213 214 rc = 1; 215 } else { 216 saved_dcvs_cpu0_slack_min = atoi(tmp_str); 217 } 218 219 if (sysfs_read(MPDECISION_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) { 220 if (!slack_node_rw_failed) { 221 ALOGE("Failed to read from %s", MPDECISION_SLACK_MAX_NODE); 222 } 223 224 rc = 1; 225 } else { 226 saved_mpdecision_slack_max = atoi(tmp_str); 227 } 228 229 if (sysfs_read(MPDECISION_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) { 230 if(!slack_node_rw_failed) { 231 ALOGE("Failed to read from %s", MPDECISION_SLACK_MIN_NODE); 232 } 233 234 rc = 1; 235 } else { 236 saved_mpdecision_slack_min = atoi(tmp_str); 237 } 238 239 /* Write new values. */ 240 if (saved_dcvs_cpu0_slack_max != -1) { 241 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_max); 242 243 if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) { 244 if (!slack_node_rw_failed) { 245 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE); 246 } 247 248 rc = 1; 249 } 250 } 251 252 if (saved_dcvs_cpu0_slack_min != -1) { 253 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_min); 254 255 if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) { 256 if(!slack_node_rw_failed) { 257 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE); 258 } 259 260 rc = 1; 261 } 262 } 263 264 if (saved_mpdecision_slack_max != -1) { 265 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_max); 266 267 if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) { 268 if(!slack_node_rw_failed) { 269 ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE); 270 } 271 272 rc = 1; 273 } 274 } 275 276 if (saved_mpdecision_slack_min != -1) { 277 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_min); 278 279 if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) { 280 if(!slack_node_rw_failed) { 281 ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE); 282 } 283 284 rc = 1; 285 } 286 } 287 } 288 289 slack_node_rw_failed = rc; 290 } 291 } else { 292 /* Display on. */ 293 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && 294 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { 295 undo_hint_action(DISPLAY_STATE_HINT_ID); 296 display_hint_sent = 0; 297 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && 298 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { 299 undo_hint_action(DISPLAY_STATE_HINT_ID); 300 display_hint_sent = 0; 301 } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) && 302 (strlen(governor) == strlen(MSMDCVS_GOVERNOR))) { 303 if (saved_interactive_mode == -1 || saved_interactive_mode == 0) { 304 /* Display turned on. Restore if possible. */ 305 if (saved_dcvs_cpu0_slack_max != -1) { 306 snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_max); 307 308 if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) { 309 if (!slack_node_rw_failed) { 310 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE); 311 } 312 313 rc = 1; 314 } 315 } 316 317 if (saved_dcvs_cpu0_slack_min != -1) { 318 snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_min); 319 320 if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) { 321 if (!slack_node_rw_failed) { 322 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE); 323 } 324 325 rc = 1; 326 } 327 } 328 329 if (saved_mpdecision_slack_max != -1) { 330 snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_max); 331 332 if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) { 333 if (!slack_node_rw_failed) { 334 ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE); 335 } 336 337 rc = 1; 338 } 339 } 340 341 if (saved_mpdecision_slack_min != -1) { 342 snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_min); 343 344 if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) { 345 if (!slack_node_rw_failed) { 346 ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE); 347 } 348 349 rc = 1; 350 } 351 } 352 } 353 354 slack_node_rw_failed = rc; 355 } 356 } 357 358 saved_interactive_mode = !!on; 359 } 360 361 362 static int parse_stats(const char **params, size_t params_size, 363 uint64_t *list, FILE *fp) { 364 ssize_t nread; 365 size_t len = LINE_SIZE; 366 char *line; 367 size_t params_read = 0; 368 size_t i; 369 370 line = malloc(len); 371 if (!line) { 372 ALOGE("%s: no memory to hold line", __func__); 373 return -ENOMEM; 374 } 375 376 while ((params_read < params_size) && 377 (nread = getline(&line, &len, fp) > 0)) { 378 char *key = line + strspn(line, " \t"); 379 char *value = strchr(key, ':'); 380 if (!value || (value > (line + len))) 381 continue; 382 *value++ = '\0'; 383 384 for (i = 0; i < params_size; i++) { 385 if (!strcmp(key, params[i])) { 386 list[i] = strtoull(value, NULL, 0); 387 params_read++; 388 break; 389 } 390 } 391 } 392 free(line); 393 394 return 0; 395 } 396 397 398 static int extract_stats(uint64_t *list, char *file, 399 struct stat_pair *map, size_t map_size) { 400 FILE *fp; 401 ssize_t read; 402 size_t len = LINE_SIZE; 403 char *line; 404 size_t i, stats_read = 0; 405 int ret = 0; 406 407 fp = fopen(file, "re"); 408 if (fp == NULL) { 409 ALOGE("%s: failed to open: %s Error = %s", __func__, file, strerror(errno)); 410 return -errno; 411 } 412 413 line = malloc(len); 414 if (!line) { 415 ALOGE("%s: no memory to hold line", __func__); 416 fclose(fp); 417 return -ENOMEM; 418 } 419 420 while ((stats_read < map_size) && (read = getline(&line, &len, fp) != -1)) { 421 size_t begin = strspn(line, " \t"); 422 423 for (i = 0; i < map_size; i++) { 424 if (!strncmp(line + begin, map[i].label, strlen(map[i].label))) { 425 stats_read++; 426 break; 427 } 428 } 429 430 if (i == map_size) 431 continue; 432 433 ret = parse_stats(map[i].parameters, map[i].num_parameters, 434 &list[map[i].stat * MAX_RPM_PARAMS], fp); 435 if (ret < 0) 436 break; 437 } 438 free(line); 439 fclose(fp); 440 441 return ret; 442 } 443 444 int extract_platform_stats(uint64_t *list) { 445 return extract_stats(list, RPM_SYSTEM_STAT, rpm_stat_map, ARRAY_SIZE(rpm_stat_map)); 446 } 447 448 int extract_wlan_stats(uint64_t *list) { 449 return extract_stats(list, WLAN_POWER_STAT, wlan_stat_map, ARRAY_SIZE(wlan_stat_map)); 450 } 451