diff --git a/gts3l.mk b/gts3l.mk index dc23371..ca7c42a 100755 --- a/gts3l.mk +++ b/gts3l.mk @@ -288,7 +288,7 @@ PRODUCT_PACKAGES += \ # Power PRODUCT_PACKAGES += \ - android.hardware.power-service-qti \ + android.hardware.power@1.1-service.gts3l \ vendor.qti.hardware.perf@1.0 PRODUCT_COPY_FILES += \ diff --git a/manifest.xml b/manifest.xml index 2e51aab..8709af2 100644 --- a/manifest.xml +++ b/manifest.xml @@ -164,6 +164,15 @@ default + + android.hardware.power + hwbinder + 1.1 + + IPower + default + + android.hardware.renderscript passthrough diff --git a/power/Android.bp b/power/Android.bp new file mode 100644 index 0000000..1de9635 --- /dev/null +++ b/power/Android.bp @@ -0,0 +1,46 @@ +// Copyright (C) 2017 The Android Open Source Project +// Copyright (C) 2018 The LineageOS Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_binary { + + relative_install_path: "hw", + proprietary: true, + owner: "qcom", + + name: "android.hardware.power@1.1-service.gts3l", + init_rc: ["android.hardware.power@1.1-service.gts3l.rc"], + srcs: [ + "service.cpp", + "Power.cpp", + "power-helper.c", + "metadata-parser.c", + "utils.c", + "list.c", + "hint-data.c", + "power-8996.c" + ], + + shared_libs: [ + "libbase", + "libcutils", + "libhidlbase", + "liblog", + "libutils", + "android.hardware.power@1.1", + ], + + header_libs: ["libhardware_headers"], + +} diff --git a/power/Power.cpp b/power/Power.cpp new file mode 100644 index 0000000..74af4a9 --- /dev/null +++ b/power/Power.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "android.hardware.power@1.1-service.gts3l" + +#include +#include + +#include + +#include "Power.h" +#include "power-helper.h" + +/* RPM runs at 19.2Mhz. Divide by 19200 for msec */ +#define RPM_CLK 19200 + +extern struct stat_pair rpm_stat_map[]; + +namespace android { +namespace hardware { +namespace power { +namespace V1_1 { +namespace implementation { + +using ::android::hardware::power::V1_0::Feature; +using ::android::hardware::power::V1_0::PowerHint; +using ::android::hardware::power::V1_0::PowerStatePlatformSleepState; +using ::android::hardware::power::V1_0::Status; +using ::android::hardware::power::V1_1::PowerStateSubsystem; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; + +Power::Power() { + power_init(); +} + +// Methods from ::android::hardware::power::V1_0::IPower follow. +Return Power::setInteractive(bool interactive) { + power_set_interactive(interactive ? 1 : 0); + return Void(); +} + +Return Power::powerHint(PowerHint hint, int32_t data) { + if (android::base::GetProperty("init.svc.vendor.perfd", "") != "running") { + ALOGW("perfd is not started"); + return Void(); + } + power_hint(static_cast(hint), data ? (&data) : NULL); + return Void(); +} + +Return Power::setFeature(Feature /*feature*/, bool /*activate*/) { + return Void(); +} + +Return Power::getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) { + + hidl_vec states; + uint64_t stats[platform_param_id::PLATFORM_PARAM_COUNT] = {0}; + struct PowerStatePlatformSleepState *state; + int ret; + + ret = extract_platform_stats(stats); + if (ret != 0) { + states.resize(0); + goto done; + } + + states.resize(platform_mode_id::RPM_MODE_COUNT); + + /* Update statistics for XO_shutdown */ + state = &states[platform_mode_id::RPM_MODE_XO]; + state->name = "XO_shutdown"; + + state->residencyInMsecSinceBoot = stats[platform_param_id::ACCUMULATED_VLOW_TIME]; + state->totalTransitions = stats[platform_param_id::VLOW_COUNT]; + state->supportedOnlyInSuspend = false; + state->voters.resize(XO_VOTERS); + + /* Update statistics for APSS voter */ + state->voters[0].name = "APSS"; + state->voters[0].totalTimeInMsecVotedForSinceBoot = + stats[platform_param_id::XO_ACCUMULATED_DURATION_APSS] / RPM_CLK; + state->voters[0].totalNumberOfTimesVotedSinceBoot = stats[platform_param_id::XO_COUNT_APSS]; + + /* Update statistics for MPSS voter */ + state->voters[1].name = "MPSS"; + state->voters[1].totalTimeInMsecVotedForSinceBoot = + stats[platform_param_id::XO_ACCUMULATED_DURATION_MPSS] / RPM_CLK; + state->voters[1].totalNumberOfTimesVotedSinceBoot = stats[platform_param_id::XO_COUNT_MPSS]; + + /* Update statistics for ADSP voter */ + state->voters[2].name = "ADSP"; + state->voters[2].totalTimeInMsecVotedForSinceBoot = + stats[platform_param_id::XO_ACCUMULATED_DURATION_ADSP] / RPM_CLK; + state->voters[2].totalNumberOfTimesVotedSinceBoot = stats[platform_param_id::XO_COUNT_ADSP]; + + /* Update statistics for SLPI voter */ + state->voters[3].name = "SLPI"; + state->voters[3].totalTimeInMsecVotedForSinceBoot = + stats[platform_param_id::XO_ACCUMULATED_DURATION_SLPI] / RPM_CLK; + state->voters[3].totalNumberOfTimesVotedSinceBoot = stats[platform_param_id::XO_COUNT_SLPI]; + + + /* Update statistics for VMIN state */ + state = &states[platform_mode_id::RPM_MODE_VMIN]; + state->name = "VMIN"; + + state->residencyInMsecSinceBoot = stats[platform_param_id::ACCUMULATED_VMIN_TIME]; + state->totalTransitions = stats[platform_param_id::VMIN_COUNT]; + state->supportedOnlyInSuspend = false; + state->voters.resize(VMIN_VOTERS); + //Note: No filling of state voters since VMIN_VOTERS = 0 + +done: + _hidl_cb(states, Status::SUCCESS); + return Void(); +} + + +// Methods from ::android::hardware::power::V1_1::IPower follow. + +Return Power::getSubsystemLowPowerStats(getSubsystemLowPowerStats_cb _hidl_cb) { + + hidl_vec subsystems; + int ret; + +done: + _hidl_cb(subsystems, Status::SUCCESS); + return Void(); +} + +Return Power::powerHintAsync(PowerHint hint, int32_t data) { + // just call the normal power hint in this oneway function + return powerHint(hint, data); +} + +} // namespace implementation +} // namespace V1_1 +} // namespace power +} // namespace hardware +} // namespace android diff --git a/power/Power.h b/power/Power.h new file mode 100644 index 0000000..2676b7d --- /dev/null +++ b/power/Power.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_POWER_V1_1_POWER_H +#define ANDROID_HARDWARE_POWER_V1_1_POWER_H + +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace power { +namespace V1_1 { +namespace implementation { + +using ::android::hardware::power::V1_0::Feature; +using ::android::hardware::power::V1_0::PowerHint; +using ::android::hardware::power::V1_1::IPower; +using ::android::hardware::Return; +using ::android::hardware::Void; + +struct Power : public IPower { + // Methods from ::android::hardware::power::V1_0::IPower follow. + + Power(); + + Return setInteractive(bool interactive) override; + Return powerHint(PowerHint hint, int32_t data) override; + Return setFeature(Feature feature, bool activate) override; + Return getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) override; + + // Methods from ::android::hardware::power::V1_1::IPower follow. + Return getSubsystemLowPowerStats(getSubsystemLowPowerStats_cb _hidl_cb) override; + Return powerHintAsync(PowerHint hint, int32_t data) override; + + // Methods from ::android::hidl::base::V1_0::IBase follow. + +}; + +} // namespace implementation +} // namespace V1_1 +} // namespace power +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_POWER_V1_1_POWER_H diff --git a/power/android.hardware.power@1.1-service.gts3l.rc b/power/android.hardware.power@1.1-service.gts3l.rc new file mode 100644 index 0000000..145d285 --- /dev/null +++ b/power/android.hardware.power@1.1-service.gts3l.rc @@ -0,0 +1,4 @@ +service vendor.power-hal-1-1 /vendor/bin/hw/android.hardware.power@1.1-service.gts3l + class hal + user system + group system diff --git a/power/hint-data.c b/power/hint-data.c new file mode 100644 index 0000000..67da77a --- /dev/null +++ b/power/hint-data.c @@ -0,0 +1,47 @@ +/* + * 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 "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..9564843 --- /dev/null +++ b/power/hint-data.h @@ -0,0 +1,44 @@ +/* + * 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. + */ + +/* 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) + +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..0fe8de2 --- /dev/null +++ b/power/list.c @@ -0,0 +1,145 @@ +/* + * 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 "list.h" +#include + +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; + + printf("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..d68c3df --- /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..5567846 --- /dev/null +++ b/power/metadata-defs.h @@ -0,0 +1,62 @@ +/* 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; +}; + +struct cam_preview_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); +int parse_cam_preview_metadata(char *metadata, + struct cam_preview_metadata_t *video_decode_metadata); diff --git a/power/metadata-parser.c b/power/metadata-parser.c new file mode 100644 index 0000000..a303069 --- /dev/null +++ b/power/metadata-parser.c @@ -0,0 +1,158 @@ +/* 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_cam_preview_metadata(char *metadata, + struct cam_preview_metadata_t *cam_preview_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) { + cam_preview_metadata->hint_id = atoi(value); + } + } + + if (strlen(attribute) == strlen("state") && + (strncmp(attribute, "state", strlen("state")) == 0)) { + if (strlen(value) > 0) { + cam_preview_metadata->state = atoi(value); + } + } + + temp_metadata = NULL; + } + + if (parsing_status == METADATA_PARSING_ERR) + return -1; + + return 0; +} + +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..b6db637 --- /dev/null +++ b/power/performance.h @@ -0,0 +1,244 @@ +/* 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 + +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/8952 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/8952 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, +}; + +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 SCHED_GUIDED_LVL { + INTERACTIVE_USE_SCHED_LOAD_OFF = 0x5201, + INTERACTIVE_USE_MIGRATION_NOTIF_OFF = 0x5301 +}; + +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-8996.c b/power/power-8996.c new file mode 100644 index 0000000..85cc388 --- /dev/null +++ b/power/power-8996.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2015, 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_NDEBUG 1 + +#include +#include +#include +#include +#include +#include +#include + +#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL) +#define LOG_TAG "QCOM PowerHAL" +#include +#include +#include +#include + +#include "utils.h" +#include "metadata-defs.h" +#include "hint-data.h" +#include "performance.h" +#include "power-common.h" + +static int display_hint_sent; +int launch_handle = -1; +int launch_mode; + +#ifdef EXTRA_POWERHAL_HINTS +static int process_cam_preview_hint(void *metadata) +{ + char governor[80]; + struct cam_preview_metadata_t cam_preview_metadata; + + if (get_scaling_governor(governor, sizeof(governor)) == -1) { + ALOGE("Can't obtain scaling governor."); + + return HINT_NONE; + } + + /* Initialize encode metadata struct fields */ + memset(&cam_preview_metadata, 0, sizeof(struct cam_preview_metadata_t)); + cam_preview_metadata.state = -1; + cam_preview_metadata.hint_id = CAM_PREVIEW_HINT_ID; + + if (metadata) { + if (parse_cam_preview_metadata((char *)metadata, &cam_preview_metadata) == + -1) { + ALOGE("Error occurred while parsing metadata."); + return HINT_NONE; + } + } else { + return HINT_NONE; + } + + if (cam_preview_metadata.state == 1) { + if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { + /* sched and cpufreq params + * above_hispeed_delay for LVT - 40ms + * go hispeed load for LVT - 95 + * hispeed freq for LVT - 556 MHz + * target load for LVT - 90 + * above hispeed delay for sLVT - 40ms + * go hispeed load for sLVT - 95 + * hispeed freq for sLVT - 556 MHz + * target load for sLVT - 90 + * bus DCVS set to V2 config: + * low power ceil mpbs - 2500 + * low power io percent - 50 + */ + int resource_values[] = {0x41400000, 0x4, 0x41410000, 0x5F, 0x41414000, 0x22C, + 0x41420000, 0x5A, 0x41400100, 0x4, 0x41410100, 0x5F, 0x41414100, 0x22C, + 0x41420100, 0x5A, 0x41810000, 0x9C4, 0x41814000, 0x32}; + + perform_hint_action(cam_preview_metadata.hint_id, + resource_values, sizeof(resource_values)/sizeof(resource_values[0])); + ALOGI("Cam Preview hint start"); + return HINT_HANDLED; + } else if ((strncmp(governor, SCHED_GOVERNOR, strlen(SCHED_GOVERNOR)) == 0) && + (strlen(governor) == strlen(SCHED_GOVERNOR))) { + /* + * lower bus BW to save power + * 0x41810000: low power ceil mpbs = 2500 + * 0x41814000: low power io percent = 50 + */ + int resource_values[] = {0x41810000, 0x9C4, 0x41814000, 0x32}; + + perform_hint_action( + cam_preview_metadata.hint_id, resource_values, + sizeof(resource_values) / sizeof(resource_values[0])); + ALOGI("Cam Preview hint start"); + return HINT_HANDLED; + } + } else if (cam_preview_metadata.state == 0) { + if (((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) || + ((strncmp(governor, SCHED_GOVERNOR, strlen(SCHED_GOVERNOR)) == 0) && + (strlen(governor) == strlen(SCHED_GOVERNOR)))) { + undo_hint_action(cam_preview_metadata.hint_id); + ALOGI("Cam Preview hint stop"); + return HINT_HANDLED; + } + } + return HINT_NONE; +} +#endif + +static int process_boost(int boost_handle, int duration) +{ + char governor[80]; + int eas_launch_resources[] = {0x40804000, 0xFFF, 0x40804100, 0xFFF, + 0x40800000, 0xFFF, 0x40800100, 0xFFF, + 0x41800000, 140, 0x40400000, 0x1}; + int hmp_launch_resources[] = {0x40C00000, 0x1, 0x40804000, 0xFFF, + 0x40804100, 0xFFF, 0x40800000, 0xFFF, + 0x40800100, 0xFFF, 0x41800000, 140, + 0x40400000, 0x1}; + int* launch_resources; + size_t launch_resources_size; + + if (get_scaling_governor(governor, sizeof(governor)) == -1) { + ALOGE("Can't obtain scaling governor."); + return -1; + } + if (strncmp(governor, SCHED_GOVERNOR, strlen(SCHED_GOVERNOR)) == 0) { + launch_resources = eas_launch_resources; + launch_resources_size = sizeof(eas_launch_resources) / sizeof(eas_launch_resources[0]); + } else if (strncmp(governor, INTERACTIVE_GOVERNOR, + strlen(INTERACTIVE_GOVERNOR)) == 0) { /*HMP boost*/ + launch_resources = hmp_launch_resources; + launch_resources_size = sizeof(hmp_launch_resources) / sizeof(hmp_launch_resources[0]); + } else { + ALOGE("Unsupported governor."); + return -1; + } + boost_handle = interaction_with_handle( + boost_handle, duration, launch_resources_size, launch_resources); + return boost_handle; +} + +static int process_video_encode_hint(void *metadata) +{ + char governor[80]; + static int boost_handle = -1; + + if (get_scaling_governor(governor, sizeof(governor)) == -1) { + ALOGE("Can't obtain scaling governor."); + + return HINT_NONE; + } + + if (metadata) { + int duration = 2000; // boosts 2s for starting encoding + boost_handle = process_boost(boost_handle, duration); + ALOGD("LAUNCH ENCODER-ON: %d MS", duration); + if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { + /* 1. cpufreq params + * -above_hispeed_delay for LVT - 40ms + * -go hispeed load for LVT - 95 + * -hispeed freq for LVT - 556 MHz + * -target load for LVT - 90 + * -above hispeed delay for sLVT - 40ms + * -go hispeed load for sLVT - 95 + * -hispeed freq for sLVT - 806 MHz + * -target load for sLVT - 90 + * 2. bus DCVS set to V2 config: + * -low power ceil mpbs - 2500 + * -low power io percent - 50 + * 3. hysteresis optimization + * -bus dcvs hysteresis tuning + * -sample_ms of 10 ms + * -disable ignore_hispeed_notif + * -sLVT hispeed freq to 806MHz + */ + int resource_values[] = { + 0x41810000, 0x9C4, 0x41814000, 0x32, 0x4180C000, 0x0, 0x41820000, 0xA, + 0x41438100, 0x1, 0x41438000, 0x1 }; + + perform_hint_action(DEFAULT_VIDEO_ENCODE_HINT_ID, + resource_values, sizeof(resource_values)/sizeof(resource_values[0])); + ALOGD("Video Encode hint start"); + return HINT_HANDLED; + } else if ((strncmp(governor, SCHED_GOVERNOR, strlen(SCHED_GOVERNOR)) == 0) && + (strlen(governor) == strlen(SCHED_GOVERNOR))) { + + /* 1. bus DCVS set to V2 config: + * 0x41810000: low power ceil mpbs - 2500 + * 0x41814000: low power io percent - 50 + * 2. hysteresis optimization + * 0x4180C000: bus dcvs hysteresis tuning + * 0x41820000: sample_ms of 10 ms + */ + int resource_values[] = {0x41810000, 0x9C4, 0x41814000, 0x32, + 0x4180C000, 0x0, 0x41820000, 0xA}; + + perform_hint_action(DEFAULT_VIDEO_ENCODE_HINT_ID, + resource_values, sizeof(resource_values)/sizeof(resource_values[0])); + ALOGD("Video Encode hint start"); + return HINT_HANDLED; + } + } else { + // boost handle is intentionally not released, release_request(boost_handle); + if (((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) || + ((strncmp(governor, SCHED_GOVERNOR, strlen(SCHED_GOVERNOR)) == 0) && + (strlen(governor) == strlen(SCHED_GOVERNOR)))) { + undo_hint_action(DEFAULT_VIDEO_ENCODE_HINT_ID); + ALOGD("Video Encode hint stop"); + return HINT_HANDLED; + } + } + return HINT_NONE; +} + +static int process_activity_launch_hint(void *data) +{ + // boost will timeout in 5s + int duration = 5000; + ATRACE_BEGIN("launch"); + if (sustained_performance_mode || vr_mode) { + ATRACE_END(); + return HINT_HANDLED; + } + + ALOGD("LAUNCH HINT: %s", data ? "ON" : "OFF"); + if (data && launch_mode == 0) { + launch_handle = process_boost(launch_handle, duration); + if (launch_handle > 0) { + launch_mode = 1; + ALOGD("Activity launch hint handled"); + ATRACE_INT("launch_lock", 1); + ATRACE_END(); + return HINT_HANDLED; + } else { + ATRACE_END(); + return HINT_NONE; + } + } else if (data == NULL && launch_mode == 1) { + release_request(launch_handle); + ATRACE_INT("launch_lock", 0); + launch_mode = 0; + ATRACE_END(); + return HINT_HANDLED; + } + ATRACE_END(); + return HINT_NONE; +} + +int power_hint_override(power_hint_t hint, void *data) +{ + int ret_val = HINT_NONE; + switch(hint) { +#ifdef EXTRA_POWERHAL_HINTS + case POWER_HINT_CAM_PREVIEW: + ret_val = process_cam_preview_hint(data); + break; +#endif + case POWER_HINT_VIDEO_ENCODE: + ret_val = process_video_encode_hint(data); + break; + case POWER_HINT_LAUNCH: + ret_val = process_activity_launch_hint(data); + break; + default: + break; + } + return ret_val; +} + +int set_interactive_override(int on) +{ + return HINT_HANDLED; /* Don't excecute this code path, not in use */ + char governor[80]; + + if (get_scaling_governor(governor, sizeof(governor)) == -1) { + ALOGE("Can't obtain scaling governor."); + + return HINT_NONE; + } + + if (!on) { + /* Display off */ + if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { + int resource_values[] = {}; /* dummy node */ + if (!display_hint_sent) { + perform_hint_action(DISPLAY_STATE_HINT_ID, + resource_values, sizeof(resource_values)/sizeof(resource_values[0])); + display_hint_sent = 1; + ALOGV("Display Off hint start"); + return HINT_HANDLED; + } + } + } else { + /* Display on */ + 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; + ALOGV("Display Off hint stop"); + return HINT_HANDLED; + } + } + return HINT_NONE; +} diff --git a/power/power-common.h b/power/power-common.h new file mode 100644 index 0000000..0b2680c --- /dev/null +++ b/power/power-common.h @@ -0,0 +1,53 @@ +/* + * 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 SCHED_GOVERNOR "sched" + +#define HINT_HANDLED (0) +#define HINT_NONE (-1) + +enum CPU_GOV_CHECK { + CPU0 = 0, + CPU1 = 1, + CPU2 = 2, + CPU3 = 3 +}; + +#define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) + diff --git a/power/power-helper.c b/power/power-helper.c new file mode 100644 index 0000000..7b625b6 --- /dev/null +++ b/power/power-helper.c @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2012-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 LOG_NDEBUG 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "QCOM PowerHAL" +#include +#include +#include + +#include "utils.h" +#include "metadata-defs.h" +#include "hint-data.h" +#include "performance.h" +#include "power-common.h" +#include "power-helper.h" + +#define USINSEC 1000000L +#define NSINUS 1000L + +#ifndef RPM_STAT +#define RPM_STAT "/d/rpm_stats" +#endif + +#ifndef RPM_MASTER_STAT +#define RPM_MASTER_STAT "/d/rpm_master_stats" +#endif + +static const char *rpm_param_names[] = { + "vlow_count", + "accumulated_vlow_time", + "vmin_count", + "accumulated_vmin_time" +}; + +static const char *rpm_master_param_names[] = { + "xo_accumulated_duration", + "xo_count", + "xo_accumulated_duration", + "xo_count", + "xo_accumulated_duration", + "xo_count", + "xo_accumulated_duration", + "xo_count" +}; + + +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 sustained_mode_handle = 0; +static int vr_mode_handle = 0; +int sustained_performance_mode = 0; +int vr_mode = 0; + +//interaction boost global variables +static struct timespec s_previous_boost_timespec; +static int s_previous_duration; + +void power_init(void) +{ + ALOGV("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); + } +} + +int __attribute__ ((weak)) power_hint_override(power_hint_t UNUSED(hint), + void * UNUSED(data)) +{ + return HINT_NONE; +} + +/* Declare function before use */ +void interaction(int duration, int num_args, int opt_list[]); +void release_request(int lock_handle); + +static long long calc_timespan_us(struct timespec start, struct timespec end) { + long long diff_in_us = 0; + diff_in_us += (end.tv_sec - start.tv_sec) * USINSEC; + diff_in_us += (end.tv_nsec - start.tv_nsec) / NSINUS; + return diff_in_us; +} + +void power_hint(power_hint_t hint, void *data) +{ + /* Check if this hint has been overridden. */ + if (power_hint_override(hint, data) == HINT_HANDLED) { + /* The power_hint has been handled. We can skip the rest. */ + return; + } + + switch(hint) { + case POWER_HINT_VSYNC: + break; + /* Sustained performance mode: + * All CPUs are capped to ~1.2GHz + * GPU frequency is capped to 315MHz + */ + /* VR+Sustained performance mode: + * All CPUs are locked to ~1.2GHz + * GPU frequency is locked to 315MHz + * GPU BW min_freq is raised to 775MHz + */ + case POWER_HINT_SUSTAINED_PERFORMANCE: + { + int duration = 0; + if (data && sustained_performance_mode == 0) { + int* resources; + if (vr_mode == 0) { // Sustained mode only. + // Ensure that POWER_HINT_LAUNCH is not in progress. + if (launch_mode == 1) { + release_request(launch_handle); + launch_mode = 0; + } + // 0x40804000: cpu0 max freq + // 0x40804100: cpu2 max freq + // 0x42C20000: gpu max freq + // 0x42C24000: gpu min freq + // 0x42C28000: gpu bus min freq + int resources[] = {0x40804000, 1209, 0x40804100, 1209, + 0x42C24000, 133, 0x42C20000, 315, + 0x42C28000, 7759}; + sustained_mode_handle = interaction_with_handle( + sustained_mode_handle, duration, + sizeof(resources) / sizeof(resources[0]), resources); + } else if (vr_mode == 1) { // Sustained + VR mode. + release_request(vr_mode_handle); + // 0x40804000: cpu0 max freq + // 0x40804100: cpu2 max freq + // 0x40800000: cpu0 min freq + // 0x40800100: cpu2 min freq + // 0x42C20000: gpu max freq + // 0x42C24000: gpu min freq + // 0x42C28000: gpu bus min freq + int resources[] = {0x40800000, 1209, 0x40800100, 1209, + 0x40804000, 1209, 0x40804100, 1209, + 0x42C24000, 315, 0x42C20000, 315, + 0x42C28000, 7759}; + sustained_mode_handle = interaction_with_handle( + sustained_mode_handle, duration, + sizeof(resources) / sizeof(resources[0]), resources); + } + sustained_performance_mode = 1; + } else if (sustained_performance_mode == 1) { + release_request(sustained_mode_handle); + if (vr_mode == 1) { // Switch back to VR Mode. + // 0x40804000: cpu0 max freq + // 0x40804100: cpu2 max freq + // 0x40800000: cpu0 min freq + // 0x40800100: cpu2 min freq + // 0x42C20000: gpu max freq + // 0x42C24000: gpu min freq + // 0x42C28000: gpu bus min freq + int resources[] = {0x40804000, 1440, 0x40804100, 1440, + 0x40800000, 1440, 0x40800100, 1440, + 0x42C20000, 510, 0x42C24000, 510, + 0x42C28000, 7759}; + vr_mode_handle = interaction_with_handle( + vr_mode_handle, duration, + sizeof(resources) / sizeof(resources[0]), resources); + } + sustained_performance_mode = 0; + } + } + break; + /* VR mode: + * All CPUs are locked at ~1.4GHz + * GPU frequency is locked to 510MHz + * GPU BW min_freq is raised to 775MHz + */ + case POWER_HINT_VR_MODE: + { + int duration = 0; + if (data && vr_mode == 0) { + if (sustained_performance_mode == 0) { // VR mode only. + // Ensure that POWER_HINT_LAUNCH is not in progress. + if (launch_mode == 1) { + release_request(launch_handle); + launch_mode = 0; + } + // 0x40804000: cpu0 max freq + // 0x40804100: cpu2 max freq + // 0x40800000: cpu0 min freq + // 0x40800100: cpu2 min freq + // 0x42C20000: gpu max freq + // 0x42C24000: gpu min freq + // 0x42C28000: gpu bus min freq + int resources[] = {0x40800000, 1440, 0x40800100, 1440, + 0x40804000, 1440, 0x40804100, 1440, + 0x42C20000, 510, 0x42C24000, 510, + 0x42C28000, 7759}; + vr_mode_handle = interaction_with_handle( + vr_mode_handle, duration, + sizeof(resources) / sizeof(resources[0]), resources); + } else if (sustained_performance_mode == 1) { // Sustained + VR mode. + release_request(sustained_mode_handle); + // 0x40804000: cpu0 max freq + // 0x40804100: cpu2 max freq + // 0x40800000: cpu0 min freq + // 0x40800100: cpu2 min freq + // 0x42C20000: gpu max freq + // 0x42C24000: gpu min freq + // 0x42C28000: gpu bus min freq + int resources[] = {0x40800000, 1209, 0x40800100, 1209, + 0x40804000, 1209, 0x40804100, 1209, + 0x42C24000, 315, 0x42C20000, 315, + 0x42C28000, 7759}; + + vr_mode_handle = interaction_with_handle( + vr_mode_handle, duration, + sizeof(resources) / sizeof(resources[0]), resources); + } + vr_mode = 1; + } else if (vr_mode == 1) { + release_request(vr_mode_handle); + if (sustained_performance_mode == 1) { // Switch back to sustained Mode. + // 0x40804000: cpu0 max freq + // 0x40804100: cpu2 max freq + // 0x40800000: cpu0 min freq + // 0x40800100: cpu2 min freq + // 0x42C20000: gpu max freq + // 0x42C24000: gpu min freq + // 0x42C28000: gpu bus min freq + int resources[] = {0x40800000, 0, 0x40800100, 0, + 0x40804000, 1209, 0x40804100, 1209, + 0x42C24000, 133, 0x42C20000, 315, + 0x42C28000, 0}; + sustained_mode_handle = interaction_with_handle( + sustained_mode_handle, duration, + sizeof(resources) / sizeof(resources[0]), resources); + } + vr_mode = 0; + } + } + break; + case POWER_HINT_INTERACTION: + { + char governor[80]; + + if (get_scaling_governor(governor, sizeof(governor)) == -1) { + ALOGE("Can't obtain scaling governor."); + return; + } + + if (sustained_performance_mode || vr_mode) { + return; + } + + int duration = 1500; // 1.5s by default + if (data) { + int input_duration = *((int*)data) + 750; + if (input_duration > duration) { + duration = (input_duration > 5750) ? 5750 : input_duration; + } + } + + struct timespec cur_boost_timespec; + clock_gettime(CLOCK_MONOTONIC, &cur_boost_timespec); + + long long elapsed_time = calc_timespan_us(s_previous_boost_timespec, cur_boost_timespec); + // don't hint if previous hint's duration covers this hint's duration + if ((s_previous_duration * 1000) > (elapsed_time + duration * 1000)) { + return; + } + s_previous_boost_timespec = cur_boost_timespec; + s_previous_duration = duration; + + // Scheduler is EAS. + if (true || strncmp(governor, SCHED_GOVERNOR, strlen(SCHED_GOVERNOR)) == 0) { + // Setting the value of foreground schedtune boost to 50 and + // scaling_min_freq to 1100MHz. + int resources[] = {0x40800000, 1100, 0x40800100, 1100, 0x42C0C000, 0x32, 0x41800000, 0x33}; + interaction(duration, sizeof(resources)/sizeof(resources[0]), resources); + } else { // Scheduler is HMP. + int resources[] = {0x41800000, 0x33, 0x40800000, 1000, 0x40800100, 1000, 0x40C00000, 0x1}; + interaction(duration, sizeof(resources)/sizeof(resources[0]), resources); + } + } + break; + default: + break; + } +} + +int __attribute__ ((weak)) set_interactive_override(int UNUSED(on)) +{ + return HINT_NONE; +} + +void power_set_interactive(int on) +{ + char governor[80]; + char tmp_str[NODE_MAX]; + struct video_encode_metadata_t video_encode_metadata; + int rc = 0; + + if (set_interactive_override(on) == HINT_HANDLED) { + return; + } + + ALOGV("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 extract_stats(uint64_t *list, char *file, const char**param_names, + unsigned int num_parameters, int isHex) { + FILE *fp; + ssize_t read; + size_t len; + size_t index = 0; + char *line; + int ret; + + fp = fopen(file, "r"); + if (fp == NULL) { + ret = -errno; + ALOGE("%s: failed to open: %s Error = %s", __func__, file, strerror(errno)); + return ret; + } + + for (line = NULL, len = 0; + ((read = getline(&line, &len, fp) != -1) && (index < num_parameters)); + free(line), line = NULL, len = 0) { + uint64_t value; + char* offset; + + size_t begin = strspn(line, " \t"); + if (strncmp(line + begin, param_names[index], strlen(param_names[index]))) { + continue; + } + + offset = memchr(line, ':', len); + if (!offset) { + continue; + } + + if (isHex) { + sscanf(offset, ":%" SCNx64, &value); + } else { + sscanf(offset, ":%" SCNu64, &value); + } + list[index] = value; + index++; + } + + free(line); + fclose(fp); + + return 0; +} + + +int extract_platform_stats(uint64_t *list) { + + int ret; + + //Data is located in two files + + ret = extract_stats(list, RPM_STAT, rpm_param_names, RPM_PARAM_COUNT, false); + if (ret) { + for (size_t i=0; i < RPM_PARAM_COUNT; i++) + list[i] = 0; + } + + ret = extract_stats(list + RPM_PARAM_COUNT, RPM_MASTER_STAT, + rpm_master_param_names, PLATFORM_PARAM_COUNT - RPM_PARAM_COUNT, true); + if (ret) { + for (size_t i=RPM_PARAM_COUNT; i < PLATFORM_PARAM_COUNT; i++) + list[i] = 0; + } + + return 0; +} diff --git a/power/power-helper.h b/power/power-helper.h new file mode 100644 index 0000000..77947d4 --- /dev/null +++ b/power/power-helper.h @@ -0,0 +1,88 @@ +/* + * 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. + */ +#ifndef __POWER_HELPER_H__ +#define __POWER_HELPER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +enum platform_param_id { + VLOW_COUNT = 0, + ACCUMULATED_VLOW_TIME, + VMIN_COUNT, + ACCUMULATED_VMIN_TIME, + RPM_PARAM_COUNT, + + XO_ACCUMULATED_DURATION_APSS = RPM_PARAM_COUNT, + XO_COUNT_APSS, + XO_ACCUMULATED_DURATION_MPSS, + XO_COUNT_MPSS, + XO_ACCUMULATED_DURATION_ADSP, + XO_COUNT_ADSP, + XO_ACCUMULATED_DURATION_SLPI, + XO_COUNT_SLPI, + + //Don't add any lines after that line + PLATFORM_PARAM_COUNT +}; + +enum platform_mode_id { + RPM_MODE_XO = 0, + RPM_MODE_VMIN, + + //Don't add any lines after that line + RPM_MODE_COUNT +}; + +#define XO_VOTERS 4 +#define VMIN_VOTERS 0 + +enum voter_id { + APSS, + MPSS, + ADSP, + SLPI, + + //Don't add any lines after that line + VOTER_COUNT +}; + +void power_init(void); +void power_hint(power_hint_t hint, void *data); +void power_set_interactive(int on); +int extract_platform_stats(uint64_t *list); + +#ifdef __cplusplus +} +#endif + +#endif //__POWER_HELPER_H__ diff --git a/power/service.cpp b/power/service.cpp new file mode 100644 index 0000000..655d222 --- /dev/null +++ b/power/service.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "android.hardware.power@1.1-service.gts3l" + +#include +#include +#include +#include "Power.h" + +using android::sp; +using android::status_t; +using android::OK; + +// libhwbinder: +using android::hardware::configureRpcThreadpool; +using android::hardware::joinRpcThreadpool; + +// Generated HIDL files +using android::hardware::power::V1_1::IPower; +using android::hardware::power::V1_1::implementation::Power; + +int main() { + + status_t status; + android::sp service = nullptr; + + ALOGI("Power HAL Service 1.1 for Samsung Galaxy Tab S3 is starting."); + + service = new Power(); + if (service == nullptr) { + ALOGE("Can not create an instance of Power HAL Iface, exiting."); + + goto shutdown; + } + + configureRpcThreadpool(1, true /*callerWillJoin*/); + + status = service->registerAsService(); + if (status != OK) { + ALOGE("Could not register service for Power HAL Iface (%d).", status); + goto shutdown; + } + + ALOGI("Power Service is ready"); + joinRpcThreadpool(); + //Should not pass this line + +shutdown: + // In normal operation, we don't expect the thread pool to exit + + ALOGE("Power Service is shutting down"); + return 1; +} diff --git a/power/utils.c b/power/utils.c new file mode 100644 index 0000000..bd3f354 --- /dev/null +++ b/power/utils.c @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2012-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 LOG_NDEBUG 1 + +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "list.h" +#include "hint-data.h" +#include "power-common.h" + +#define LOG_TAG "QCOM PowerHAL" +#include + +#ifndef INTERACTION_BOOST +#define INTERACTION_BOOST +#endif + +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" +}; + +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 struct list_node active_hint_list_head; + +static void *get_qcopt_handle() +{ + char qcopt_lib_path[PATH_MAX] = {0}; + void *handle = NULL; + + dlerror(); + + if (property_get("ro.vendor.extension_library", qcopt_lib_path, + NULL)) { + handle = dlopen(qcopt_lib_path, RTLD_NOW); + if (!handle) { + ALOGE("Unable to open %s: %s\n", qcopt_lib_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"); + } + } +} + +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; +} + +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[]) +{ +#ifdef INTERACTION_BOOST + 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; +#endif + return 0; +} + +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) { + struct hint_data temp_hint_data = { + .hint_id = hint_id + }; + struct list_node *found_node = find_node(&active_hint_list_head, + &temp_hint_data); + if (found_node) { + ALOGE("hint ID %d already active", hint_id); + return; + } + 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: %d", hint_id); + } + + 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); + ALOGV("Undo of hint ID %d succeeded", hint_id); + } else { + ALOGE("Invalid hint ID: %d", 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..8d80971 --- /dev/null +++ b/power/utils.h @@ -0,0 +1,51 @@ +/* + * 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 + +extern int launch_handle; +extern int launch_mode; +extern int sustained_performance_mode; +extern int vr_mode; + +int sysfs_read(char *path, char *s, int num_bytes); +int sysfs_write(char *path, char *s); +int sysfs_get_size_in_bytes(char *path); +int get_scaling_governor(char governor[], int size); +int get_scaling_governor_check_cores(char governor[], int size,int core_num); + +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[]);