power: Release interaction lock when idle state is detected
An idle state node has been added to some of our common kernels
(e.g. qcom_msm8998). Use this node to unlock interaction boost before
the timeout expires when screen updates are not expected soon.
Change-Id: I43d1e2f2cbd6713baa132be7eb475243a8d4a5c6
diff --git a/android.hardware.power-service-qti.rc b/android.hardware.power-service-qti.rc
index 05c4d33..01f9e70 100644
--- a/android.hardware.power-service-qti.rc
+++ b/android.hardware.power-service-qti.rc
@@ -2,3 +2,8 @@
class hal
user system
group system
+
+on post-fs
+ chmod 0664 /sys/devices/virtual/graphics/fb0/idle_time
+ chown system graphics /sys/devices/virtual/graphics/fb0/idle_time
+ write /sys/devices/virtual/graphics/fb0/idle_time 100
diff --git a/power-common.c b/power-common.c
index 533df83..b6a88e4 100644
--- a/power-common.c
+++ b/power-common.c
@@ -27,16 +27,19 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#define LOG_NIDEBUG 0
+//#define LOG_NDEBUG 0
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
+#include <poll.h>
+#include <sys/eventfd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <pthread.h>
#define LOG_TAG "QTI PowerHAL"
#include <hardware/hardware.h>
@@ -48,13 +51,157 @@
#include "power-common.h"
#include "utils.h"
+#define MAX_LENGTH 64
+
static struct hint_handles handles[NUM_HINTS];
static int handleER = 0;
+static const char* fb_idle_paths[] = {"/sys/class/drm/card0/device/idle_state",
+ "/sys/class/graphics/fb0/idle_state"};
+
+static pthread_t tid;
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+static pthread_cond_t interaction_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t interaction_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static enum INTERACTION_STATE mState = INTERACTION_STATE_UNINITIALIZED;
+
+static int mIdleFd = 0;
+static int mEventFd = 0;
+
+const int kWaitDuration = 100; /* ms */
const int kMaxLaunchDuration = 5000; /* ms */
const int kMaxInteractiveDuration = 5000; /* ms */
const int kMinInteractiveDuration = 500; /* ms */
+static struct timespec s_previous_boost_timespec;
+static int s_previous_duration = 0;
+static int prev_interaction_handle = -1;
+
+int process_boost(int hint_id, int duration, int type) {
+ ALOGV("%s: acquiring perf lock", __func__);
+ int boost_handle = perf_hint_enable_with_type(hint_id, duration, type);
+ if (!CHECK_HANDLE(boost_handle)) {
+ ALOGE("Failed process_boost for boost_handle");
+ }
+ return boost_handle;
+}
+
+bool release_boost(int boost_handle) {
+ ALOGV("%s: releasing perf lock %i", __func__, boost_handle);
+ if (CHECK_HANDLE(boost_handle)) {
+ release_request(boost_handle);
+ return true;
+ }
+ return false;
+}
+
+int fb_idle_open(void) {
+ int fd;
+ int n = sizeof(fb_idle_paths) / sizeof(fb_idle_paths[0]);
+ for (int i = 0; i < n; i++) {
+ const char* path = fb_idle_paths[i];
+ fd = open(path, O_RDONLY);
+ if (fd >= 0)
+ return fd;
+ }
+ ALOGE("Unable to open fb idle state path (%d)", errno);
+ return -1;
+}
+
+void release() {
+ pthread_mutex_lock(&interaction_lock);
+ if (mState == INTERACTION_STATE_WAITING) {
+ if (release_boost(prev_interaction_handle)) {
+ prev_interaction_handle = -1;
+ }
+ mState = INTERACTION_STATE_IDLE;
+ } else {
+ // clear any wait aborts pending in event fd
+ uint64_t val;
+ ssize_t ret = read(mEventFd, &val, sizeof(val));
+
+ ALOGW_IF(ret < 0, "%s: failed to clear eventfd (%zd, %d)",
+ __func__, ret, errno);
+ }
+ pthread_mutex_unlock(&interaction_lock);
+}
+
+void abortWaitLocked() {
+ uint64_t val = 1;
+ ssize_t ret = write(mEventFd, &val, sizeof(val));
+ if (ret != sizeof(val))
+ ALOGW("Unable to write to event fd (%zd)", ret);
+}
+
+void waitForIdle(int32_t wait_ms, int32_t timeout_ms) {
+ char data[MAX_LENGTH];
+ ssize_t ret;
+ struct pollfd pfd[2];
+
+ ALOGV("%s: wait:%d timeout:%d", __func__, wait_ms, timeout_ms);
+
+ pfd[0].fd = mEventFd;
+ pfd[0].events = POLLIN;
+ pfd[1].fd = mIdleFd;
+ pfd[1].events = POLLPRI | POLLERR;
+
+ ret = poll(pfd, 1, wait_ms);
+ if (ret > 0) {
+ ALOGV("%s: wait aborted", __func__);
+ return;
+ } else if (ret < 0) {
+ ALOGE("%s: error in poll while waiting", __func__);
+ return;
+ }
+
+ ret = pread(mIdleFd, data, sizeof(data), 0);
+ if (!ret) {
+ ALOGE("%s: Unexpected EOF!", __func__);
+ return;
+ }
+
+ if (!strncmp(data, "idle", 4)) {
+ ALOGV("%s: already idle", __func__);
+ return;
+ }
+
+ ret = poll(pfd, 2, timeout_ms);
+ if (ret < 0)
+ ALOGE("%s: Error on waiting for idle (%zd)", __func__, ret);
+ else if (ret == 0)
+ ALOGV("%s: timed out waiting for idle", __func__);
+ else if (pfd[0].revents)
+ ALOGV("%s: wait for idle aborted", __func__);
+ else if (pfd[1].revents)
+ ALOGV("%s: idle detected", __func__);
+}
+
+void* interaction_routine(void *vargp) {
+ while (true) {
+ pthread_mutex_lock(&interaction_lock);
+
+ while (mState == INTERACTION_STATE_IDLE)
+ pthread_cond_wait(&interaction_cond, &interaction_lock);
+
+ if (mState == INTERACTION_STATE_UNINITIALIZED) {
+ pthread_mutex_unlock(&interaction_lock);
+ return NULL;
+ }
+
+ mState = INTERACTION_STATE_WAITING;
+ pthread_mutex_unlock(&interaction_lock);
+
+ waitForIdle(kWaitDuration, s_previous_duration);
+ release();
+ }
+ return NULL;
+}
+
+static void create_once(void) {
+ pthread_create(&tid, NULL, interaction_routine, NULL);
+}
+
void power_init() {
ALOGI("Initing");
@@ -62,17 +209,40 @@
handles[i].handle = 0;
handles[i].ref_count = 0;
}
+
+ pthread_mutex_lock(&interaction_lock);
+ if (mState != INTERACTION_STATE_UNINITIALIZED) {
+ pthread_mutex_unlock(&interaction_lock);
+ return;
+ }
+
+ int fd = fb_idle_open();
+ if (fd < 0) {
+ pthread_mutex_unlock(&interaction_lock);
+ return;
+ }
+ mIdleFd = fd;
+
+ mEventFd = eventfd(0, EFD_NONBLOCK);
+ if (mEventFd < 0) {
+ ALOGE("Unable to create event fd (%d)", errno);
+ close(mIdleFd);
+ pthread_mutex_unlock(&interaction_lock);
+ return;
+ }
+
+ mState = INTERACTION_STATE_IDLE;
+ pthread_once(&once, create_once);
+ pthread_mutex_unlock(&interaction_lock);
}
void process_interaction_hint(void* data) {
- static struct timespec s_previous_boost_timespec;
- static int s_previous_duration = 0;
- static int prev_interaction_handle = -1;
-
struct timespec cur_boost_timespec;
long long elapsed_time;
int duration = kMinInteractiveDuration;
+ pthread_mutex_lock(&interaction_lock);
+
if (data) {
int input_duration = *((int*)data);
if (input_duration > duration) {
@@ -84,26 +254,48 @@
clock_gettime(CLOCK_MONOTONIC, &cur_boost_timespec);
elapsed_time = calc_timespan_us(s_previous_boost_timespec, cur_boost_timespec);
- // don't hint if it's been less than 250ms since last boost
- // also detect if we're doing anything resembling a fling
- // support additional boosting in case of flings
- if (elapsed_time < 250000 && duration <= kMinInteractiveDuration) {
- return;
+ if (mState == INTERACTION_STATE_UNINITIALIZED) {
+ // don't hint if it's been less than 250ms since last boost
+ // also detect if we're doing anything resembling a fling
+ // support additional boosting in case of flings
+ if (elapsed_time < 250000 && duration <= kMinInteractiveDuration) {
+ pthread_mutex_unlock(&interaction_lock);
+ return;
+ }
}
- // also don't hint if previous hint's duration covers this hint's duration
- if ((s_previous_duration * 1000) > (elapsed_time + duration * 1000)) {
- return;
+
+ if (mState != INTERACTION_STATE_IDLE && duration <= s_previous_duration) {
+ // don't hint if previous hint's duration covers this hint's duration
+ if (elapsed_time <= (s_previous_duration - duration) * 1000) {
+ ALOGV("%s: Previous duration (%d) cover this (%d) elapsed: %lld",
+ __func__, s_previous_duration, duration, elapsed_time);
+ pthread_mutex_unlock(&interaction_lock);
+ return;
+ }
}
+
s_previous_boost_timespec = cur_boost_timespec;
s_previous_duration = duration;
- int interaction_handle =
- perf_hint_enable_with_type(VENDOR_HINT_SCROLL_BOOST, duration, SCROLL_VERTICAL);
+ if (mState == INTERACTION_STATE_UNINITIALIZED) {
+ int interaction_handle =
+ process_boost(VENDOR_HINT_SCROLL_BOOST, duration, SCROLL_VERTICAL);
- if (CHECK_HANDLE(prev_interaction_handle)) {
- release_request(prev_interaction_handle);
+ release_boost(prev_interaction_handle);
+ prev_interaction_handle = interaction_handle;
+ pthread_mutex_unlock(&interaction_lock);
+ return;
}
- prev_interaction_handle = interaction_handle;
+
+ if (mState == INTERACTION_STATE_WAITING)
+ abortWaitLocked();
+ else if (mState == INTERACTION_STATE_IDLE)
+ prev_interaction_handle =
+ process_boost(VENDOR_HINT_SCROLL_BOOST, INT_MAX, SCROLL_VERTICAL);
+
+ mState = INTERACTION_STATE_INTERACTION;
+ pthread_cond_signal(&interaction_cond);
+ pthread_mutex_unlock(&interaction_lock);
}
void process_activity_launch_hint(void* data) {
@@ -112,8 +304,7 @@
// release lock early if launch has finished
if (!data) {
- if (CHECK_HANDLE(launch_handle)) {
- release_request(launch_handle);
+ if (release_boost(launch_handle)) {
launch_handle = -1;
}
launch_mode = 0;
@@ -121,7 +312,7 @@
}
if (!launch_mode) {
- launch_handle = perf_hint_enable_with_type(VENDOR_HINT_FIRST_LAUNCH_BOOST,
+ launch_handle = process_boost(VENDOR_HINT_FIRST_LAUNCH_BOOST,
kMaxLaunchDuration, LAUNCH_BOOST_V1);
if (!CHECK_HANDLE(launch_handle)) {
ALOGE("Failed to perform launch boost");
diff --git a/power-common.h b/power-common.h
index 02fd557..1566061 100644
--- a/power-common.h
+++ b/power-common.h
@@ -45,6 +45,13 @@
enum CPU_GOV_CHECK { CPU0 = 0, CPU1 = 1, CPU2 = 2, CPU3 = 3 };
+enum INTERACTION_STATE {
+ INTERACTION_STATE_UNINITIALIZED,
+ INTERACTION_STATE_IDLE,
+ INTERACTION_STATE_INTERACTION,
+ INTERACTION_STATE_WAITING,
+};
+
void power_init(void);
void power_hint(power_hint_t hint, void* data);
bool is_expensive_rendering_supported();