From 311ead102a3f18507285ff27647049cb3d5e1489 Mon Sep 17 00:00:00 2001 From: Bruno Martins Date: Thu, 19 Jul 2018 23:53:01 +0100 Subject: [PATCH] sdm710-common: Import QCOM power HAL Change-Id: Ia4fe5dd8e0240955b7d2199e3e1d6743138b511d Signed-off-by: SamarV-121 --- power/Android.mk | 28 +++ power/hint-data.c | 46 ++++ power/hint-data.h | 61 +++++ power/list.c | 135 +++++++++++ power/list.h | 41 ++++ power/metadata-defs.h | 54 +++++ power/metadata-parser.c | 118 ++++++++++ power/performance.h | 265 +++++++++++++++++++++ power/power-710.c | 224 ++++++++++++++++++ power/power-common.h | 44 ++++ power/power.c | 495 +++++++++++++++++++++++++++++++++++++++ power/powerhintparser.c | 171 ++++++++++++++ power/powerhintparser.h | 48 ++++ power/utils.c | 320 +++++++++++++++++++++++++ power/utils.h | 46 ++++ rootdir/etc/init.qcom.rc | 2 + sdm710.mk | 4 + 17 files changed, 2102 insertions(+) create mode 100644 power/Android.mk create mode 100644 power/hint-data.c create mode 100644 power/hint-data.h create mode 100644 power/list.c create mode 100644 power/list.h create mode 100644 power/metadata-defs.h create mode 100644 power/metadata-parser.c create mode 100644 power/performance.h create mode 100644 power/power-710.c create mode 100644 power/power-common.h create mode 100644 power/power.c create mode 100644 power/powerhintparser.c create mode 100644 power/powerhintparser.h create mode 100644 power/utils.c create mode 100644 power/utils.h diff --git a/power/Android.mk b/power/Android.mk new file mode 100644 index 0000000..49b7583 --- /dev/null +++ b/power/Android.mk @@ -0,0 +1,28 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := power.qcom +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := \ + hint-data.c \ + list.c \ + metadata-parser.c \ + power-710.c \ + power.c \ + powerhintparser.c \ + utils.c + +LOCAL_C_INCLUDES := external/libxml2/include \ + external/icu/icu4c/source/common + +LOCAL_SHARED_LIBRARIES := liblog libcutils libdl libxml2 + +LOCAL_HEADER_LIBRARIES := generated_kernel_headers + +LOCAL_CFLAGS += -Werror -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -DINTERACTION_BOOST + +include $(BUILD_SHARED_LIBRARY) diff --git a/power/hint-data.c b/power/hint-data.c new file mode 100644 index 0000000..f621dd7 --- /dev/null +++ b/power/hint-data.c @@ -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 The Linux Foundation 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 "hint-data.h" + +int hint_compare(struct hint_data* first_hint, struct hint_data* other_hint) { + if (first_hint == other_hint) { + return 0; + } else if ((first_hint && other_hint) && (first_hint->hint_id == other_hint->hint_id)) { + return 0; + } else { + return 1; + } +} + +void hint_dump(struct hint_data* hint) { + ALOGI("hint_id: %lu", hint->hint_id); +} diff --git a/power/hint-data.h b/power/hint-data.h new file mode 100644 index 0000000..22b98d9 --- /dev/null +++ b/power/hint-data.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012, 2013, 2015, 2017, 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 The Linux Foundation 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. + */ + +/* Default use-case hint IDs */ +#define DEFAULT_VIDEO_ENCODE_HINT_ID (0x0A00) +#define DEFAULT_VIDEO_DECODE_HINT_ID (0x0B00) +#define DISPLAY_STATE_HINT_ID (0x0C00) +#define DISPLAY_STATE_HINT_ID_2 (0x0D00) +#define CAM_PREVIEW_HINT_ID (0x0E00) +#define SUSTAINED_PERF_HINT_ID (0x0F00) +#define VR_MODE_HINT_ID (0x1000) +#define VR_MODE_SUSTAINED_PERF_HINT_ID (0x1001) +#define INTERACTION_HINT_ID (0x1A00) + +#define AOSP_DELTA (0x1200) + +#define VSYNC_HINT AOSP_DELTA + POWER_HINT_VSYNC +#define INTERACTION_HINT AOSP_DELTA + POWER_HINT_INTERACTION +#define VIDEO_DECODE_HINT AOSP_DELTA + POWER_HINT_VIDEO_DECODE +#define VIDEO_ENCODE_HINT AOSP_DELTA + POWER_HINT_VIDEO_ENCODE +#define LOW_POWER_HINT AOSP_DELTA + POWER_HINT_LOW_POWER +#define SUSTAINED_PERF_HINT AOSP_DELTA + POWER_HINT_SUSTAINED_PERFORMANCE +#define VR_MODE_HINT AOSP_DELTA + POWER_HINT_VR_MODE +#define LAUNCH_HINT AOSP_DELTA + POWER_HINT_LAUNCH +#define DISABLE_TOUCH_HINT AOSP_DELTA + POWER_HINT_DISABLE_TOUCH + +#define VR_MODE_SUSTAINED_PERF_HINT (0x1301) + +struct hint_data { + unsigned long hint_id; /* This is our key. */ + unsigned long perflock_handle; +}; + +int hint_compare(struct hint_data* first_hint, struct hint_data* other_hint); +void hint_dump(struct hint_data* hint); diff --git a/power/list.c b/power/list.c new file mode 100644 index 0000000..b3b987c --- /dev/null +++ b/power/list.c @@ -0,0 +1,135 @@ +/* + * 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 The Linux Foundation 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 "list.h" + +int init_list_head(struct list_node* head) { + if (head == NULL) return -1; + + memset(head, 0, sizeof(*head)); + + return 0; +} + +struct list_node* add_list_node(struct list_node* head, void* data) { + /* Create a new list_node. And put 'data' into it. */ + struct list_node* new_node; + + if (head == NULL) { + return NULL; + } + + if (!(new_node = malloc(sizeof(struct list_node)))) { + return NULL; + } + + new_node->data = data; + new_node->next = head->next; + new_node->compare = head->compare; + new_node->dump = head->dump; + head->next = new_node; + + return new_node; +} + +int is_list_empty(struct list_node* head) { + return (head == NULL || head->next == NULL); +} + +/* + * Delink and de-allocate 'node'. + */ +int remove_list_node(struct list_node* head, struct list_node* del_node) { + struct list_node* current_node; + struct list_node* saved_node; + + if (head == NULL || head->next == NULL) { + return -1; + } + + current_node = head->next; + saved_node = head; + + while (current_node && current_node != del_node) { + saved_node = current_node; + current_node = current_node->next; + } + + if (saved_node) { + if (current_node) { + saved_node->next = current_node->next; + } else { + /* Node not found. */ + return -1; + } + } + + if (del_node) { + free(del_node); + } + + return 0; +} + +void dump_list(struct list_node* head) { + struct list_node* current_node = head; + + if (head == NULL) return; + + ALOGV("List:\n"); + + while ((current_node = current_node->next)) { + if (current_node->dump) { + current_node->dump(current_node->data); + } + } +} + +struct list_node* find_node(struct list_node* head, void* comparison_data) { + struct list_node* current_node = head; + + if (head == NULL) return NULL; + + while ((current_node = current_node->next)) { + if (current_node->compare) { + if (current_node->compare(current_node->data, comparison_data) == 0) { + /* Match found. Return current_node. */ + return current_node; + } + } + } + + /* No match found. */ + return NULL; +} diff --git a/power/list.h b/power/list.h new file mode 100644 index 0000000..6e81d98 --- /dev/null +++ b/power/list.h @@ -0,0 +1,41 @@ +/* + * 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 The Linux Foundation 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. + */ + +struct list_node { + struct list_node* next; + void* data; + int (*compare)(void* data1, void* data2); + void (*dump)(void* data); +}; + +int init_list_head(struct list_node* head); +struct list_node* add_list_node(struct list_node* head, void* data); +int remove_list_node(struct list_node* head, struct list_node* del_node); +void dump_list(struct list_node* head); +struct list_node* find_node(struct list_node* head, void* comparison_data); diff --git a/power/metadata-defs.h b/power/metadata-defs.h new file mode 100644 index 0000000..bbc5fbf --- /dev/null +++ b/power/metadata-defs.h @@ -0,0 +1,54 @@ +/* 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 The Linux Foundation 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 hint_id; + int state; +}; + +struct video_decode_metadata_t { + int hint_id; + int state; +}; + +int parse_metadata(char* metadata, char** metadata_saveptr, char* attribute, + unsigned int attribute_size, char* value, unsigned int value_size); +int parse_video_encode_metadata(char* metadata, + struct video_encode_metadata_t* video_encode_metadata); +int parse_video_decode_metadata(char* metadata, + struct video_decode_metadata_t* video_decode_metadata); diff --git a/power/metadata-parser.c b/power/metadata-parser.c new file mode 100644 index 0000000..4a8bc30 --- /dev/null +++ b/power/metadata-parser.c @@ -0,0 +1,118 @@ +/* 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 The Linux Foundation 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, + unsigned int attribute_size, char* value, unsigned 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) { + unsigned int attribute_len = (unsigned int)(attribute_value_delim - attribute_string); + /* copy only attribute len + NUL character, or as much as can be fit */ + bytes_to_copy = MIN(attribute_len + 1, attribute_size); + + strlcpy(attribute, attribute_string, bytes_to_copy); + strlcpy(value, attribute_value_delim + 1, value_size); + } + + return METADATA_PARSING_CONTINUE; +} + +int parse_video_encode_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("hint_id") && + (strncmp(attribute, "hint_id", strlen("hint_id")) == 0)) { + if (strlen(value) > 0) { + video_encode_metadata->hint_id = atoi(value); + } + } + + 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; +} + +int parse_video_decode_metadata(char* metadata, + struct video_decode_metadata_t* video_decode_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("hint_id") && + (strncmp(attribute, "hint_id", strlen("hint_id")) == 0)) { + if (strlen(value) > 0) { + video_decode_metadata->hint_id = atoi(value); + } + } + + if (strlen(attribute) == strlen("state") && + (strncmp(attribute, "state", strlen("state")) == 0)) { + if (strlen(value) > 0) { + video_decode_metadata->state = atoi(value); + } + } + + temp_metadata = NULL; + } + + if (parsing_status == METADATA_PARSING_ERR) return -1; + + return 0; +} diff --git a/power/performance.h b/power/performance.h new file mode 100644 index 0000000..5deddd4 --- /dev/null +++ b/power/performance.h @@ -0,0 +1,265 @@ +/* Copyright (c) 2012, 2014, 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 The Linux Foundation 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. + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define FAILED -1 +#define SUCCESS 0 +#define INDEFINITE_DURATION 0 + +/* Hints sent to perf HAL from power HAL + * These have to be kept in sync with Perf HAL side definitions + */ +#define VENDOR_HINT_DISPLAY_OFF 0x00001040 +#define VENDOR_HINT_DISPLAY_ON 0x00001041 + +enum SCREEN_DISPLAY_TYPE { + DISPLAY_OFF = 0x00FF, +}; + +enum PWR_CLSP_TYPE { + ALL_CPUS_PWR_CLPS_DIS = 0x101, +}; + +/* For CPUx min freq, the leftmost byte + * represents the CPU and the + * rightmost byte represents the frequency + * All intermediate frequencies on the + * device are supported. The hex value + * passed into PerfLock will be multiplied + * by 10^5. This frequency or the next + * highest frequency available will be set + * + * For example, if 1.4 Ghz is required on + * CPU0, use 0x20E + * + * If the highest available frequency + * on the device is required, use + * CPUx_MIN_FREQ_TURBO_MAX + * where x represents the CPU + */ +enum CPU0_MIN_FREQ_LVL { + CPU0_MIN_FREQ_NONTURBO_MAX = 0x20A, + CPU0_MIN_FREQ_TURBO_MAX = 0x2FE, +}; + +enum CPU1_MIN_FREQ_LVL { + CPU1_MIN_FREQ_NONTURBO_MAX = 0x30A, + CPU1_MIN_FREQ_TURBO_MAX = 0x3FE, +}; + +enum CPU2_MIN_FREQ_LVL { + CPU2_MIN_FREQ_NONTURBO_MAX = 0x40A, + CPU2_MIN_FREQ_TURBO_MAX = 0x4FE, +}; + +enum CPU3_MIN_FREQ_LVL { + CPU3_MIN_FREQ_NONTURBO_MAX = 0x50A, + CPU3_MIN_FREQ_TURBO_MAX = 0x5FE, +}; + +enum CPU0_MAX_FREQ_LVL { + CPU0_MAX_FREQ_NONTURBO_MAX = 0x150A, +}; + +enum CPU1_MAX_FREQ_LVL { + CPU1_MAX_FREQ_NONTURBO_MAX = 0x160A, +}; + +enum CPU2_MAX_FREQ_LVL { + CPU2_MAX_FREQ_NONTURBO_MAX = 0x170A, +}; + +enum CPU3_MAX_FREQ_LVL { + CPU3_MAX_FREQ_NONTURBO_MAX = 0x180A, +}; + +enum MIN_CPUS_ONLINE_LVL { + CPUS_ONLINE_MIN_2 = 0x702, + CPUS_ONLINE_MIN_3 = 0x703, + CPUS_ONLINE_MIN_4 = 0x704, + CPUS_ONLINE_MPD_OVERRIDE = 0x777, + CPUS_ONLINE_MAX = 0x7FF, +}; + +enum MAX_CPUS_ONLINE_LVL { + CPUS_ONLINE_MAX_LIMIT_1 = 0x8FE, + CPUS_ONLINE_MAX_LIMIT_2 = 0x8FD, + CPUS_ONLINE_MAX_LIMIT_3 = 0x8FC, + CPUS_ONLINE_MAX_LIMIT_4 = 0x8FB, + CPUS_ONLINE_MAX_LIMIT_MAX = 0x8FB, +}; + +enum SAMPLING_RATE_LVL { + MS_500 = 0xBCD, + MS_50 = 0xBFA, + MS_20 = 0xBFD, +}; + +enum ONDEMAND_IO_BUSY_LVL { + IO_BUSY_OFF = 0xC00, + IO_BUSY_ON = 0xC01, +}; + +enum ONDEMAND_SAMPLING_DOWN_FACTOR_LVL { + SAMPLING_DOWN_FACTOR_1 = 0xD01, + SAMPLING_DOWN_FACTOR_4 = 0xD04, +}; + +enum INTERACTIVE_TIMER_RATE_LVL { + TR_MS_500 = 0xECD, + TR_MS_100 = 0xEF5, + TR_MS_50 = 0xEFA, + TR_MS_30 = 0xEFC, + TR_MS_20 = 0xEFD, +}; + +/* This timer rate applicable to cpu0 + across 8939 series chipset */ +enum INTERACTIVE_TIMER_RATE_LVL_CPU0_8939 { + TR_MS_CPU0_500 = 0x30CD, + TR_MS_CPU0_100 = 0x30F5, + TR_MS_CPU0_50 = 0x30FA, + TR_MS_CPU0_30 = 0x30FC, + TR_MS_CPU0_20 = 0x30FD, +}; + +/* This timer rate applicable to cpu4 + across 8939 series chipset */ +enum INTERACTIVE_TIMER_RATE_LVL_CPU4_8939 { + TR_MS_CPU4_500 = 0x3BCD, + TR_MS_CPU4_100 = 0x3BF5, + TR_MS_CPU4_50 = 0x3BFA, + TR_MS_CPU4_30 = 0x3BFC, + TR_MS_CPU4_20 = 0x3BFD, +}; + +/* This timer rate applicable to big.little arch */ +enum INTERACTIVE_TIMER_RATE_LVL_BIG_LITTLE { + BIG_LITTLE_TR_MS_100 = 0x64, + BIG_LITTLE_TR_MS_50 = 0x32, + BIG_LITTLE_TR_MS_40 = 0x28, + BIG_LITTLE_TR_MS_30 = 0x1E, + BIG_LITTLE_TR_MS_20 = 0x14, +}; + +/* INTERACTIVE opcodes */ +enum INTERACTIVE_OPCODES { + INT_OP_CLUSTER0_TIMER_RATE = 0x41424000, + INT_OP_CLUSTER1_TIMER_RATE = 0x41424100, + INT_OP_CLUSTER0_USE_SCHED_LOAD = 0x41430000, + INT_OP_CLUSTER1_USE_SCHED_LOAD = 0x41430100, + INT_OP_CLUSTER0_USE_MIGRATION_NOTIF = 0x41434000, + INT_OP_CLUSTER1_USE_MIGRATION_NOTIF = 0x41434100, + INT_OP_NOTIFY_ON_MIGRATE = 0x4241C000 +}; + +enum INTERACTIVE_HISPEED_FREQ_LVL { + HS_FREQ_1026 = 0xF0A, +}; + +enum INTERACTIVE_HISPEED_LOAD_LVL { + HISPEED_LOAD_90 = 0x105A, +}; + +enum SYNC_FREQ_LVL { + SYNC_FREQ_300 = 0x1103, + SYNC_FREQ_600 = 0X1106, + SYNC_FREQ_384 = 0x1103, + SYNC_FREQ_NONTURBO_MAX = 0x110A, + SYNC_FREQ_TURBO = 0x110F, +}; + +enum OPTIMAL_FREQ_LVL { + OPTIMAL_FREQ_300 = 0x1203, + OPTIMAL_FREQ_600 = 0x1206, + OPTIMAL_FREQ_384 = 0x1203, + OPTIMAL_FREQ_NONTURBO_MAX = 0x120A, + OPTIMAL_FREQ_TURBO = 0x120F, +}; + +enum SCREEN_PWR_CLPS_LVL { + PWR_CLPS_DIS = 0x1300, + PWR_CLPS_ENA = 0x1301, +}; + +enum THREAD_MIGRATION_LVL { + THREAD_MIGRATION_SYNC_OFF = 0x1400, +}; + +enum INTERACTIVE_IO_BUSY_LVL { + INTERACTIVE_IO_BUSY_OFF = 0x1B00, + INTERACTIVE_IO_BUSY_ON = 0x1B01, +}; + +enum SCHED_BOOST_LVL { + SCHED_BOOST_ON = 0x1E01, +}; + +enum CPU4_MIN_FREQ_LVL { + CPU4_MIN_FREQ_NONTURBO_MAX = 0x1F0A, + CPU4_MIN_FREQ_TURBO_MAX = 0x1FFE, +}; + +enum CPU5_MIN_FREQ_LVL { + CPU5_MIN_FREQ_NONTURBO_MAX = 0x200A, + CPU5_MIN_FREQ_TURBO_MAX = 0x20FE, +}; + +enum CPU6_MIN_FREQ_LVL { + CPU6_MIN_FREQ_NONTURBO_MAX = 0x210A, + CPU6_MIN_FREQ_TURBO_MAX = 0x21FE, +}; + +enum CPU7_MIN_FREQ_LVL { + CPU7_MIN_FREQ_NONTURBO_MAX = 0x220A, + CPU7_MIN_FREQ_TURBO_MAX = 0x22FE, +}; + +enum CPU4_MAX_FREQ_LVL { + CPU4_MAX_FREQ_NONTURBO_MAX = 0x230A, +}; + +enum CPU5_MAX_FREQ_LVL { + CPU5_MAX_FREQ_NONTURBO_MAX = 0x240A, +}; + +enum CPU6_MAX_FREQ_LVL { + CPU6_MAX_FREQ_NONTURBO_MAX = 0x250A, +}; + +enum CPU7_MAX_FREQ_LVL { + CPU7_MAX_FREQ_NONTURBO_MAX = 0x260A, +}; + +#ifdef __cplusplus +} +#endif diff --git a/power/power-710.c b/power/power-710.c new file mode 100644 index 0000000..3a33e43 --- /dev/null +++ b/power/power-710.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2017, 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 The Linux Foundation 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 LOG_NIDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "QTI PowerHAL" +#include +#include +#include + +#include "hint-data.h" +#include "metadata-defs.h" +#include "performance.h" +#include "power-common.h" +#include "utils.h" + +#define CHECK_HANDLE(x) ((x) > 0) +#define NUM_PERF_MODES 3 + +typedef enum { + NORMAL_MODE = 0, + SUSTAINED_MODE = 1, + VR_MODE = 2, + VR_SUSTAINED_MODE = (SUSTAINED_MODE | VR_MODE), + INVALID_MODE = 0xFF +} perf_mode_type_t; + +typedef struct perf_mode { + perf_mode_type_t type; + int perf_hint_id; +} perf_mode_t; + +perf_mode_t perf_modes[NUM_PERF_MODES] = {{SUSTAINED_MODE, SUSTAINED_PERF_HINT}, + {VR_MODE, VR_MODE_HINT}, + {VR_SUSTAINED_MODE, VR_MODE_SUSTAINED_PERF_HINT}}; + +static pthread_mutex_t perf_mode_switch_lock = PTHREAD_MUTEX_INITIALIZER; +static int current_mode = NORMAL_MODE; + +static inline int get_perfd_hint_id(perf_mode_type_t type) { + int i; + for (i = 0; i < NUM_PERF_MODES; i++) { + if (perf_modes[i].type == type) { + ALOGD("Hint id is 0x%x for mode 0x%x", perf_modes[i].perf_hint_id, type); + return perf_modes[i].perf_hint_id; + } + } + ALOGD("Couldn't find the hint for mode 0x%x", type); + return 0; +} + +static int switch_mode(perf_mode_type_t mode) { + int hint_id = 0; + static int perfd_mode_handle = -1; + + // release existing mode if any + if (CHECK_HANDLE(perfd_mode_handle)) { + ALOGD("Releasing handle 0x%x", perfd_mode_handle); + release_request(perfd_mode_handle); + perfd_mode_handle = -1; + } + // switch to a perf mode + hint_id = get_perfd_hint_id(mode); + if (hint_id != 0) { + perfd_mode_handle = perf_hint_enable(hint_id, 0); + if (!CHECK_HANDLE(perfd_mode_handle)) { + ALOGE("Failed perf_hint_interaction for mode: 0x%x", mode); + return -1; + } + ALOGD("Acquired handle 0x%x", perfd_mode_handle); + } + return 0; +} + +static int process_perf_hint(void* data, perf_mode_type_t mode) { + pthread_mutex_lock(&perf_mode_switch_lock); + + // enable + if (data) { + ALOGI("Enable request for mode: 0x%x", mode); + // check if mode is current mode + if (current_mode & mode) { + pthread_mutex_unlock(&perf_mode_switch_lock); + ALOGD("Mode 0x%x already enabled", mode); + return HINT_HANDLED; + } + // enable requested mode + if (0 != switch_mode(current_mode | mode)) { + pthread_mutex_unlock(&perf_mode_switch_lock); + ALOGE("Couldn't enable mode 0x%x", mode); + return HINT_NONE; + } + current_mode |= mode; + ALOGI("Current mode is 0x%x", current_mode); + // disable + } else { + ALOGI("Disable request for mode: 0x%x", mode); + // check if mode is enabled + if (!(current_mode & mode)) { + pthread_mutex_unlock(&perf_mode_switch_lock); + ALOGD("Mode 0x%x already disabled", mode); + return HINT_HANDLED; + } + // disable requested mode + if (0 != switch_mode(current_mode & ~mode)) { + pthread_mutex_unlock(&perf_mode_switch_lock); + ALOGE("Couldn't disable mode 0x%x", mode); + return HINT_NONE; + } + current_mode &= ~mode; + ALOGI("Current mode is 0x%x", current_mode); + } + + pthread_mutex_unlock(&perf_mode_switch_lock); + return HINT_HANDLED; +} + +static int process_video_encode_hint(void* metadata) { + char governor[80]; + struct video_encode_metadata_t video_encode_metadata; + static int video_encode_handle = 0; + + if (!metadata) return HINT_NONE; + + if (get_scaling_governor(governor, sizeof(governor)) == -1) { + ALOGE("Can't obtain scaling governor."); + + return HINT_NONE; + } + + /* Initialize encode metadata struct fields */ + memset(&video_encode_metadata, 0, sizeof(struct video_encode_metadata_t)); + video_encode_metadata.state = -1; + + if (parse_video_encode_metadata((char*)metadata, &video_encode_metadata) == -1) { + ALOGE("Error occurred while parsing metadata."); + return HINT_NONE; + } + + if (video_encode_metadata.state == 1) { + if (is_interactive_governor(governor)) { + video_encode_handle = perf_hint_enable(VIDEO_ENCODE_HINT, 0); + ALOGI("Video encode hint start"); + return HINT_HANDLED; + } + } else if (video_encode_metadata.state == 0) { + if (is_interactive_governor(governor)) { + release_request(video_encode_handle); + ALOGI("Video Encode hint stop"); + return HINT_HANDLED; + } + } + return HINT_NONE; +} + +/* Declare function before use */ +void interaction(int duration, int num_args, int opt_list[]); + +int power_hint_override(struct power_module* module, power_hint_t hint, void* data) { + int ret_val = HINT_NONE; + switch (hint) { + case POWER_HINT_VIDEO_ENCODE: + ret_val = process_video_encode_hint(data); + break; + case POWER_HINT_SUSTAINED_PERFORMANCE: + ret_val = process_perf_hint(data, SUSTAINED_MODE); + break; + case POWER_HINT_VR_MODE: + ret_val = process_perf_hint(data, VR_MODE); + break; + case POWER_HINT_INTERACTION: { + int resources[] = {0x40800100, 0x514}; + int duration = 100; + interaction(duration, sizeof(resources) / sizeof(resources[0]), resources); + ret_val = HINT_HANDLED; + } break; + default: + break; + } + return ret_val; +} + +int set_interactive_override(struct power_module* module, int on) { + static int set_i_count = 0; + + set_i_count++; + ALOGI("Got set_interactive hint on= %d, count= %d\n", on, set_i_count); + + return HINT_HANDLED; /* Don't excecute this code path, not in use */ +} diff --git a/power/power-common.h b/power/power-common.h new file mode 100644 index 0000000..f3d70d3 --- /dev/null +++ b/power/power-common.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013, 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 The Linux Foundation 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 NODE_MAX (64) + +#define SCALING_GOVERNOR_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor" +#define DCVS_CPU0_SLACK_MAX_NODE "/sys/module/msm_dcvs/cores/cpu0/slack_time_max_us" +#define DCVS_CPU0_SLACK_MIN_NODE "/sys/module/msm_dcvs/cores/cpu0/slack_time_min_us" +#define MPDECISION_SLACK_MAX_NODE "/sys/module/msm_mpdecision/slack_time_max_us" +#define MPDECISION_SLACK_MIN_NODE "/sys/module/msm_mpdecision/slack_time_min_us" +#define SCALING_MIN_FREQ "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq" +#define ONDEMAND_GOVERNOR "ondemand" +#define INTERACTIVE_GOVERNOR "interactive" +#define MSMDCVS_GOVERNOR "msm-dcvs" + +#define HINT_HANDLED (0) +#define HINT_NONE (-1) + +enum CPU_GOV_CHECK { CPU0 = 0, CPU1 = 1, CPU2 = 2, CPU3 = 3 }; diff --git a/power/power.c b/power/power.c new file mode 100644 index 0000000..08e5ccd --- /dev/null +++ b/power/power.c @@ -0,0 +1,495 @@ +/* + * Copyright (c) 2012-2017, 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 The Linux Foundation 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 LOG_NIDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "QCOM PowerHAL" +#include +#include +#include + +#include "hint-data.h" +#include "metadata-defs.h" +#include "performance.h" +#include "power-common.h" +#include "utils.h" + +static int saved_dcvs_cpu0_slack_max = -1; +static int saved_dcvs_cpu0_slack_min = -1; +static int saved_mpdecision_slack_max = -1; +static int saved_mpdecision_slack_min = -1; +static int saved_interactive_mode = -1; +static int slack_node_rw_failed = 0; +static int display_hint_sent; +int display_boost; + +static int power_device_open(const hw_module_t* module, const char* name, hw_device_t** device); + +static struct hw_module_methods_t power_module_methods = { + .open = power_device_open, +}; + +static void power_init(struct power_module* module) { + ALOGI("QCOM power HAL initing."); + + int fd; + char buf[10] = {0}; + + fd = open("/sys/devices/soc0/soc_id", O_RDONLY); + if (fd >= 0) { + if (read(fd, buf, sizeof(buf) - 1) == -1) { + ALOGW("Unable to read soc_id"); + } else { + int soc_id = atoi(buf); + if (soc_id == 194 || (soc_id >= 208 && soc_id <= 218) || soc_id == 178) { + display_boost = 1; + } + } + close(fd); + } +} + +static void process_video_decode_hint(void* metadata) { + char governor[80]; + struct video_decode_metadata_t video_decode_metadata; + + if (get_scaling_governor(governor, sizeof(governor)) == -1) { + ALOGE("Can't obtain scaling governor."); + + return; + } + + if (metadata) { + ALOGI("Processing video decode hint. Metadata: %s", (char*)metadata); + } + + /* Initialize encode metadata struct fields. */ + memset(&video_decode_metadata, 0, sizeof(struct video_decode_metadata_t)); + video_decode_metadata.state = -1; + video_decode_metadata.hint_id = DEFAULT_VIDEO_DECODE_HINT_ID; + + if (metadata) { + if (parse_video_decode_metadata((char*)metadata, &video_decode_metadata) == -1) { + ALOGE("Error occurred while parsing metadata."); + return; + } + } else { + return; + } + + if (video_decode_metadata.state == 1) { + if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && + (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { + int resource_values[] = {THREAD_MIGRATION_SYNC_OFF}; + + perform_hint_action(video_decode_metadata.hint_id, resource_values, + sizeof(resource_values) / sizeof(resource_values[0])); + } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { + int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026, + THREAD_MIGRATION_SYNC_OFF}; + + perform_hint_action(video_decode_metadata.hint_id, resource_values, + sizeof(resource_values) / sizeof(resource_values[0])); + } + } else if (video_decode_metadata.state == 0) { + if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && + (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { + } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { + undo_hint_action(video_decode_metadata.hint_id); + } + } +} + +static void process_video_encode_hint(void* metadata) { + 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(struct video_encode_metadata_t)); + video_encode_metadata.state = -1; + video_encode_metadata.hint_id = DEFAULT_VIDEO_ENCODE_HINT_ID; + + if (metadata) { + if (parse_video_encode_metadata((char*)metadata, &video_encode_metadata) == -1) { + ALOGE("Error occurred while parsing metadata."); + return; + } + } else { + return; + } + + if (video_encode_metadata.state == 1) { + if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && + (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { + int resource_values[] = {IO_BUSY_OFF, SAMPLING_DOWN_FACTOR_1, THREAD_MIGRATION_SYNC_OFF}; + + perform_hint_action(video_encode_metadata.hint_id, resource_values, + sizeof(resource_values) / sizeof(resource_values[0])); + } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { + int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026, + THREAD_MIGRATION_SYNC_OFF, INTERACTIVE_IO_BUSY_OFF}; + + perform_hint_action(video_encode_metadata.hint_id, resource_values, + sizeof(resource_values) / sizeof(resource_values[0])); + } + } else if (video_encode_metadata.state == 0) { + if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && + (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { + undo_hint_action(video_encode_metadata.hint_id); + } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { + undo_hint_action(video_encode_metadata.hint_id); + } + } +} + +int __attribute__((weak)) +power_hint_override(struct power_module* module, power_hint_t hint, void* data) { + return HINT_NONE; +} + +/* Declare function before use */ +void interaction(int duration, int num_args, int opt_list[]); + +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) == HINT_HANDLED) { + /* The power_hint has been handled. We can skip the rest. */ + return; + } + + switch (hint) { + case POWER_HINT_VSYNC: + break; + case POWER_HINT_SUSTAINED_PERFORMANCE: + ALOGI("Sustained perf power hint not handled in power_hint_override"); + break; + case POWER_HINT_VR_MODE: + ALOGI("VR mode power hint not handled in power_hint_override"); + break; + case POWER_HINT_INTERACTION: { + int resources[] = {0x702, 0x20F, 0x30F}; + int duration = 3000; + + interaction(duration, sizeof(resources) / sizeof(resources[0]), resources); + } break; + case POWER_HINT_VIDEO_ENCODE: + process_video_encode_hint(data); + break; + case POWER_HINT_VIDEO_DECODE: + process_video_decode_hint(data); + break; + default: + break; + } +} + +int __attribute__((weak)) set_interactive_override(struct power_module* module, int on) { + return HINT_NONE; +} + +void set_interactive(struct power_module* module, int on) { + char governor[80]; + char tmp_str[NODE_MAX]; + int rc = 0; + + if (!on) { + /* Send Display OFF hint to perf HAL */ + perf_hint_enable(VENDOR_HINT_DISPLAY_OFF, 0); + } else { + /* Send Display ON hint to perf HAL */ + perf_hint_enable(VENDOR_HINT_DISPLAY_ON, 0); + } + + if (set_interactive_override(module, on) == HINT_HANDLED) { + return; + } + + ALOGI("Got set_interactive hint"); + + if (get_scaling_governor(governor, sizeof(governor)) == -1) { + ALOGE("Can't obtain scaling governor."); + + return; + } + + if (!on) { + /* Display off. */ + if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && + (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { + int resource_values[] = {DISPLAY_OFF, MS_500, THREAD_MIGRATION_SYNC_OFF}; + + if (!display_hint_sent) { + perform_hint_action(DISPLAY_STATE_HINT_ID, resource_values, + sizeof(resource_values) / sizeof(resource_values[0])); + display_hint_sent = 1; + } + } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { + int resource_values[] = {TR_MS_50, THREAD_MIGRATION_SYNC_OFF}; + + if (!display_hint_sent) { + perform_hint_action(DISPLAY_STATE_HINT_ID, resource_values, + sizeof(resource_values) / sizeof(resource_values[0])); + display_hint_sent = 1; + } + } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) && + (strlen(governor) == strlen(MSMDCVS_GOVERNOR))) { + if (saved_interactive_mode == 1) { + /* Display turned off. */ + if (sysfs_read(DCVS_CPU0_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) { + if (!slack_node_rw_failed) { + ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MAX_NODE); + } + + rc = 1; + } else { + saved_dcvs_cpu0_slack_max = atoi(tmp_str); + } + + if (sysfs_read(DCVS_CPU0_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) { + if (!slack_node_rw_failed) { + ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MIN_NODE); + } + + rc = 1; + } else { + saved_dcvs_cpu0_slack_min = atoi(tmp_str); + } + + if (sysfs_read(MPDECISION_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) { + if (!slack_node_rw_failed) { + ALOGE("Failed to read from %s", MPDECISION_SLACK_MAX_NODE); + } + + rc = 1; + } else { + saved_mpdecision_slack_max = atoi(tmp_str); + } + + if (sysfs_read(MPDECISION_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) { + if (!slack_node_rw_failed) { + ALOGE("Failed to read from %s", MPDECISION_SLACK_MIN_NODE); + } + + rc = 1; + } else { + saved_mpdecision_slack_min = atoi(tmp_str); + } + + /* Write new values. */ + if (saved_dcvs_cpu0_slack_max != -1) { + snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_max); + + if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) { + if (!slack_node_rw_failed) { + ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE); + } + + rc = 1; + } + } + + if (saved_dcvs_cpu0_slack_min != -1) { + snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_min); + + if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) { + if (!slack_node_rw_failed) { + ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE); + } + + rc = 1; + } + } + + if (saved_mpdecision_slack_max != -1) { + snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_max); + + if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) { + if (!slack_node_rw_failed) { + ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE); + } + + rc = 1; + } + } + + if (saved_mpdecision_slack_min != -1) { + snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_min); + + if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) { + if (!slack_node_rw_failed) { + ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE); + } + + rc = 1; + } + } + } + + slack_node_rw_failed = rc; + } + } else { + /* Display on. */ + if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && + (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { + undo_hint_action(DISPLAY_STATE_HINT_ID); + display_hint_sent = 0; + } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { + undo_hint_action(DISPLAY_STATE_HINT_ID); + display_hint_sent = 0; + } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) && + (strlen(governor) == strlen(MSMDCVS_GOVERNOR))) { + if (saved_interactive_mode == -1 || saved_interactive_mode == 0) { + /* Display turned on. Restore if possible. */ + if (saved_dcvs_cpu0_slack_max != -1) { + snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_max); + + if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) { + if (!slack_node_rw_failed) { + ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE); + } + + rc = 1; + } + } + + if (saved_dcvs_cpu0_slack_min != -1) { + snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_min); + + if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) { + if (!slack_node_rw_failed) { + ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE); + } + + rc = 1; + } + } + + if (saved_mpdecision_slack_max != -1) { + snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_max); + + if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) { + if (!slack_node_rw_failed) { + ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE); + } + + rc = 1; + } + } + + if (saved_mpdecision_slack_min != -1) { + snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_min); + + if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) { + if (!slack_node_rw_failed) { + ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE); + } + + rc = 1; + } + } + } + + slack_node_rw_failed = rc; + } + } + + saved_interactive_mode = !!on; +} + +static int power_device_open(const hw_module_t* module, const char* name, hw_device_t** device) { + int status = -EINVAL; + if (module && name && device) { + if (!strcmp(name, POWER_HARDWARE_MODULE_ID)) { + power_module_t* dev = (power_module_t*)malloc(sizeof(*dev)); + + if (dev) { + memset(dev, 0, sizeof(*dev)); + + if (dev) { + /* initialize the fields */ + dev->common.module_api_version = POWER_MODULE_API_VERSION_0_2; + dev->common.tag = HARDWARE_DEVICE_TAG; + dev->init = power_init; + dev->powerHint = power_hint; + dev->setInteractive = set_interactive; + /* At the moment we support 0.2 APIs */ + dev->setFeature = NULL, + dev->get_number_of_platform_modes = NULL; + dev->get_platform_low_power_stats = NULL; + dev->get_voter_list = NULL; + *device = (hw_device_t*)dev; + status = 0; + } else { + status = -ENOMEM; + } + } else { + status = -ENOMEM; + } + } + } + + return status; +} + +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, +}; diff --git a/power/powerhintparser.c b/power/powerhintparser.c new file mode 100644 index 0000000..648a219 --- /dev/null +++ b/power/powerhintparser.c @@ -0,0 +1,171 @@ +/* Copyright (c) 2016-2017, 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 The Linux Foundation 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 LOG_TAG "QCOM PowerHAL" + +#include "powerhintparser.h" +#include +#include +#include +#include +#include +#include + +int parsePowerhintXML() { + xmlDocPtr doc; + xmlNodePtr currNode; + const char *opcode_str, *value_str, *type_str; + int opcode = 0, value = 0, type = 0; + int numParams = 0; + static int hintCount; + + if (access(POWERHINT_XML, F_OK) < 0) { + return -1; + } + + doc = xmlReadFile(POWERHINT_XML, "UTF-8", XML_PARSE_RECOVER); + if (!doc) { + ALOGE("Document not parsed successfully"); + return -1; + } + + currNode = xmlDocGetRootElement(doc); + if (!currNode) { + ALOGE("Empty document"); + xmlFreeDoc(doc); + xmlCleanupParser(); + return -1; + } + + // Confirm the root-element of the tree + if (xmlStrcmp(currNode->name, BAD_CAST "Powerhint")) { + ALOGE("document of the wrong type, root node != root"); + xmlFreeDoc(doc); + xmlCleanupParser(); + return -1; + } + + currNode = currNode->xmlChildrenNode; + + for (; currNode != NULL; currNode = currNode->next) { + if (currNode->type != XML_ELEMENT_NODE) continue; + + xmlNodePtr node = currNode; + + if (hintCount == MAX_HINT) { + ALOGE("Number of hints exceeded the max count of %d\n", MAX_HINT); + break; + } + + if (!xmlStrcmp(node->name, BAD_CAST "Hint")) { + if (xmlHasProp(node, BAD_CAST "type")) { + type_str = (const char*)xmlGetProp(node, BAD_CAST "type"); + if (type_str == NULL) { + ALOGE("xmlGetProp failed on type"); + xmlFreeDoc(doc); + xmlCleanupParser(); + return -1; + } + type = strtol(type_str, NULL, 16); + } + + node = node->children; + while (node != NULL) { + if (!xmlStrcmp(node->name, BAD_CAST "Resource")) { + if (xmlHasProp(node, BAD_CAST "opcode")) { + opcode_str = (const char*)xmlGetProp(node, BAD_CAST "opcode"); + if (opcode_str == NULL) { + ALOGE("xmlGetProp failed on opcode"); + xmlFreeDoc(doc); + xmlCleanupParser(); + return -1; + } + opcode = strtol(opcode_str, NULL, 16); + } + if (xmlHasProp(node, BAD_CAST "value")) { + value_str = (const char*)xmlGetProp(node, BAD_CAST "value"); + if (value_str == NULL) { + ALOGE("xmlGetProp failed on value"); + xmlFreeDoc(doc); + xmlCleanupParser(); + return -1; + } + value = strtol(value_str, NULL, 16); + } + if (opcode > 0) { + if (numParams < (MAX_PARAM - 1)) { + powerhint[hintCount].paramList[numParams++] = opcode; + powerhint[hintCount].paramList[numParams++] = value; + } else { + ALOGE("Maximum parameters exceeded for Hint ID %x\n", type); + opcode = value = 0; + break; + } + } + + opcode = value = 0; + } + node = node->next; + } + powerhint[hintCount].type = type; + powerhint[hintCount].numParams = numParams; + numParams = 0; + } + hintCount++; + } + + xmlFreeDoc(doc); + xmlCleanupParser(); + return 0; +} + +int* getPowerhint(int hint_id, int* params) { + int* result = NULL; + + if (!hint_id) return result; + + ALOGI("Powerhal hint received=%x\n", hint_id); + + if (!powerhint[0].numParams) { + parsePowerhintXML(); + } + + for (int i = 0; i < MAX_HINT; i++) { + if (hint_id == powerhint[i].type) { + *params = powerhint[i].numParams; + result = powerhint[i].paramList; + break; + } + } + + /*for (int j = 0; j < *params; j++) + ALOGI("Powerhal resource again%x = \n", result[j]);*/ + + return result; +} diff --git a/power/powerhintparser.h b/power/powerhintparser.h new file mode 100644 index 0000000..48a9bb4 --- /dev/null +++ b/power/powerhintparser.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2016, 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 The Linux Foundation 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. + * + */ + +#ifndef __POWERHINTPARSER__ +#define __POWERHINTPARSER__ + +#define POWERHINT_XML "/vendor/etc/powerhint.xml" +#define MAX_HINT 6 +#define MAX_PARAM 30 + +typedef struct perflock_param_t { + int type; + int numParams; + int paramList[MAX_PARAM]; // static limit on number of hints - 15 +} perflock_param_t; + +static perflock_param_t powerhint[MAX_HINT]; + +int parsePowerhintXML(); +int* getPowerhint(int, int*); + +#endif /* __POWERHINTPARSER__ */ diff --git a/power/utils.c b/power/utils.c new file mode 100644 index 0000000..5153701 --- /dev/null +++ b/power/utils.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2012-2013,2015-2017, 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 The Linux Foundation 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 LOG_NIDEBUG 0 + +#include +#include +#include +#include +#include + +#include "hint-data.h" +#include "list.h" +#include "power-common.h" +#include "utils.h" + +#define LOG_TAG "QCOM PowerHAL" +#include + +char scaling_gov_path[4][80] = {"sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", + "sys/devices/system/cpu/cpu1/cpufreq/scaling_governor", + "sys/devices/system/cpu/cpu2/cpufreq/scaling_governor", + "sys/devices/system/cpu/cpu3/cpufreq/scaling_governor"}; + +#define PERF_HAL_PATH "libqti-perfd-client.so" +static void* qcopt_handle; +static int (*perf_lock_acq)(unsigned long handle, int duration, int list[], int numArgs); +static int (*perf_lock_rel)(unsigned long handle); +static int (*perf_hint)(int, char*, int, int); +static struct list_node active_hint_list_head; + +static void* get_qcopt_handle() { + void* handle = NULL; + + dlerror(); + + handle = dlopen(PERF_HAL_PATH, RTLD_NOW); + if (!handle) { + ALOGE("Unable to open %s: %s\n", PERF_HAL_PATH, dlerror()); + } + + return handle; +} + +static void __attribute__((constructor)) initialize(void) { + qcopt_handle = get_qcopt_handle(); + + if (!qcopt_handle) { + ALOGE("Failed to get qcopt handle.\n"); + } else { + /* + * qc-opt handle obtained. Get the perflock acquire/release + * function pointers. + */ + perf_lock_acq = dlsym(qcopt_handle, "perf_lock_acq"); + + if (!perf_lock_acq) { + ALOGE("Unable to get perf_lock_acq function handle.\n"); + } + + perf_lock_rel = dlsym(qcopt_handle, "perf_lock_rel"); + + if (!perf_lock_rel) { + ALOGE("Unable to get perf_lock_rel function handle.\n"); + } + + perf_hint = dlsym(qcopt_handle, "perf_hint"); + + if (!perf_hint) { + ALOGE("Unable to get perf_hint function handle.\n"); + } + } +} + +static void __attribute__((destructor)) cleanup(void) { + if (qcopt_handle) { + if (dlclose(qcopt_handle)) ALOGE("Error occurred while closing qc-opt library."); + } +} + +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; +} + +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; +} + +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; +} + +int get_scaling_governor_check_cores(char governor[], int size, int core_num) { + if (sysfs_read(scaling_gov_path[core_num], governor, size) == -1) { + // Can't obtain the scaling governor. Return. + return -1; + } + + // Strip newline at the end. + int len = strlen(governor); + len--; + while (len >= 0 && (governor[len] == '\n' || governor[len] == '\r')) governor[len--] = '\0'; + + return 0; +} + +int is_interactive_governor(char* governor) { + if (strncmp(governor, INTERACTIVE_GOVERNOR, (strlen(INTERACTIVE_GOVERNOR) + 1)) == 0) return 1; + return 0; +} + +void interaction(int duration, int num_args, int opt_list[]) { +#ifdef INTERACTION_BOOST + static int lock_handle = 0; + + if (duration < 0 || num_args < 1 || opt_list[0] == 0) return; + + if (qcopt_handle) { + if (perf_lock_acq) { + lock_handle = perf_lock_acq(lock_handle, duration, opt_list, num_args); + if (lock_handle == -1) ALOGE("Failed to acquire lock."); + } + } +#endif +} + +int interaction_with_handle(int lock_handle, int duration, int num_args, int opt_list[]) { + if (duration < 0 || num_args < 1 || opt_list[0] == 0) return 0; + + if (qcopt_handle) { + if (perf_lock_acq) { + lock_handle = perf_lock_acq(lock_handle, duration, opt_list, num_args); + if (lock_handle == -1) ALOGE("Failed to acquire lock."); + } + } + return lock_handle; +} + +// this is interaction_with_handle using perf_hint instead of +// perf_lock_acq +int perf_hint_enable(int hint_id, int duration) { + int lock_handle = 0; + + if (duration < 0) return 0; + + if (qcopt_handle) { + if (perf_hint) { + lock_handle = perf_hint(hint_id, NULL, duration, -1); + if (lock_handle == -1) ALOGE("Failed to acquire lock."); + } + } + return lock_handle; +} + +void release_request(int lock_handle) { + if (qcopt_handle && perf_lock_rel) perf_lock_rel(lock_handle); +} + +void perform_hint_action(int hint_id, int resource_values[], int num_resources) { + if (qcopt_handle) { + if (perf_lock_acq) { + /* Acquire an indefinite lock for the requested resources. */ + int lock_handle = perf_lock_acq(0, 0, resource_values, num_resources); + + if (lock_handle == -1) { + ALOGE("Failed to acquire lock."); + } else { + /* Add this handle to our internal hint-list. */ + struct hint_data* new_hint = (struct hint_data*)malloc(sizeof(struct hint_data)); + + if (new_hint) { + if (!active_hint_list_head.compare) { + active_hint_list_head.compare = (int (*)(void*, void*))hint_compare; + active_hint_list_head.dump = (void (*)(void*))hint_dump; + } + + new_hint->hint_id = hint_id; + new_hint->perflock_handle = lock_handle; + + if (add_list_node(&active_hint_list_head, new_hint) == NULL) { + free(new_hint); + /* Can't keep track of this lock. Release it. */ + if (perf_lock_rel) perf_lock_rel(lock_handle); + + ALOGE("Failed to process hint."); + } + } else { + /* Can't keep track of this lock. Release it. */ + if (perf_lock_rel) perf_lock_rel(lock_handle); + + ALOGE("Failed to process hint."); + } + } + } + } +} + +void undo_hint_action(int hint_id) { + if (qcopt_handle) { + if (perf_lock_rel) { + /* Get hint-data associated with this hint-id */ + struct list_node* found_node; + struct hint_data temp_hint_data = {.hint_id = hint_id}; + + found_node = find_node(&active_hint_list_head, &temp_hint_data); + + if (found_node) { + /* Release this lock. */ + struct hint_data* found_hint_data = (struct hint_data*)(found_node->data); + + if (found_hint_data) { + if (perf_lock_rel(found_hint_data->perflock_handle) == -1) + ALOGE("Perflock release failed."); + } + + if (found_node->data) { + /* We can free the hint-data for this node. */ + free(found_node->data); + } + + remove_list_node(&active_hint_list_head, found_node); + } else { + ALOGE("Invalid hint ID."); + } + } + } +} + +/* + * Used to release initial lock holding + * two cores online when the display is on + */ +void undo_initial_hint_action() { + if (qcopt_handle) { + if (perf_lock_rel) { + perf_lock_rel(1); + } + } +} diff --git a/power/utils.h b/power/utils.h new file mode 100644 index 0000000..b2d5d2c --- /dev/null +++ b/power/utils.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2012-2013,2015-2017, 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 The Linux Foundation 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 + +int sysfs_read(char* path, char* s, int num_bytes); +int sysfs_write(char* path, char* s); +int get_scaling_governor(char governor[], int size); +int get_scaling_governor_check_cores(char governor[], int size, int core_num); +int is_interactive_governor(char*); + +void vote_ondemand_io_busy_off(); +void unvote_ondemand_io_busy_off(); +void vote_ondemand_sdf_low(); +void unvote_ondemand_sdf_low(); +void perform_hint_action(int hint_id, int resource_values[], int num_resources); +void undo_hint_action(int hint_id); +void release_request(int lock_handle); +int interaction_with_handle(int lock_handle, int duration, int num_args, int opt_list[]); +int perf_hint_enable(int hint_id, int duration); diff --git a/rootdir/etc/init.qcom.rc b/rootdir/etc/init.qcom.rc index a33e466..0369db6 100644 --- a/rootdir/etc/init.qcom.rc +++ b/rootdir/etc/init.qcom.rc @@ -1,6 +1,8 @@ on init mount none /system/etc/audio_policy_configuration.xml /vendor/etc/audio/audio_policy_configuration.xml bind mount none /dev/null /vendor/etc/init/vendor.oppo.hardware.biometrics.fingerprint@2.1-service.rc bind + mount none /system/lib64/hw/power.qcom.so /vendor/lib64/hw/power.qcom.so bind + mount none /system/lib/hw/power.qcom.so /vendor/lib/hw/power.qcom.so bind on boot mount none /system/overlay/BluetoothResCommon.apk /vendor/overlay/BluetoothResCommon.apk bind diff --git a/sdm710.mk b/sdm710.mk index b9c5e78..b4310d5 100644 --- a/sdm710.mk +++ b/sdm710.mk @@ -82,6 +82,10 @@ PRODUCT_PACKAGES += \ PRODUCT_COPY_FILES += \ frameworks/native/data/etc/handheld_core_hardware.xml:system/etc/permissions/handheld_core_hardware.xml +# Power +PRODUCT_PACKAGES += \ + power.qcom + # QTI PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/configs/qti_whitelist.xml:system/etc/sysconfig/qti_whitelist.xml \