/* Copyright (c) 2015-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. * */ #define LOG_TAG "QCameraPerf" // To remove #include <cutils/properties.h> // System dependencies #include <stdlib.h> #include <dlfcn.h> // Camera dependencies #include "QCameraPerf.h" #include "QCameraTrace.h" extern "C" { #include "mm_camera_dbg.h" } namespace qcamera { /*=========================================================================== * FUNCTION : QCameraPerfLock constructor * * DESCRIPTION: initialize member variables * * PARAMETERS : * None * * RETURN : void * *==========================================================================*/ QCameraPerfLock::QCameraPerfLock() : perf_lock_acq(NULL), perf_lock_rel(NULL), mDlHandle(NULL), mPerfLockEnable(0), mPerfLockHandle(-1), mPerfLockHandleTimed(-1), mTimerSet(0), mPerfLockTimeout(0), mStartTimeofLock(0) { } /*=========================================================================== * FUNCTION : QCameraPerfLock destructor * * DESCRIPTION: class desctructor * * PARAMETERS : * None * * RETURN : void * *==========================================================================*/ QCameraPerfLock::~QCameraPerfLock() { lock_deinit(); } /*=========================================================================== * FUNCTION : lock_init * * DESCRIPTION: opens the performance lib and initilizes the perf lock functions * * PARAMETERS : * None * * RETURN : void * *==========================================================================*/ void QCameraPerfLock::lock_init() { const char *rc; char value[PROPERTY_VALUE_MAX]; LOGD("E"); Mutex::Autolock lock(mLock); // Clear the list of active power hints mActivePowerHints.clear(); mCurrentPowerHint = static_cast<power_hint_t>(0); mCurrentPowerHintEnable = false; property_get("persist.camera.perflock.enable", value, "1"); mPerfLockEnable = atoi(value); #ifdef HAS_MULTIMEDIA_HINTS if (hw_get_module(POWER_HARDWARE_MODULE_ID, (const hw_module_t **)&m_pPowerModule)) { LOGE("%s module not found", POWER_HARDWARE_MODULE_ID); } #endif if (mPerfLockEnable) { perf_lock_acq = NULL; perf_lock_rel = NULL; mPerfLockHandle = -1; /* Retrieve name of vendor extension library */ if (property_get("ro.vendor.extension_library", value, NULL) <= 0) { goto cleanup; } mDlHandle = dlopen(value, RTLD_NOW | RTLD_LOCAL); if (mDlHandle == NULL) { goto cleanup; } dlerror(); perf_lock_acq = (int (*) (int, int, int[], int))dlsym(mDlHandle, "perf_lock_acq"); if ((rc = dlerror()) != NULL) { LOGE("failed to perf_lock_acq function handle"); goto cleanup; } perf_lock_rel = (int (*) (int))dlsym(mDlHandle, "perf_lock_rel"); if ((rc = dlerror()) != NULL) { LOGE("failed to perf_lock_rel function handle"); goto cleanup; } LOGD("X"); return; cleanup: perf_lock_acq = NULL; perf_lock_rel = NULL; mPerfLockEnable = 0; if (mDlHandle) { dlclose(mDlHandle); mDlHandle = NULL; } } LOGD("X"); } /*=========================================================================== * FUNCTION : lock_deinit * * DESCRIPTION: deinitialize the perf lock parameters * * PARAMETERS : * None * * RETURN : void * *==========================================================================*/ void QCameraPerfLock::lock_deinit() { Mutex::Autolock lock(mLock); if (mPerfLockEnable) { LOGD("E"); if (mActivePowerHints.empty() == false) { // Disable the active power hint mCurrentPowerHint = *mActivePowerHints.begin(); powerHintInternal(mCurrentPowerHint, false); mActivePowerHints.clear(); } if ((NULL != perf_lock_rel) && (mPerfLockHandleTimed >= 0)) { (*perf_lock_rel)(mPerfLockHandleTimed); } if ((NULL != perf_lock_rel) && (mPerfLockHandle >= 0)) { (*perf_lock_rel)(mPerfLockHandle); } if (mDlHandle) { perf_lock_acq = NULL; perf_lock_rel = NULL; dlclose(mDlHandle); mDlHandle = NULL; } mPerfLockEnable = 0; LOGD("X"); } } /*=========================================================================== * FUNCTION : isTimerReset * * DESCRIPTION: Check if timout duration is reached * * PARAMETERS : None * * RETURN : true if timeout reached * false if timeout not reached * *==========================================================================*/ bool QCameraPerfLock::isTimerReset() { Mutex::Autolock lock(mLock); if (mPerfLockEnable && mTimerSet) { nsecs_t timeDiff = systemTime() - mStartTimeofLock; if (ns2ms(timeDiff) > (uint32_t)mPerfLockTimeout) { resetTimer(); return true; } } return false; } /*=========================================================================== * FUNCTION : resetTimer * * DESCRIPTION: Reset the timer used in timed perf lock * * PARAMETERS : None * * RETURN : void * *==========================================================================*/ void QCameraPerfLock::resetTimer() { mPerfLockTimeout = 0; mTimerSet = 0; } /*=========================================================================== * FUNCTION : start_timer * * DESCRIPTION: get the start of the timer * * PARAMETERS : * @timer_val: timer duration in milliseconds * * RETURN : int32_t type of status * NO_ERROR -- success * none-zero failure code * *==========================================================================*/ void QCameraPerfLock::startTimer(uint32_t timer_val) { mStartTimeofLock = systemTime(); mTimerSet = 1; mPerfLockTimeout = timer_val; } /*=========================================================================== * FUNCTION : lock_acq_timed * * DESCRIPTION: Acquire the performance lock for the specified duration. * If an existing lock timeout has not elapsed, extend the * lock further for the specified duration * * PARAMETERS : * @timer_val: lock duration * * RETURN : int32_t type of status * NO_ERROR -- success * none-zero failure code * *==========================================================================*/ int32_t QCameraPerfLock::lock_acq_timed(int32_t timer_val) { int32_t ret = -1; LOGD("E"); Mutex::Autolock lock(mLock); if (mPerfLockEnable) { int32_t perf_lock_params[] = { ALL_CPUS_PWR_CLPS_DIS, CPU0_MIN_FREQ_TURBO_MAX, CPU4_MIN_FREQ_TURBO_MAX }; if (mTimerSet) { nsecs_t curElapsedTime = systemTime() - mStartTimeofLock; int32_t pendingTimeout = mPerfLockTimeout - ns2ms(curElapsedTime); timer_val += pendingTimeout; } startTimer(timer_val); // Disable power hint when acquiring the perf lock if (mCurrentPowerHintEnable) { LOGD("mCurrentPowerHintEnable %d" ,mCurrentPowerHintEnable); powerHintInternal(mCurrentPowerHint, false); } if ((NULL != perf_lock_acq) && (mPerfLockHandleTimed < 0)) { ret = (*perf_lock_acq)(mPerfLockHandleTimed, timer_val, perf_lock_params, sizeof(perf_lock_params) / sizeof(int32_t)); LOGD("ret %d", ret); if (ret < 0) { LOGE("failed to acquire lock"); } else { mPerfLockHandleTimed = ret; } } LOGD("perf_handle_acq %d ", mPerfLockHandleTimed); } LOGD("X"); return ret; } /*=========================================================================== * FUNCTION : lock_acq * * DESCRIPTION: acquire the performance lock * * PARAMETERS : * None * * RETURN : int32_t type of status * NO_ERROR -- success * none-zero failure code * *==========================================================================*/ int32_t QCameraPerfLock::lock_acq() { int32_t ret = -1; LOGD("E"); Mutex::Autolock lock(mLock); if (mPerfLockEnable) { int32_t perf_lock_params[] = { ALL_CPUS_PWR_CLPS_DIS, CPU0_MIN_FREQ_TURBO_MAX, CPU4_MIN_FREQ_TURBO_MAX }; // Disable power hint when acquiring the perf lock if (mCurrentPowerHintEnable) { powerHintInternal(mCurrentPowerHint, false); } if ((NULL != perf_lock_acq) && (mPerfLockHandle < 0)) { ret = (*perf_lock_acq)(mPerfLockHandle, ONE_SEC, perf_lock_params, sizeof(perf_lock_params) / sizeof(int32_t)); LOGD("ret %d", ret); if (ret < 0) { LOGE("failed to acquire lock"); } else { mPerfLockHandle = ret; } } LOGD("perf_handle_acq %d ", mPerfLockHandle); } LOGD("X"); return ret; } /*=========================================================================== * FUNCTION : lock_rel_timed * * DESCRIPTION: release the performance lock * * PARAMETERS : * None * * RETURN : int32_t type of status * NO_ERROR -- success * none-zero failure code * *==========================================================================*/ int32_t QCameraPerfLock::lock_rel_timed() { int ret = -1; Mutex::Autolock lock(mLock); if (mPerfLockEnable) { LOGD("E"); if (mPerfLockHandleTimed < 0) { LOGW("mPerfLockHandle < 0,check if lock is acquired"); return ret; } LOGD("perf_handle_rel %d ", mPerfLockHandleTimed); if ((NULL != perf_lock_rel) && (0 <= mPerfLockHandleTimed)) { ret = (*perf_lock_rel)(mPerfLockHandleTimed); if (ret < 0) { LOGE("failed to release lock"); } mPerfLockHandleTimed = -1; resetTimer(); } if ((mCurrentPowerHintEnable == 1) && (mTimerSet == 0)) { powerHintInternal(mCurrentPowerHint, mCurrentPowerHintEnable); } LOGD("X"); } return ret; } /*=========================================================================== * FUNCTION : lock_rel * * DESCRIPTION: release the performance lock * * PARAMETERS : * None * * RETURN : int32_t type of status * NO_ERROR -- success * none-zero failure code * *==========================================================================*/ int32_t QCameraPerfLock::lock_rel() { int ret = -1; Mutex::Autolock lock(mLock); if (mPerfLockEnable) { LOGD("E"); if (mPerfLockHandle < 0) { LOGW("mPerfLockHandle < 0,check if lock is acquired"); return ret; } LOGD("perf_handle_rel %d ", mPerfLockHandle); if ((NULL != perf_lock_rel) && (0 <= mPerfLockHandle)) { ret = (*perf_lock_rel)(mPerfLockHandle); if (ret < 0) { LOGE("failed to release lock"); } mPerfLockHandle = -1; } if (mCurrentPowerHintEnable == 1) { powerHintInternal(mCurrentPowerHint, mCurrentPowerHintEnable); } LOGD("X"); } return ret; } /*=========================================================================== * FUNCTION : powerHintInternal * * DESCRIPTION: Sets the requested power hint and state to power HAL. * * PARAMETERS : * hint : Power hint * enable : Enable power hint if set to 1. Disable if set to 0. * RETURN : void * *==========================================================================*/ void QCameraPerfLock::powerHintInternal(power_hint_t hint, bool enable) { #ifdef HAS_MULTIMEDIA_HINTS if (m_pPowerModule != NULL) { if (enable == true) { m_pPowerModule->powerHint(m_pPowerModule, hint, (void *)"state=1"); } else { m_pPowerModule->powerHint(m_pPowerModule, hint, (void *)"state=0"); } } #endif } /*=========================================================================== * FUNCTION : powerHint * * DESCRIPTION: Updates the list containing active/enabled power hints. * If needed, calls the internal powerHint function with * requested power hint and state. * PARAMETERS : * hint : Power hint * enable : Enable power hint if set to 1. Disable if set to 0. * RETURN : void * *==========================================================================*/ void QCameraPerfLock::powerHint(power_hint_t hint, bool enable) { #ifdef HAS_MULTIMEDIA_HINTS if (enable == true) { if ((hint != mCurrentPowerHint) || (enable != mCurrentPowerHintEnable)) { // Disable the current active power hint if (mCurrentPowerHintEnable == true) { powerHintInternal(mCurrentPowerHint, false); } // Push the new power hint at the head of the active power hint list mActivePowerHints.push_front(hint); // Set the new power hint mCurrentPowerHint = hint; mCurrentPowerHintEnable = enable; powerHintInternal(hint, enable); } } else { // Remove the power hint from the list for (List<power_hint_t>::iterator it = mActivePowerHints.begin(); it != mActivePowerHints.end(); ++it) { if (*it == hint) { if (it != mActivePowerHints.begin()) { LOGW("Request to remove the previous power hint: %d instead of " "currently active power hint: %d", static_cast<int>(hint), static_cast<int>(mCurrentPowerHint)); } mActivePowerHints.erase(it); break; } } if (hint == mCurrentPowerHint) { // Disable the power hint powerHintInternal(hint, false); // If the active power hint list is not empty, // restore the previous power hint from the head of the list if (mActivePowerHints.empty() == false) { mCurrentPowerHint = *mActivePowerHints.begin(); mCurrentPowerHintEnable = true; powerHintInternal(mCurrentPowerHint, true); } else { mCurrentPowerHint = static_cast<power_hint_t>(0); mCurrentPowerHintEnable = false; } } } #endif } }; // namespace qcamera