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 "QCOMPowerHAL"
42 #include <utils/Log.h>
43 #include <hardware/hardware.h>
44 #include <hardware/power.h>
45
46 #include "utils.h"
47 #include "metadata-defs.h"
48 #include "hint-data.h"
49 #include "performance.h"
50 #include "power-common.h"
51
52 #define BUS_SPEED_PATH "/sys/class/devfreq/qcom,gpubw.70/min_freq"
53 #define GPU_MAX_FREQ_PATH "/sys/class/kgsl/kgsl-3d0/devfreq/max_freq"
54 #define GPU_MIN_FREQ_PATH "/sys/class/kgsl/kgsl-3d0/devfreq/min_freq"
55 #define CPU4_ONLINE_PATH "/sys/devices/system/cpu/cpu4/online"
56 #define CPU5_ONLINE_PATH "/sys/devices/system/cpu/cpu5/online"
57 #define CPU6_ONLINE_PATH "/sys/devices/system/cpu/cpu6/online"
58 #define CPU7_ONLINE_PATH "/sys/devices/system/cpu/cpu7/online"
59
60 #define PLATFORM_SLEEP_MODES 2
61 #define XO_VOTERS 3
62 #define VMIN_VOTERS 0
63
64 #define RPM_PARAMETERS 4
65 #define NUM_PARAMETERS 10
66
67 #ifndef RPM_STAT
68 #define RPM_STAT "/d/rpm_stats"
69 #endif
70
71 #ifndef RPM_MASTER_STAT
72 #define RPM_MASTER_STAT "/d/rpm_master_stats"
73 #endif
74
75 /* RPM runs at 19.2Mhz. Divide by 19200 for msec */
76 #define RPM_CLK 19200
77 #define USINSEC 1000000L
78 #define NSINUS 1000L
79
80 const char *parameter_names[] = {
81 "vlow_count",
82 "accumulated_vlow_time",
83 "vmin_count",
84 "accumulated_vmin_time",
85 "xo_accumulated_duration",
86 "xo_count",
87 "xo_accumulated_duration",
88 "xo_count",
89 "xo_accumulated_duration",
90 "xo_count"};
91
92 static int saved_dcvs_cpu0_slack_max = -1;
93 static int saved_dcvs_cpu0_slack_min = -1;
94 static int saved_mpdecision_slack_max = -1;
95 static int saved_mpdecision_slack_min = -1;
96 static int saved_interactive_mode = -1;
97 static int slack_node_rw_failed = 0;
98 static int display_hint_sent;
99 static int sustained_performance_mode = 0;
100 static int vr_mode = 0;
101 int display_boost;
102 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
103
104 static struct hw_module_methods_t power_module_methods = {
105 .open = NULL,
106 };
107
power_init(struct power_module * module)108 static void power_init(struct power_module *module)
109 {
110 ALOGI("QCOM power HAL initing.");
111
112 int fd;
113 char buf[10] = {0};
114
115 fd = open("/sys/devices/soc0/soc_id", O_RDONLY);
116 if (fd >= 0) {
117 if (read(fd, buf, sizeof(buf) - 1) == -1) {
118 ALOGW("Unable to read soc_id");
119 } else {
120 int soc_id = atoi(buf);
121 if (soc_id == 194 || (soc_id >= 208 && soc_id <= 218) || soc_id == 178) {
122 display_boost = 1;
123 }
124 }
125 close(fd);
126 }
127 }
128
process_video_decode_hint(void * metadata)129 static void process_video_decode_hint(void *metadata)
130 {
131 char governor[80];
132 struct video_decode_metadata_t video_decode_metadata;
133
134 if (get_scaling_governor(governor, sizeof(governor)) == -1) {
135 ALOGE("Can't obtain scaling governor.");
136
137 return;
138 }
139
140 if (metadata) {
141 ALOGI("Processing video decode hint. Metadata: %s", (char *)metadata);
142 }
143
144 /* Initialize encode metadata struct fields. */
145 memset(&video_decode_metadata, 0, sizeof(struct video_decode_metadata_t));
146 video_decode_metadata.state = -1;
147 video_decode_metadata.hint_id = DEFAULT_VIDEO_DECODE_HINT_ID;
148
149 if (metadata) {
150 if (parse_video_decode_metadata((char *)metadata, &video_decode_metadata) ==
151 -1) {
152 ALOGE("Error occurred while parsing metadata.");
153 return;
154 }
155 } else {
156 return;
157 }
158
159 if (video_decode_metadata.state == 1) {
160 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
161 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
162 int resource_values[] = {THREAD_MIGRATION_SYNC_OFF};
163
164 perform_hint_action(video_decode_metadata.hint_id,
165 resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
166 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
167 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
168 int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026, THREAD_MIGRATION_SYNC_OFF};
169
170 perform_hint_action(video_decode_metadata.hint_id,
171 resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
172 }
173 } else if (video_decode_metadata.state == 0) {
174 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
175 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
176 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
177 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
178 undo_hint_action(video_decode_metadata.hint_id);
179 }
180 }
181 }
182
process_video_encode_hint(void * metadata)183 static void process_video_encode_hint(void *metadata)
184 {
185 char governor[80];
186 struct video_encode_metadata_t video_encode_metadata;
187
188 if (get_scaling_governor(governor, sizeof(governor)) == -1) {
189 ALOGE("Can't obtain scaling governor.");
190
191 return;
192 }
193
194 /* Initialize encode metadata struct fields. */
195 memset(&video_encode_metadata, 0, sizeof(struct video_encode_metadata_t));
196 video_encode_metadata.state = -1;
197 video_encode_metadata.hint_id = DEFAULT_VIDEO_ENCODE_HINT_ID;
198
199 if (metadata) {
200 if (parse_video_encode_metadata((char *)metadata, &video_encode_metadata) ==
201 -1) {
202 ALOGE("Error occurred while parsing metadata.");
203 return;
204 }
205 } else {
206 return;
207 }
208
209 if (video_encode_metadata.state == 1) {
210 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
211 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
212 int resource_values[] = {IO_BUSY_OFF, SAMPLING_DOWN_FACTOR_1, THREAD_MIGRATION_SYNC_OFF};
213
214 perform_hint_action(video_encode_metadata.hint_id,
215 resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
216 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
217 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
218 int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026, THREAD_MIGRATION_SYNC_OFF,
219 INTERACTIVE_IO_BUSY_OFF};
220
221 perform_hint_action(video_encode_metadata.hint_id,
222 resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
223 }
224 } else if (video_encode_metadata.state == 0) {
225 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
226 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
227 undo_hint_action(video_encode_metadata.hint_id);
228 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
229 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
230 undo_hint_action(video_encode_metadata.hint_id);
231 }
232 }
233 }
234
power_hint_override(struct power_module * module,power_hint_t hint,void * data)235 int __attribute__ ((weak)) power_hint_override(struct power_module *module, power_hint_t hint,
236 void *data)
237 {
238 return HINT_NONE;
239 }
240
241 /* Declare function before use */
242 int interaction(int duration, int num_args, int opt_list[]);
243 int interaction_with_handle(int lock_handle, int duration, int num_args, int opt_list[]);
244
calc_timespan_us(struct timespec start,struct timespec end)245 static long long calc_timespan_us(struct timespec start, struct timespec end) {
246 long long diff_in_us = 0;
247 diff_in_us += (end.tv_sec - start.tv_sec) * USINSEC;
248 diff_in_us += (end.tv_nsec - start.tv_nsec) / NSINUS;
249 return diff_in_us;
250 }
251
power_hint(struct power_module * module,power_hint_t hint,void * data)252 static void power_hint(struct power_module *module, power_hint_t hint,
253 void *data)
254 {
255 static int handle_hotplug = 0;
256 int resources_hotplug[] = {0x3DFF};
257 /* Check if this hint has been overridden. */
258 if (power_hint_override(module, hint, data) == HINT_HANDLED) {
259 /* The power_hint has been handled. We can skip the rest. */
260 return;
261 }
262
263 switch(hint) {
264 case POWER_HINT_VSYNC:
265 break;
266 case POWER_HINT_INTERACTION:
267 {
268 int duration_hint = 0;
269 static struct timespec previous_boost_timespec = {0, 0};
270
271 // If we are in sustained performance mode or VR mode, touch boost
272 // should be ignored.
273 pthread_mutex_lock(&lock);
274 if (sustained_performance_mode || vr_mode) {
275 pthread_mutex_unlock(&lock);
276 return;
277 }
278 pthread_mutex_unlock(&lock);
279
280 // little core freq bump for 1.5s
281 int resources[] = {0x20C};
282 int duration = 1500;
283 static int handle_little = 0;
284
285 // big core freq bump for 500ms
286 int resources_big[] = {0x2312, 0x1F08};
287 int duration_big = 500;
288 static int handle_big = 0;
289
290 // sched_downmigrate lowered to 10 for 1s at most
291 // should be half of upmigrate
292 int resources_downmigrate[] = {0x4F00};
293 int duration_downmigrate = 1000;
294 static int handle_downmigrate = 0;
295
296 // sched_upmigrate lowered to at most 20 for 500ms
297 // set threshold based on elapsed time since last boost
298 int resources_upmigrate[] = {0x4E00};
299 int duration_upmigrate = 500;
300 static int handle_upmigrate = 0;
301
302 // set duration hint
303 if (data) {
304 duration_hint = *((int*)data);
305 }
306
307 struct timespec cur_boost_timespec;
308 clock_gettime(CLOCK_MONOTONIC, &cur_boost_timespec);
309 pthread_mutex_lock(&lock);
310 long long elapsed_time = calc_timespan_us(previous_boost_timespec, cur_boost_timespec);
311 if (elapsed_time > 750000)
312 elapsed_time = 750000;
313 // don't hint if it's been less than 250ms since last boost
314 // also detect if we're doing anything resembling a fling
315 // support additional boosting in case of flings
316 else if (elapsed_time < 250000 && duration_hint <= 750) {
317 pthread_mutex_unlock(&lock);
318 return;
319 }
320
321 previous_boost_timespec = cur_boost_timespec;
322 pthread_mutex_unlock(&lock);
323
324 // 95: default upmigrate for phone
325 // 20: upmigrate for sporadic touch
326 // 750ms: a completely arbitrary threshold for last touch
327 int upmigrate_value = 95 - (int)(75. * ((elapsed_time*elapsed_time) / (750000.*750000.)));
328
329 // keep sched_upmigrate high when flinging
330 if (duration_hint >= 750)
331 upmigrate_value = 20;
332
333 resources_upmigrate[0] = resources_upmigrate[0] | upmigrate_value;
334 resources_downmigrate[0] = resources_downmigrate[0] | (upmigrate_value / 2);
335
336 // modify downmigrate duration based on interaction data hint
337 // 1000 <= duration_downmigrate <= 5000
338 // extend little core freq bump past downmigrate to soften downmigrates
339 if (duration_hint > 1000) {
340 if (duration_hint < 5000) {
341 duration_downmigrate = duration_hint;
342 duration = duration_hint + 750;
343 } else {
344 duration_downmigrate = 5000;
345 duration = 5750;
346 }
347 }
348
349 handle_little = interaction_with_handle(handle_little,duration, sizeof(resources)/sizeof(resources[0]), resources);
350 handle_big = interaction_with_handle(handle_big, duration_big, sizeof(resources_big)/sizeof(resources_big[0]), resources_big);
351 handle_downmigrate = interaction_with_handle(handle_downmigrate, duration_downmigrate, sizeof(resources_downmigrate)/sizeof(resources_downmigrate[0]), resources_downmigrate);
352 handle_upmigrate = interaction_with_handle(handle_upmigrate, duration_upmigrate, sizeof(resources_upmigrate)/sizeof(resources_upmigrate[0]), resources_upmigrate);
353 }
354 break;
355 case POWER_HINT_VIDEO_ENCODE:
356 process_video_encode_hint(data);
357 break;
358 case POWER_HINT_VIDEO_DECODE:
359 process_video_decode_hint(data);
360 break;
361
362 /* While the system is Sustained Performance Mode:
363 * CPUfreq for the little cores are capped to 864MHz
364 * Big cores are hotplugged out
365 * GPU frequency is capped to 305 MHz
366 */
367 case POWER_HINT_SUSTAINED_PERFORMANCE:
368 {
369 static int handle = 0;
370
371 pthread_mutex_lock(&lock);
372 if (data && sustained_performance_mode == 0) {
373 int resources[] = {0x1509};
374 int duration = 0;
375 handle = interaction_with_handle(handle, duration,
376 sizeof(resources)/sizeof(resources[0]),
377 resources);
378 sysfs_write(GPU_MAX_FREQ_PATH, "305000000");
379 if (vr_mode == 0) {
380 handle_hotplug = interaction_with_handle(handle_hotplug, duration,
381 sizeof(resources_hotplug)/sizeof(resources_hotplug[0]),
382 resources_hotplug);
383 }
384 sustained_performance_mode = 1;
385 } else if (sustained_performance_mode == 1){
386 release_request(handle);
387 sysfs_write(GPU_MAX_FREQ_PATH, "600000000");
388 if (vr_mode == 0) {
389 release_request(handle_hotplug);
390 }
391 sustained_performance_mode = 0;
392 }
393 pthread_mutex_unlock(&lock);
394 }
395 break;
396 case POWER_HINT_VR_MODE:
397 {
398 static int handle_vr = 0;
399 pthread_mutex_lock(&lock);
400 if (data && vr_mode == 0) {
401 int resources[] = {0x206};
402 int duration = 0;
403 handle_vr = interaction_with_handle(handle_vr, duration,
404 sizeof(resources)/sizeof(resources[0]),
405 resources);
406 sysfs_write(GPU_MIN_FREQ_PATH, "305000000");
407 sysfs_write(BUS_SPEED_PATH, "7904");
408 if (sustained_performance_mode == 0) {
409 handle_hotplug = interaction_with_handle(handle_hotplug, duration,
410 sizeof(resources_hotplug)/sizeof(resources_hotplug[0]),
411 resources_hotplug);
412 }
413 vr_mode = 1;
414 } else if (vr_mode == 1){
415 release_request(handle_vr);
416 sysfs_write(GPU_MIN_FREQ_PATH, "180000000");
417 sysfs_write(BUS_SPEED_PATH, "0");
418 if (sustained_performance_mode == 0) {
419 release_request(handle_hotplug);
420 }
421 vr_mode = 0;
422 }
423 pthread_mutex_unlock(&lock);
424 }
425 }
426 }
427
set_interactive_override(struct power_module * module,int on)428 int __attribute__ ((weak)) set_interactive_override(struct power_module *module, int on)
429 {
430 return HINT_NONE;
431 }
432
set_interactive(struct power_module * module,int on)433 void set_interactive(struct power_module *module, int on)
434 {
435 char governor[80];
436 char tmp_str[NODE_MAX];
437 struct video_encode_metadata_t video_encode_metadata;
438 int rc;
439
440 if (set_interactive_override(module, on) == HINT_HANDLED) {
441 return;
442 }
443
444 ALOGI("Got set_interactive hint");
445
446 if (get_scaling_governor(governor, sizeof(governor)) == -1) {
447 ALOGE("Can't obtain scaling governor.");
448
449 return;
450 }
451
452 if (!on) {
453 /* Display off. */
454 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
455 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
456 int resource_values[] = {DISPLAY_OFF, MS_500, THREAD_MIGRATION_SYNC_OFF};
457
458 if (!display_hint_sent) {
459 perform_hint_action(DISPLAY_STATE_HINT_ID,
460 resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
461 display_hint_sent = 1;
462 }
463 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
464 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
465 int resource_values[] = {TR_MS_50, THREAD_MIGRATION_SYNC_OFF};
466
467 if (!display_hint_sent) {
468 perform_hint_action(DISPLAY_STATE_HINT_ID,
469 resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
470 display_hint_sent = 1;
471 }
472 } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) &&
473 (strlen(governor) == strlen(MSMDCVS_GOVERNOR))) {
474 if (saved_interactive_mode == 1){
475 /* Display turned off. */
476 if (sysfs_read(DCVS_CPU0_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) {
477 if (!slack_node_rw_failed) {
478 ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MAX_NODE);
479 }
480
481 rc = 1;
482 } else {
483 saved_dcvs_cpu0_slack_max = atoi(tmp_str);
484 }
485
486 if (sysfs_read(DCVS_CPU0_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) {
487 if (!slack_node_rw_failed) {
488 ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MIN_NODE);
489 }
490
491 rc = 1;
492 } else {
493 saved_dcvs_cpu0_slack_min = atoi(tmp_str);
494 }
495
496 if (sysfs_read(MPDECISION_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) {
497 if (!slack_node_rw_failed) {
498 ALOGE("Failed to read from %s", MPDECISION_SLACK_MAX_NODE);
499 }
500
501 rc = 1;
502 } else {
503 saved_mpdecision_slack_max = atoi(tmp_str);
504 }
505
506 if (sysfs_read(MPDECISION_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) {
507 if(!slack_node_rw_failed) {
508 ALOGE("Failed to read from %s", MPDECISION_SLACK_MIN_NODE);
509 }
510
511 rc = 1;
512 } else {
513 saved_mpdecision_slack_min = atoi(tmp_str);
514 }
515
516 /* Write new values. */
517 if (saved_dcvs_cpu0_slack_max != -1) {
518 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_max);
519
520 if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) {
521 if (!slack_node_rw_failed) {
522 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE);
523 }
524
525 rc = 1;
526 }
527 }
528
529 if (saved_dcvs_cpu0_slack_min != -1) {
530 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_min);
531
532 if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) {
533 if(!slack_node_rw_failed) {
534 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE);
535 }
536
537 rc = 1;
538 }
539 }
540
541 if (saved_mpdecision_slack_max != -1) {
542 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_max);
543
544 if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) {
545 if(!slack_node_rw_failed) {
546 ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE);
547 }
548
549 rc = 1;
550 }
551 }
552
553 if (saved_mpdecision_slack_min != -1) {
554 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_min);
555
556 if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) {
557 if(!slack_node_rw_failed) {
558 ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE);
559 }
560
561 rc = 1;
562 }
563 }
564 }
565
566 slack_node_rw_failed = rc;
567 }
568 } else {
569 /* Display on. */
570 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
571 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
572 undo_hint_action(DISPLAY_STATE_HINT_ID);
573 display_hint_sent = 0;
574 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
575 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
576 undo_hint_action(DISPLAY_STATE_HINT_ID);
577 display_hint_sent = 0;
578 } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) &&
579 (strlen(governor) == strlen(MSMDCVS_GOVERNOR))) {
580 if (saved_interactive_mode == -1 || saved_interactive_mode == 0) {
581 /* Display turned on. Restore if possible. */
582 if (saved_dcvs_cpu0_slack_max != -1) {
583 snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_max);
584
585 if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) {
586 if (!slack_node_rw_failed) {
587 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE);
588 }
589
590 rc = 1;
591 }
592 }
593
594 if (saved_dcvs_cpu0_slack_min != -1) {
595 snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_min);
596
597 if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) {
598 if (!slack_node_rw_failed) {
599 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE);
600 }
601
602 rc = 1;
603 }
604 }
605
606 if (saved_mpdecision_slack_max != -1) {
607 snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_max);
608
609 if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) {
610 if (!slack_node_rw_failed) {
611 ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE);
612 }
613
614 rc = 1;
615 }
616 }
617
618 if (saved_mpdecision_slack_min != -1) {
619 snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_min);
620
621 if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) {
622 if (!slack_node_rw_failed) {
623 ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE);
624 }
625
626 rc = 1;
627 }
628 }
629 }
630
631 slack_node_rw_failed = rc;
632 }
633 }
634
635 saved_interactive_mode = !!on;
636 }
637
get_number_of_platform_modes(struct power_module * module)638 static ssize_t get_number_of_platform_modes(struct power_module *module) {
639 return PLATFORM_SLEEP_MODES;
640 }
641
get_voter_list(struct power_module * module,size_t * voter)642 static int get_voter_list(struct power_module *module, size_t *voter) {
643 voter[0] = XO_VOTERS;
644 voter[1] = VMIN_VOTERS;
645
646 return 0;
647 }
648
extract_stats(uint64_t * list,char * file,unsigned int num_parameters,unsigned int index)649 static int extract_stats(uint64_t *list, char *file,
650 unsigned int num_parameters, unsigned int index) {
651 FILE *fp;
652 ssize_t read;
653 size_t len;
654 char *line;
655 int ret;
656
657 fp = fopen(file, "r");
658 if (fp == NULL) {
659 ret = -errno;
660 ALOGE("%s: failed to open: %s", __func__, strerror(errno));
661 return ret;
662 }
663
664 for (line = NULL, len = 0;
665 ((read = getline(&line, &len, fp) != -1) && (index < num_parameters));
666 free(line), line = NULL, len = 0) {
667 uint64_t value;
668 char* offset;
669
670 size_t begin = strspn(line, " \t");
671 if (strncmp(line + begin, parameter_names[index], strlen(parameter_names[index]))) {
672 continue;
673 }
674
675 offset = memchr(line, ':', len);
676 if (!offset) {
677 continue;
678 }
679
680 if (!strcmp(file, RPM_MASTER_STAT)) {
681 /* RPM_MASTER_STAT is reported in hex */
682 sscanf(offset, ":%" SCNx64, &value);
683 /* Duration is reported in rpm SLEEP TICKS */
684 if (!strcmp(parameter_names[index], "xo_accumulated_duration")) {
685 value /= RPM_CLK;
686 }
687 } else {
688 /* RPM_STAT is reported in decimal */
689 sscanf(offset, ":%" SCNu64, &value);
690 }
691 list[index] = value;
692 index++;
693 }
694 free(line);
695
696 fclose(fp);
697 return 0;
698 }
699
get_platform_low_power_stats(struct power_module * module,power_state_platform_sleep_state_t * list)700 static int get_platform_low_power_stats(struct power_module *module,
701 power_state_platform_sleep_state_t *list) {
702 uint64_t stats[sizeof(parameter_names)] = {0};
703 int ret;
704
705 if (!list) {
706 return -EINVAL;
707 }
708
709 ret = extract_stats(stats, RPM_STAT, RPM_PARAMETERS, 0);
710
711 if (ret) {
712 return ret;
713 }
714
715 ret = extract_stats(stats, RPM_MASTER_STAT, NUM_PARAMETERS, 4);
716
717 if (ret) {
718 return ret;
719 }
720
721 /* Update statistics for XO_shutdown */
722 strcpy(list[0].name, "XO_shutdown");
723 list[0].total_transitions = stats[0];
724 list[0].residency_in_msec_since_boot = stats[1];
725 list[0].supported_only_in_suspend = false;
726 list[0].number_of_voters = XO_VOTERS;
727
728 /* Update statistics for APSS voter */
729 strcpy(list[0].voters[0].name, "APSS");
730 list[0].voters[0].total_time_in_msec_voted_for_since_boot = stats[4];
731 list[0].voters[0].total_number_of_times_voted_since_boot = stats[5];
732
733 /* Update statistics for MPSS voter */
734 strcpy(list[0].voters[1].name, "MPSS");
735 list[0].voters[1].total_time_in_msec_voted_for_since_boot = stats[6];
736 list[0].voters[1].total_number_of_times_voted_since_boot = stats[7];
737
738 /* Update statistics for LPASS voter */
739 strcpy(list[0].voters[2].name, "LPASS");
740 list[0].voters[2].total_time_in_msec_voted_for_since_boot = stats[8];
741 list[0].voters[2].total_number_of_times_voted_since_boot = stats[9];
742
743 /* Update statistics for VMIN state */
744 strcpy(list[1].name, "VMIN");
745 list[1].total_transitions = stats[2];
746 list[1].residency_in_msec_since_boot = stats[3];
747 list[1].supported_only_in_suspend = false;
748 list[1].number_of_voters = VMIN_VOTERS;
749
750 return 0;
751 }
752
753 struct power_module HAL_MODULE_INFO_SYM = {
754 .common = {
755 .tag = HARDWARE_MODULE_TAG,
756 .module_api_version = POWER_MODULE_API_VERSION_0_5,
757 .hal_api_version = HARDWARE_HAL_API_VERSION,
758 .id = POWER_HARDWARE_MODULE_ID,
759 .name = "QCOM Power HAL",
760 .author = "Qualcomm",
761 .methods = &power_module_methods,
762 },
763
764 .init = power_init,
765 .powerHint = power_hint,
766 .setInteractive = set_interactive,
767 .get_number_of_platform_modes = get_number_of_platform_modes,
768 .get_platform_low_power_stats = get_platform_low_power_stats,
769 .get_voter_list = get_voter_list
770 };
771