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