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
power_init(void)102 void power_init(void)
103 {
104 ALOGI("QCOM power HAL initing.");
105 }
106
power_hint_override(power_hint_t UNUSED (hint),void * UNUSED (data))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
power_hint(power_hint_t hint,void * data)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
is_perf_hint_active(int UNUSED (hint))146 int __attribute__ ((weak)) is_perf_hint_active(int UNUSED(hint))
147 {
148 return 0;
149 }
150
set_interactive_override(int UNUSED (on))151 int __attribute__ ((weak)) set_interactive_override(int UNUSED(on))
152 {
153 return HINT_NONE;
154 }
155
power_set_interactive(int on)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
parse_stats(const char ** params,size_t params_size,uint64_t * list,FILE * fp)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
extract_stats(uint64_t * list,char * file,struct stat_pair * map,size_t map_size)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
extract_platform_stats(uint64_t * list)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
extract_wlan_stats(uint64_t * list)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