From 2d5b93ca9f08ce41f7b3eade03e150eedc6f153c Mon Sep 17 00:00:00 2001 From: Anurag Singh Date: Tue, 4 Sep 2012 21:46:24 -0700 Subject: [PATCH] common: Add platform-specific power libraries. Add platform-specific power HAL libraries to the built product. Change-Id: Ie6e8d3af3f5f83618bc66ccc2f420d209c53ce12 --- common.mk | 4 + power/Android.mk | 12 ++ power/metadata-defs.h | 46 ++++++ power/metadata-parser.c | 93 +++++++++++++ power/power.c | 301 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 456 insertions(+) create mode 100644 power/Android.mk create mode 100644 power/metadata-defs.h create mode 100644 power/metadata-parser.c create mode 100644 power/power.c diff --git a/common.mk b/common.mk index 641728a8..96517a3e 100755 --- a/common.mk +++ b/common.mk @@ -284,6 +284,9 @@ LIBQDUTILS := libqdutils #LIBQDMETADATA LIBQDMETADATA := libqdMetaData +#LIBPOWER +LIBPOWER := power.qcom + #LLVM for RenderScript #use qcom LLVM $(call inherit-product, external/llvm/llvm-select.mk) @@ -478,6 +481,7 @@ PRODUCT_PACKAGES += $(LIBPERFLOCK) PRODUCT_PACKAGES += $(LIBQCOMUI) PRODUCT_PACKAGES += $(LIBQDUTILS) PRODUCT_PACKAGES += $(LIBQDMETADATA) +PRODUCT_PACKAGES += $(LIBPOWER) PRODUCT_PACKAGES += $(LOC_API) PRODUCT_PACKAGES += $(MEDIA_PROFILES) PRODUCT_PACKAGES += $(MM_AUDIO) diff --git a/power/Android.mk b/power/Android.mk new file mode 100644 index 00000000..bfac991c --- /dev/null +++ b/power/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH := $(call my-dir) + +# HAL module implemenation stored in +# hw/..so +include $(CLEAR_VARS) + +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/hw +LOCAL_SHARED_LIBRARIES := liblog libcutils libdl +LOCAL_SRC_FILES := power.c metadata-parser.c +LOCAL_MODULE := power.qcom +LOCAL_MODULE_TAGS := optional +include $(BUILD_SHARED_LIBRARY) diff --git a/power/metadata-defs.h b/power/metadata-defs.h new file mode 100644 index 00000000..2240a5b7 --- /dev/null +++ b/power/metadata-defs.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#define ATTRIBUTE_VALUE_DELIM ('=') +#define ATTRIBUTE_STRING_DELIM (";") + +#define METADATA_PARSING_ERR (-1) +#define METADATA_PARSING_CONTINUE (0) +#define METADATA_PARSING_DONE (1) + +#define MIN(x,y) (((x)>(y))?(y):(x)) + +struct video_encode_metadata_t { + int state; +}; + +int parse_metadata(char *metadata, char **metadata_saveptr, + char *attribute, int attribute_size, char *value, int value_size); +int parse_video_metadata(char *metadata, + struct video_encode_metadata_t *video_encode_metadata); diff --git a/power/metadata-parser.c b/power/metadata-parser.c new file mode 100644 index 00000000..b6e739e5 --- /dev/null +++ b/power/metadata-parser.c @@ -0,0 +1,93 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include "metadata-defs.h" + +int parse_metadata(char *metadata, char **metadata_saveptr, + char *attribute, int attribute_size, char *value, int value_size) +{ + char *attribute_string; + char *attribute_value_delim; + unsigned int bytes_to_copy; + + attribute_string = strtok_r(metadata, ATTRIBUTE_STRING_DELIM, + metadata_saveptr); + + if (attribute_string == NULL) + return METADATA_PARSING_DONE; + + attribute[0] = value[0] = '\0'; + + if ((attribute_value_delim = strchr(attribute_string, + ATTRIBUTE_VALUE_DELIM)) != NULL) { + bytes_to_copy = MIN((attribute_value_delim - attribute_string), + attribute_size - 1); + strncpy(attribute, attribute_string, + bytes_to_copy); + attribute[bytes_to_copy] = '\0'; + + bytes_to_copy = MIN(strlen(attribute_string) - strlen(attribute) - 1, + value_size - 1); + strncpy(value, attribute_value_delim + 1, + bytes_to_copy); + value[bytes_to_copy] = '\0'; + } + + return METADATA_PARSING_CONTINUE; +} + +int parse_video_metadata(char *metadata, + struct video_encode_metadata_t *video_encode_metadata) +{ + char attribute[1024], value[1024], *saveptr; + char *temp_metadata = metadata; + int parsing_status; + + while ((parsing_status = parse_metadata(temp_metadata, &saveptr, + attribute, sizeof(attribute), value, sizeof(value))) == METADATA_PARSING_CONTINUE) { + if (strlen(attribute) == strlen("state") && + (strncmp(attribute, "state", strlen("state")) == 0)) { + if (strlen(value) > 0) { + video_encode_metadata->state = atoi(value); + } + } + + temp_metadata = NULL; + } + + if (parsing_status == METADATA_PARSING_ERR) + return -1; + + return 0; +} + diff --git a/power/power.c b/power/power.c new file mode 100644 index 00000000..a25a26a7 --- /dev/null +++ b/power/power.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "QCOM PowerHAL" + +#include +#include + +#include +#include + +#include "metadata-defs.h" + +#define SCALING_GOVERNOR_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor" +#define ONDEMAND_PATH "/sys/devices/system/cpu/cpufreq/ondemand/" +#define ONDEMAND_IO_BUSY_PATH "/sys/devices/system/cpu/cpufreq/ondemand/io_is_busy" +#define ONDEMAND_SAMPLING_DOWN_PATH "/sys/devices/system/cpu/cpufreq/ondemand/sampling_down_factor" + +static int (*perf_vote_turnoff_ondemand_io_busy)(int vote); +static int perf_vote_ondemand_io_busy_unavailable; +static int (*perf_vote_lower_ondemand_sdf)(int vote); +static int perf_vote_ondemand_sdf_unavailable; +static void *qcopt_handle; +static int qcopt_handle_unavailable; +static int saved_ondemand_sampling_down_factor = 4; +static int saved_ondemand_io_is_busy_status = 1; + +static void *get_qcopt_handle() +{ + if (qcopt_handle_unavailable) { + return NULL; + } + + if (!qcopt_handle) { + char qcopt_lib_path[PATH_MAX] = {0}; + dlerror(); + + if (property_get("ro.vendor.extension_library", qcopt_lib_path, + NULL) != 0) { + if((qcopt_handle = dlopen(qcopt_lib_path, RTLD_NOW)) == NULL) { + qcopt_handle_unavailable = 1; + ALOGE("Unable to open %s: %s\n", qcopt_lib_path, + dlerror()); + } + } else { + qcopt_handle_unavailable = 1; + ALOGE("Property ro.vendor.extension_library does not exist."); + } + } + + return qcopt_handle; +} + +static int sysfs_read(char *path, char *s, int num_bytes) +{ + char buf[80]; + int count; + int ret = 0; + int fd = open(path, O_RDONLY); + + if (fd < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error opening %s: %s\n", path, buf); + + return -1; + } + + if ((count = read(fd, s, num_bytes - 1)) < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", path, buf); + + ret = -1; + } else { + s[count] = '\0'; + } + + close(fd); + + return ret; +} + +static int sysfs_write(char *path, char *s) +{ + char buf[80]; + int len; + int ret = 0; + int fd = open(path, O_WRONLY); + + if (fd < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error opening %s: %s\n", path, buf); + return -1 ; + } + + len = write(fd, s, strlen(s)); + if (len < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", path, buf); + + ret = -1; + } + + close(fd); + + return ret; +} + +static struct hw_module_methods_t power_module_methods = { + .open = NULL, +}; + +void power_init(struct power_module *module) +{ + ALOGI("QCOM power HAL initing."); +} + +static int get_scaling_governor(char governor[], int size) { + if (sysfs_read(SCALING_GOVERNOR_PATH, governor, + size) == -1) { + // Can't obtain the scaling governor. Return. + return -1; + } else { + // Strip newline at the end. + int len = strlen(governor); + + len--; + + while (len >= 0 && (governor[len] == '\n' || governor[len] == '\r')) + governor[len--] = '\0'; + } + + return 0; +} + +static void process_video_encode_hint(void *metadata) +{ + void *handle; + char governor[80]; + struct video_encode_metadata_t video_encode_metadata; + + if (get_scaling_governor(governor, sizeof(governor)) == -1) { + ALOGE("Can't obtain scaling governor."); + + return; + } + + /* Initialize encode metadata struct fields. */ + memset(&video_encode_metadata, 0, sizeof(video_encode_metadata)); + video_encode_metadata.state = -1; + + if (metadata) { + if (parse_video_metadata((char *)metadata, &video_encode_metadata) == + -1) { + ALOGE("Error occurred while parsing metadata."); + return; + } + } else { + return; + } + + if ((handle = get_qcopt_handle())) { + if (video_encode_metadata.state == 1) { + if ((strlen(governor) == strlen("ondemand")) && + (strncmp(governor, "ondemand", strlen("ondemand")) == 0)) { + if (!perf_vote_ondemand_io_busy_unavailable) { + perf_vote_turnoff_ondemand_io_busy = dlsym(handle, + "perf_vote_turnoff_ondemand_io_busy"); + + if (perf_vote_turnoff_ondemand_io_busy) { + /* Vote to turn io_is_busy off */ + perf_vote_turnoff_ondemand_io_busy(1); + } else { + perf_vote_ondemand_io_busy_unavailable = 1; + ALOGE("Can't set io_busy_status."); + } + } + + if (!perf_vote_ondemand_sdf_unavailable) { + perf_vote_lower_ondemand_sdf = dlsym(handle, + "perf_vote_lower_ondemand_sdf"); + + if (perf_vote_lower_ondemand_sdf) { + perf_vote_lower_ondemand_sdf(1); + } else { + perf_vote_ondemand_sdf_unavailable = 1; + ALOGE("Can't set sampling_down_factor."); + } + } + } + } else if (video_encode_metadata.state == 0) { + if ((strlen(governor) == strlen("ondemand")) && + (strncmp(governor, "ondemand", strlen("ondemand")) == 0)) { + if (!perf_vote_ondemand_io_busy_unavailable) { + perf_vote_turnoff_ondemand_io_busy = dlsym(handle, + "perf_vote_turnoff_ondemand_io_busy"); + + if (perf_vote_turnoff_ondemand_io_busy) { + /* Remove vote to turn io_busy off. */ + perf_vote_turnoff_ondemand_io_busy(0); + } else { + perf_vote_ondemand_io_busy_unavailable = 1; + ALOGE("Can't set io_busy_status."); + } + } + + if (!perf_vote_ondemand_sdf_unavailable) { + perf_vote_lower_ondemand_sdf = dlsym(handle, + "perf_vote_lower_ondemand_sdf"); + + if (perf_vote_lower_ondemand_sdf) { + /* Remove vote to lower sampling down factor. */ + perf_vote_lower_ondemand_sdf(0); + } else { + perf_vote_ondemand_sdf_unavailable = 1; + ALOGE("Can't set sampling_down_factor."); + } + } + } + } + } +} + +int __attribute__ ((weak)) power_hint_override(struct power_module *module, power_hint_t hint, + void *data) +{ + return -1; +} + +static void power_hint(struct power_module *module, power_hint_t hint, + void *data) +{ + /* Check if this hint has been overridden. */ + if (power_hint_override(module, hint, data) == 0) { + /* The power_hint has been handled. We can skip the rest. */ + return; + } + + switch(hint) { + case POWER_HINT_VSYNC: + break; + case POWER_HINT_INTERACTION: + break; + case POWER_HINT_VIDEO_ENCODE: + process_video_encode_hint(data); + break; + } +} + +void set_interactive(struct power_module *module, int on) +{ +} + +struct power_module HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = POWER_MODULE_API_VERSION_0_2, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = POWER_HARDWARE_MODULE_ID, + .name = "QCOM Power HAL", + .author = "Qualcomm", + .methods = &power_module_methods, + }, + + .init = power_init, + .powerHint = power_hint, + .setInteractive = set_interactive, +};