/* 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