David Ng | ee7c4c5 | 2018-03-22 23:49:12 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2012-2017, 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 <string.h> |
| 34 | #include <sys/types.h> |
| 35 | #include <sys/stat.h> |
| 36 | #include <fcntl.h> |
| 37 | #include <dlfcn.h> |
| 38 | #include <stdlib.h> |
Vinay Verma | 2fd9c12 | 2018-04-29 14:08:30 +0530 | [diff] [blame] | 39 | #include <unistd.h> |
David Ng | ee7c4c5 | 2018-03-22 23:49:12 -0700 | [diff] [blame] | 40 | |
| 41 | #define LOG_TAG "QTI PowerHAL" |
| 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 | static int saved_dcvs_cpu0_slack_max = -1; |
| 53 | static int saved_dcvs_cpu0_slack_min = -1; |
| 54 | static int saved_mpdecision_slack_max = -1; |
| 55 | static int saved_mpdecision_slack_min = -1; |
| 56 | static int saved_interactive_mode = -1; |
| 57 | static int slack_node_rw_failed = 0; |
| 58 | static int display_hint_sent; |
| 59 | int display_boost; |
| 60 | |
| 61 | static int power_device_open(const hw_module_t* module, const char* name, |
| 62 | hw_device_t** device); |
| 63 | |
| 64 | static struct hw_module_methods_t power_module_methods = { |
| 65 | .open = power_device_open, |
| 66 | }; |
| 67 | |
| 68 | static void power_init(struct power_module *module) |
| 69 | { |
| 70 | ALOGI("QTI power HAL initing."); |
| 71 | |
| 72 | int fd; |
| 73 | char buf[10] = {0}; |
| 74 | |
| 75 | fd = open("/sys/devices/soc0/soc_id", O_RDONLY); |
| 76 | if (fd >= 0) { |
| 77 | if (read(fd, buf, sizeof(buf) - 1) == -1) { |
| 78 | ALOGW("Unable to read soc_id"); |
| 79 | } else { |
| 80 | int soc_id = atoi(buf); |
| 81 | if (soc_id == 194 || (soc_id >= 208 && soc_id <= 218) || soc_id == 178) { |
| 82 | display_boost = 1; |
| 83 | } |
| 84 | } |
| 85 | close(fd); |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | static void process_video_decode_hint(void *metadata) |
| 90 | { |
| 91 | char governor[80]; |
| 92 | struct video_decode_metadata_t video_decode_metadata; |
| 93 | |
| 94 | if (get_scaling_governor(governor, sizeof(governor)) == -1) { |
| 95 | ALOGE("Can't obtain scaling governor."); |
| 96 | |
| 97 | return; |
| 98 | } |
| 99 | |
| 100 | if (metadata) { |
| 101 | ALOGI("Processing video decode hint. Metadata: %s", (char *)metadata); |
| 102 | } |
| 103 | |
| 104 | /* Initialize encode metadata struct fields. */ |
| 105 | memset(&video_decode_metadata, 0, sizeof(struct video_decode_metadata_t)); |
| 106 | video_decode_metadata.state = -1; |
| 107 | video_decode_metadata.hint_id = DEFAULT_VIDEO_DECODE_HINT_ID; |
| 108 | |
| 109 | if (metadata) { |
| 110 | if (parse_video_decode_metadata((char *)metadata, &video_decode_metadata) == |
| 111 | -1) { |
| 112 | ALOGE("Error occurred while parsing metadata."); |
| 113 | return; |
| 114 | } |
| 115 | } else { |
| 116 | return; |
| 117 | } |
| 118 | |
| 119 | if (video_decode_metadata.state == 1) { |
| 120 | if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && |
| 121 | (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { |
| 122 | int resource_values[] = {THREAD_MIGRATION_SYNC_OFF}; |
| 123 | |
| 124 | perform_hint_action(video_decode_metadata.hint_id, |
| 125 | resource_values, sizeof(resource_values)/sizeof(resource_values[0])); |
| 126 | } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && |
| 127 | (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { |
| 128 | int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026, THREAD_MIGRATION_SYNC_OFF}; |
| 129 | |
| 130 | perform_hint_action(video_decode_metadata.hint_id, |
| 131 | resource_values, sizeof(resource_values)/sizeof(resource_values[0])); |
| 132 | } |
| 133 | } else if (video_decode_metadata.state == 0) { |
| 134 | if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && |
| 135 | (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { |
| 136 | } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && |
| 137 | (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { |
| 138 | undo_hint_action(video_decode_metadata.hint_id); |
| 139 | } |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | static void process_video_encode_hint(void *metadata) |
| 144 | { |
| 145 | char governor[80]; |
| 146 | struct video_encode_metadata_t video_encode_metadata; |
| 147 | |
| 148 | if (get_scaling_governor(governor, sizeof(governor)) == -1) { |
| 149 | ALOGE("Can't obtain scaling governor."); |
| 150 | |
| 151 | return; |
| 152 | } |
| 153 | |
| 154 | /* Initialize encode metadata struct fields. */ |
| 155 | memset(&video_encode_metadata, 0, sizeof(struct video_encode_metadata_t)); |
| 156 | video_encode_metadata.state = -1; |
| 157 | video_encode_metadata.hint_id = DEFAULT_VIDEO_ENCODE_HINT_ID; |
| 158 | |
| 159 | if (metadata) { |
| 160 | if (parse_video_encode_metadata((char *)metadata, &video_encode_metadata) == |
| 161 | -1) { |
| 162 | ALOGE("Error occurred while parsing metadata."); |
| 163 | return; |
| 164 | } |
| 165 | } else { |
| 166 | return; |
| 167 | } |
| 168 | |
| 169 | if (video_encode_metadata.state == 1) { |
| 170 | if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && |
| 171 | (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { |
| 172 | int resource_values[] = {IO_BUSY_OFF, SAMPLING_DOWN_FACTOR_1, THREAD_MIGRATION_SYNC_OFF}; |
| 173 | |
| 174 | perform_hint_action(video_encode_metadata.hint_id, |
| 175 | resource_values, sizeof(resource_values)/sizeof(resource_values[0])); |
| 176 | } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && |
| 177 | (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { |
| 178 | int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026, THREAD_MIGRATION_SYNC_OFF, |
| 179 | INTERACTIVE_IO_BUSY_OFF}; |
| 180 | |
| 181 | perform_hint_action(video_encode_metadata.hint_id, |
| 182 | resource_values, sizeof(resource_values)/sizeof(resource_values[0])); |
| 183 | } |
| 184 | } else if (video_encode_metadata.state == 0) { |
| 185 | if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && |
| 186 | (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { |
| 187 | undo_hint_action(video_encode_metadata.hint_id); |
| 188 | } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && |
| 189 | (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { |
| 190 | undo_hint_action(video_encode_metadata.hint_id); |
| 191 | } |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | int __attribute__ ((weak)) power_hint_override(struct power_module *module, power_hint_t hint, |
| 196 | void *data) |
| 197 | { |
| 198 | return HINT_NONE; |
| 199 | } |
| 200 | |
| 201 | /* Declare function before use */ |
| 202 | void interaction(int duration, int num_args, int opt_list[]); |
| 203 | |
| 204 | static void power_hint(struct power_module *module, power_hint_t hint, |
| 205 | void *data) |
| 206 | { |
| 207 | /* Check if this hint has been overridden. */ |
| 208 | if (power_hint_override(module, hint, data) == HINT_HANDLED) { |
| 209 | /* The power_hint has been handled. We can skip the rest. */ |
| 210 | return; |
| 211 | } |
| 212 | |
| 213 | switch(hint) { |
| 214 | case POWER_HINT_VSYNC: |
| 215 | break; |
| 216 | case POWER_HINT_SUSTAINED_PERFORMANCE: |
| 217 | ALOGI("Sustained perf power hint not handled in power_hint_override"); |
| 218 | break; |
| 219 | case POWER_HINT_VR_MODE: |
| 220 | ALOGI("VR mode power hint not handled in power_hint_override"); |
| 221 | break; |
| 222 | case POWER_HINT_INTERACTION: |
| 223 | { |
| 224 | int resources[] = {0x702, 0x20F, 0x30F}; |
| 225 | int duration = 3000; |
| 226 | |
| 227 | interaction(duration, sizeof(resources)/sizeof(resources[0]), resources); |
| 228 | } |
| 229 | break; |
| 230 | case POWER_HINT_VIDEO_ENCODE: |
| 231 | process_video_encode_hint(data); |
| 232 | break; |
| 233 | case POWER_HINT_VIDEO_DECODE: |
| 234 | process_video_decode_hint(data); |
| 235 | break; |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | int __attribute__ ((weak)) set_interactive_override(struct power_module *module, int on) |
| 240 | { |
| 241 | return HINT_NONE; |
| 242 | } |
| 243 | |
| 244 | void set_interactive(struct power_module *module, int on) |
| 245 | { |
| 246 | char governor[80]; |
| 247 | char tmp_str[NODE_MAX]; |
| 248 | struct video_encode_metadata_t video_encode_metadata; |
| 249 | int rc = 0; |
| 250 | |
| 251 | if (!on) { |
| 252 | /* Send Display OFF hint to perf HAL */ |
| 253 | perf_hint_enable(VENDOR_HINT_DISPLAY_OFF, 0); |
| 254 | } else { |
| 255 | /* Send Display ON hint to perf HAL */ |
| 256 | perf_hint_enable(VENDOR_HINT_DISPLAY_ON, 0); |
| 257 | } |
| 258 | |
| 259 | if (set_interactive_override(module, on) == HINT_HANDLED) { |
| 260 | return; |
| 261 | } |
| 262 | |
| 263 | ALOGI("Got set_interactive hint"); |
| 264 | |
| 265 | if (get_scaling_governor(governor, sizeof(governor)) == -1) { |
| 266 | ALOGE("Can't obtain scaling governor."); |
| 267 | |
| 268 | return; |
| 269 | } |
| 270 | |
| 271 | if (!on) { |
| 272 | /* Display off. */ |
| 273 | if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && |
| 274 | (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { |
| 275 | int resource_values[] = {DISPLAY_OFF, MS_500, THREAD_MIGRATION_SYNC_OFF}; |
| 276 | |
| 277 | if (!display_hint_sent) { |
| 278 | perform_hint_action(DISPLAY_STATE_HINT_ID, |
| 279 | resource_values, sizeof(resource_values)/sizeof(resource_values[0])); |
| 280 | display_hint_sent = 1; |
| 281 | } |
| 282 | } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && |
| 283 | (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { |
| 284 | int resource_values[] = {TR_MS_50, THREAD_MIGRATION_SYNC_OFF}; |
| 285 | |
| 286 | if (!display_hint_sent) { |
| 287 | perform_hint_action(DISPLAY_STATE_HINT_ID, |
| 288 | resource_values, sizeof(resource_values)/sizeof(resource_values[0])); |
| 289 | display_hint_sent = 1; |
| 290 | } |
| 291 | } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) && |
| 292 | (strlen(governor) == strlen(MSMDCVS_GOVERNOR))) { |
| 293 | if (saved_interactive_mode == 1){ |
| 294 | /* Display turned off. */ |
| 295 | if (sysfs_read(DCVS_CPU0_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) { |
| 296 | if (!slack_node_rw_failed) { |
| 297 | ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MAX_NODE); |
| 298 | } |
| 299 | |
| 300 | rc = 1; |
| 301 | } else { |
| 302 | saved_dcvs_cpu0_slack_max = atoi(tmp_str); |
| 303 | } |
| 304 | |
| 305 | if (sysfs_read(DCVS_CPU0_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) { |
| 306 | if (!slack_node_rw_failed) { |
| 307 | ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MIN_NODE); |
| 308 | } |
| 309 | |
| 310 | rc = 1; |
| 311 | } else { |
| 312 | saved_dcvs_cpu0_slack_min = atoi(tmp_str); |
| 313 | } |
| 314 | |
| 315 | if (sysfs_read(MPDECISION_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) { |
| 316 | if (!slack_node_rw_failed) { |
| 317 | ALOGE("Failed to read from %s", MPDECISION_SLACK_MAX_NODE); |
| 318 | } |
| 319 | |
| 320 | rc = 1; |
| 321 | } else { |
| 322 | saved_mpdecision_slack_max = atoi(tmp_str); |
| 323 | } |
| 324 | |
| 325 | if (sysfs_read(MPDECISION_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) { |
| 326 | if(!slack_node_rw_failed) { |
| 327 | ALOGE("Failed to read from %s", MPDECISION_SLACK_MIN_NODE); |
| 328 | } |
| 329 | |
| 330 | rc = 1; |
| 331 | } else { |
| 332 | saved_mpdecision_slack_min = atoi(tmp_str); |
| 333 | } |
| 334 | |
| 335 | /* Write new values. */ |
| 336 | if (saved_dcvs_cpu0_slack_max != -1) { |
| 337 | snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_max); |
| 338 | |
| 339 | if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) { |
| 340 | if (!slack_node_rw_failed) { |
| 341 | ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE); |
| 342 | } |
| 343 | |
| 344 | rc = 1; |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | if (saved_dcvs_cpu0_slack_min != -1) { |
| 349 | snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_min); |
| 350 | |
| 351 | if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) { |
| 352 | if(!slack_node_rw_failed) { |
| 353 | ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE); |
| 354 | } |
| 355 | |
| 356 | rc = 1; |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | if (saved_mpdecision_slack_max != -1) { |
| 361 | snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_max); |
| 362 | |
| 363 | if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) { |
| 364 | if(!slack_node_rw_failed) { |
| 365 | ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE); |
| 366 | } |
| 367 | |
| 368 | rc = 1; |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | if (saved_mpdecision_slack_min != -1) { |
| 373 | snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_min); |
| 374 | |
| 375 | if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) { |
| 376 | if(!slack_node_rw_failed) { |
| 377 | ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE); |
| 378 | } |
| 379 | |
| 380 | rc = 1; |
| 381 | } |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | slack_node_rw_failed = rc; |
| 386 | } |
| 387 | } else { |
| 388 | /* Display on. */ |
| 389 | if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && |
| 390 | (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { |
| 391 | undo_hint_action(DISPLAY_STATE_HINT_ID); |
| 392 | display_hint_sent = 0; |
| 393 | } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && |
| 394 | (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { |
| 395 | undo_hint_action(DISPLAY_STATE_HINT_ID); |
| 396 | display_hint_sent = 0; |
| 397 | } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) && |
| 398 | (strlen(governor) == strlen(MSMDCVS_GOVERNOR))) { |
| 399 | if (saved_interactive_mode == -1 || saved_interactive_mode == 0) { |
| 400 | /* Display turned on. Restore if possible. */ |
| 401 | if (saved_dcvs_cpu0_slack_max != -1) { |
| 402 | snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_max); |
| 403 | |
| 404 | if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) { |
| 405 | if (!slack_node_rw_failed) { |
| 406 | ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE); |
| 407 | } |
| 408 | |
| 409 | rc = 1; |
| 410 | } |
| 411 | } |
| 412 | |
| 413 | if (saved_dcvs_cpu0_slack_min != -1) { |
| 414 | snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_min); |
| 415 | |
| 416 | if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) { |
| 417 | if (!slack_node_rw_failed) { |
| 418 | ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE); |
| 419 | } |
| 420 | |
| 421 | rc = 1; |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | if (saved_mpdecision_slack_max != -1) { |
| 426 | snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_max); |
| 427 | |
| 428 | if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) { |
| 429 | if (!slack_node_rw_failed) { |
| 430 | ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE); |
| 431 | } |
| 432 | |
| 433 | rc = 1; |
| 434 | } |
| 435 | } |
| 436 | |
| 437 | if (saved_mpdecision_slack_min != -1) { |
| 438 | snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_min); |
| 439 | |
| 440 | if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) { |
| 441 | if (!slack_node_rw_failed) { |
| 442 | ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE); |
| 443 | } |
| 444 | |
| 445 | rc = 1; |
| 446 | } |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | slack_node_rw_failed = rc; |
| 451 | } |
| 452 | } |
| 453 | |
| 454 | saved_interactive_mode = !!on; |
| 455 | } |
| 456 | |
| 457 | static int power_device_open(const hw_module_t* module, const char* name, |
| 458 | hw_device_t** device) |
| 459 | { |
| 460 | int status = -EINVAL; |
| 461 | if (module && name && device) { |
| 462 | if (!strcmp(name, POWER_HARDWARE_MODULE_ID)) { |
| 463 | power_module_t *dev = (power_module_t *)malloc(sizeof(*dev)); |
| 464 | |
| 465 | if(dev) { |
| 466 | memset(dev, 0, sizeof(*dev)); |
| 467 | |
| 468 | if(dev) { |
| 469 | /* initialize the fields */ |
| 470 | dev->common.module_api_version = POWER_MODULE_API_VERSION_0_2; |
| 471 | dev->common.tag = HARDWARE_DEVICE_TAG; |
| 472 | dev->init = power_init; |
| 473 | dev->powerHint = power_hint; |
| 474 | dev->setInteractive = set_interactive; |
| 475 | /* At the moment we support 0.2 APIs */ |
| 476 | dev->setFeature = NULL, |
| 477 | dev->get_number_of_platform_modes = NULL, |
| 478 | dev->get_platform_low_power_stats = NULL, |
| 479 | dev->get_voter_list = NULL, |
| 480 | *device = (hw_device_t*)dev; |
| 481 | status = 0; |
| 482 | } else { |
| 483 | status = -ENOMEM; |
| 484 | } |
| 485 | } |
| 486 | else { |
| 487 | status = -ENOMEM; |
| 488 | } |
| 489 | } |
| 490 | } |
| 491 | |
| 492 | return status; |
| 493 | } |
| 494 | |
| 495 | struct power_module HAL_MODULE_INFO_SYM = { |
| 496 | .common = { |
| 497 | .tag = HARDWARE_MODULE_TAG, |
| 498 | .module_api_version = POWER_MODULE_API_VERSION_0_2, |
| 499 | .hal_api_version = HARDWARE_HAL_API_VERSION, |
| 500 | .id = POWER_HARDWARE_MODULE_ID, |
| 501 | .name = "QTI Power HAL", |
| 502 | .author = "QTI", |
| 503 | .methods = &power_module_methods, |
| 504 | }, |
| 505 | |
| 506 | .init = power_init, |
| 507 | .powerHint = power_hint, |
| 508 | .setInteractive = set_interactive, |
| 509 | }; |