blob: 5c018576fad8f2cf175cecc9466167c6422763c5 [file] [log] [blame]
Anurag Singh9cec85b2012-09-04 21:46:24 -07001/*
2 * Copyright (c) 2012, 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 Code Aurora Forum, Inc. 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#include <errno.h>
31#include <string.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <fcntl.h>
35#include <dlfcn.h>
36#include <stdlib.h>
37
38#define LOG_TAG "QCOM PowerHAL"
39
40#include <utils/Log.h>
41#include <cutils/properties.h>
42
43#include <hardware/hardware.h>
44#include <hardware/power.h>
45
46#include "metadata-defs.h"
47
Anurag Singh6f64e9f2012-10-09 16:08:38 -070048#define NODE_MAX (64)
49
Anurag Singh9cec85b2012-09-04 21:46:24 -070050#define SCALING_GOVERNOR_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
51#define ONDEMAND_PATH "/sys/devices/system/cpu/cpufreq/ondemand/"
52#define ONDEMAND_IO_BUSY_PATH "/sys/devices/system/cpu/cpufreq/ondemand/io_is_busy"
53#define ONDEMAND_SAMPLING_DOWN_PATH "/sys/devices/system/cpu/cpufreq/ondemand/sampling_down_factor"
Anurag Singh6f64e9f2012-10-09 16:08:38 -070054#define DCVS_CPU0_SLACK_MAX_NODE "/sys/module/msm_dcvs/cores/cpu0/slack_time_max_us"
55#define DCVS_CPU0_SLACK_MIN_NODE "/sys/module/msm_dcvs/cores/cpu0/slack_time_min_us"
56#define MPDECISION_SLACK_MAX_NODE "/sys/module/msm_mpdecision/slack_time_max_us"
57#define MPDECISION_SLACK_MIN_NODE "/sys/module/msm_mpdecision/slack_time_min_us"
Anurag Singh9cec85b2012-09-04 21:46:24 -070058
59static int (*perf_vote_turnoff_ondemand_io_busy)(int vote);
60static int perf_vote_ondemand_io_busy_unavailable;
61static int (*perf_vote_lower_ondemand_sdf)(int vote);
62static int perf_vote_ondemand_sdf_unavailable;
63static void *qcopt_handle;
64static int qcopt_handle_unavailable;
65static int saved_ondemand_sampling_down_factor = 4;
66static int saved_ondemand_io_is_busy_status = 1;
Anurag Singh6f64e9f2012-10-09 16:08:38 -070067static int saved_dcvs_cpu0_slack_max = -1;
68static int saved_dcvs_cpu0_slack_min = -1;
69static int saved_mpdecision_slack_max = -1;
70static int saved_mpdecision_slack_min = -1;
71static int saved_interactive_mode = -1;
72static int slack_node_rw_failed = 0;
Anurag Singh9cec85b2012-09-04 21:46:24 -070073
74static void *get_qcopt_handle()
75{
76 if (qcopt_handle_unavailable) {
77 return NULL;
78 }
79
80 if (!qcopt_handle) {
81 char qcopt_lib_path[PATH_MAX] = {0};
82 dlerror();
83
84 if (property_get("ro.vendor.extension_library", qcopt_lib_path,
85 NULL) != 0) {
86 if((qcopt_handle = dlopen(qcopt_lib_path, RTLD_NOW)) == NULL) {
87 qcopt_handle_unavailable = 1;
88 ALOGE("Unable to open %s: %s\n", qcopt_lib_path,
89 dlerror());
90 }
91 } else {
92 qcopt_handle_unavailable = 1;
93 ALOGE("Property ro.vendor.extension_library does not exist.");
94 }
95 }
96
97 return qcopt_handle;
98}
99
100static int sysfs_read(char *path, char *s, int num_bytes)
101{
102 char buf[80];
103 int count;
104 int ret = 0;
105 int fd = open(path, O_RDONLY);
106
107 if (fd < 0) {
108 strerror_r(errno, buf, sizeof(buf));
109 ALOGE("Error opening %s: %s\n", path, buf);
110
111 return -1;
112 }
113
114 if ((count = read(fd, s, num_bytes - 1)) < 0) {
115 strerror_r(errno, buf, sizeof(buf));
116 ALOGE("Error writing to %s: %s\n", path, buf);
117
118 ret = -1;
119 } else {
120 s[count] = '\0';
121 }
122
123 close(fd);
124
125 return ret;
126}
127
128static int sysfs_write(char *path, char *s)
129{
130 char buf[80];
131 int len;
132 int ret = 0;
133 int fd = open(path, O_WRONLY);
134
135 if (fd < 0) {
136 strerror_r(errno, buf, sizeof(buf));
137 ALOGE("Error opening %s: %s\n", path, buf);
138 return -1 ;
139 }
140
141 len = write(fd, s, strlen(s));
142 if (len < 0) {
143 strerror_r(errno, buf, sizeof(buf));
144 ALOGE("Error writing to %s: %s\n", path, buf);
145
146 ret = -1;
147 }
148
149 close(fd);
150
151 return ret;
152}
153
154static struct hw_module_methods_t power_module_methods = {
155 .open = NULL,
156};
157
158void power_init(struct power_module *module)
159{
160 ALOGI("QCOM power HAL initing.");
161}
162
163static int get_scaling_governor(char governor[], int size) {
164 if (sysfs_read(SCALING_GOVERNOR_PATH, governor,
165 size) == -1) {
166 // Can't obtain the scaling governor. Return.
167 return -1;
168 } else {
169 // Strip newline at the end.
170 int len = strlen(governor);
171
172 len--;
173
174 while (len >= 0 && (governor[len] == '\n' || governor[len] == '\r'))
175 governor[len--] = '\0';
176 }
177
178 return 0;
179}
180
181static void process_video_encode_hint(void *metadata)
182{
183 void *handle;
184 char governor[80];
185 struct video_encode_metadata_t video_encode_metadata;
186
187 if (get_scaling_governor(governor, sizeof(governor)) == -1) {
188 ALOGE("Can't obtain scaling governor.");
189
190 return;
191 }
192
193 /* Initialize encode metadata struct fields. */
194 memset(&video_encode_metadata, 0, sizeof(video_encode_metadata));
195 video_encode_metadata.state = -1;
196
197 if (metadata) {
198 if (parse_video_metadata((char *)metadata, &video_encode_metadata) ==
199 -1) {
200 ALOGE("Error occurred while parsing metadata.");
201 return;
202 }
203 } else {
204 return;
205 }
206
207 if ((handle = get_qcopt_handle())) {
208 if (video_encode_metadata.state == 1) {
209 if ((strlen(governor) == strlen("ondemand")) &&
210 (strncmp(governor, "ondemand", strlen("ondemand")) == 0)) {
211 if (!perf_vote_ondemand_io_busy_unavailable) {
212 perf_vote_turnoff_ondemand_io_busy = dlsym(handle,
213 "perf_vote_turnoff_ondemand_io_busy");
214
215 if (perf_vote_turnoff_ondemand_io_busy) {
216 /* Vote to turn io_is_busy off */
217 perf_vote_turnoff_ondemand_io_busy(1);
218 } else {
219 perf_vote_ondemand_io_busy_unavailable = 1;
220 ALOGE("Can't set io_busy_status.");
221 }
222 }
223
224 if (!perf_vote_ondemand_sdf_unavailable) {
225 perf_vote_lower_ondemand_sdf = dlsym(handle,
226 "perf_vote_lower_ondemand_sdf");
227
228 if (perf_vote_lower_ondemand_sdf) {
229 perf_vote_lower_ondemand_sdf(1);
230 } else {
231 perf_vote_ondemand_sdf_unavailable = 1;
232 ALOGE("Can't set sampling_down_factor.");
233 }
234 }
235 }
236 } else if (video_encode_metadata.state == 0) {
237 if ((strlen(governor) == strlen("ondemand")) &&
238 (strncmp(governor, "ondemand", strlen("ondemand")) == 0)) {
239 if (!perf_vote_ondemand_io_busy_unavailable) {
240 perf_vote_turnoff_ondemand_io_busy = dlsym(handle,
241 "perf_vote_turnoff_ondemand_io_busy");
242
243 if (perf_vote_turnoff_ondemand_io_busy) {
244 /* Remove vote to turn io_busy off. */
245 perf_vote_turnoff_ondemand_io_busy(0);
246 } else {
247 perf_vote_ondemand_io_busy_unavailable = 1;
248 ALOGE("Can't set io_busy_status.");
249 }
250 }
251
252 if (!perf_vote_ondemand_sdf_unavailable) {
253 perf_vote_lower_ondemand_sdf = dlsym(handle,
254 "perf_vote_lower_ondemand_sdf");
255
256 if (perf_vote_lower_ondemand_sdf) {
257 /* Remove vote to lower sampling down factor. */
258 perf_vote_lower_ondemand_sdf(0);
259 } else {
260 perf_vote_ondemand_sdf_unavailable = 1;
261 ALOGE("Can't set sampling_down_factor.");
262 }
263 }
264 }
265 }
266 }
267}
268
269int __attribute__ ((weak)) power_hint_override(struct power_module *module, power_hint_t hint,
270 void *data)
271{
272 return -1;
273}
274
275static void power_hint(struct power_module *module, power_hint_t hint,
276 void *data)
277{
278 /* Check if this hint has been overridden. */
279 if (power_hint_override(module, hint, data) == 0) {
280 /* The power_hint has been handled. We can skip the rest. */
281 return;
282 }
283
284 switch(hint) {
285 case POWER_HINT_VSYNC:
286 break;
287 case POWER_HINT_INTERACTION:
288 break;
289 case POWER_HINT_VIDEO_ENCODE:
290 process_video_encode_hint(data);
291 break;
292 }
293}
294
Anurag Singh6f64e9f2012-10-09 16:08:38 -0700295static void set_interactive(struct power_module *module, int on)
Anurag Singh9cec85b2012-09-04 21:46:24 -0700296{
Anurag Singh6f64e9f2012-10-09 16:08:38 -0700297 char governor[80];
298 char tmp_str[NODE_MAX];
299 int rc = 0;
300
301 if (get_scaling_governor(governor, sizeof(governor)) == -1) {
302 if (!slack_node_rw_failed) {
303 ALOGE("Can't obtain scaling governor.");
304 slack_node_rw_failed = 1;
305 }
306
307 return;
308 }
309
310 if ((strlen(governor) == strlen("msm-dcvs")) &&
311 (strncmp(governor, "msm-dcvs", strlen("msm-dcvs")) == 0)) {
312 if (on && (saved_interactive_mode == -1 || saved_interactive_mode == 0)) {
313 /* Display turned on. Restore if possible. */
314 if (saved_dcvs_cpu0_slack_max != -1) {
315 snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_max);
316
317 if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) {
318 if (!slack_node_rw_failed) {
319 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE);
320 }
321
322 rc = 1;
323 }
324 }
325
326 if (saved_dcvs_cpu0_slack_max != -1) {
327 snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_min);
328
329 if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) {
330 if (!slack_node_rw_failed) {
331 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE);
332 }
333
334 rc = 1;
335 }
336 }
337
338 if (saved_mpdecision_slack_max != -1) {
339 snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_max);
340
341 if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) {
342 if (!slack_node_rw_failed) {
343 ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE);
344 }
345
346 rc = 1;
347 }
348 }
349
350 if (saved_mpdecision_slack_min != -1) {
351 snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_min);
352
353 if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) {
354 if (!slack_node_rw_failed) {
355 ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE);
356 }
357
358 rc = 1;
359 }
360 }
361 } else if (!on && saved_interactive_mode == 1){
362 /* Display turned off. */
363 if (sysfs_read(DCVS_CPU0_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) {
364 if (!slack_node_rw_failed) {
365 ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MAX_NODE);
366 }
367
368 rc = 1;
369 } else {
370 saved_dcvs_cpu0_slack_max = atoi(tmp_str);
371 }
372
373 if (sysfs_read(DCVS_CPU0_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) {
374 if (!slack_node_rw_failed) {
375 ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MIN_NODE);
376 }
377
378 rc = 1;
379 } else {
380 saved_dcvs_cpu0_slack_min = atoi(tmp_str);
381 }
382
383 if (sysfs_read(MPDECISION_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) {
384 if (!slack_node_rw_failed) {
385 ALOGE("Failed to read from %s", MPDECISION_SLACK_MAX_NODE);
386 }
387
388 rc = 1;
389 } else {
390 saved_mpdecision_slack_max = atoi(tmp_str);
391 }
392
393 if (sysfs_read(MPDECISION_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) {
394 if(!slack_node_rw_failed) {
395 ALOGE("Failed to read from %s", MPDECISION_SLACK_MIN_NODE);
396 }
397
398 rc = 1;
399 } else {
400 saved_mpdecision_slack_min = atoi(tmp_str);
401 }
402
403 /* Write new values. */
404 if (saved_dcvs_cpu0_slack_max != -1) {
405 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_max);
406
407 if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) {
408 if (!slack_node_rw_failed) {
409 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE);
410 }
411
412 rc = 1;
413 }
414 }
415
416 if (saved_dcvs_cpu0_slack_max != -1) {
417 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_min);
418
419 if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) {
420 if(!slack_node_rw_failed) {
421 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE);
422 }
423
424 rc = 1;
425 }
426 }
427
428 if (saved_mpdecision_slack_max != -1) {
429 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_max);
430
431 if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) {
432 if(!slack_node_rw_failed) {
433 ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE);
434 }
435
436 rc = 1;
437 }
438 }
439
440 if (saved_mpdecision_slack_min != -1) {
441 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_min);
442
443 if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) {
444 if(!slack_node_rw_failed) {
445 ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE);
446 }
447
448 rc = 1;
449 }
450 }
451 }
452
453 slack_node_rw_failed = rc;
454 }
455
456 saved_interactive_mode = !!on;
Anurag Singh9cec85b2012-09-04 21:46:24 -0700457}
458
459struct power_module HAL_MODULE_INFO_SYM = {
460 .common = {
461 .tag = HARDWARE_MODULE_TAG,
462 .module_api_version = POWER_MODULE_API_VERSION_0_2,
463 .hal_api_version = HARDWARE_HAL_API_VERSION,
464 .id = POWER_HARDWARE_MODULE_ID,
465 .name = "QCOM Power HAL",
466 .author = "Qualcomm",
467 .methods = &power_module_methods,
468 },
469
470 .init = power_init,
471 .powerHint = power_hint,
472 .setInteractive = set_interactive,
473};