power: Process video decode and screen off hints.

Process video decode hints and display-off hints
in the power HAL. Also move to using the perflock
API to request system-level parameter changes.

Change-Id: I42e33d6d22016b02d85572a69fee64aadf36da58
diff --git a/utils.c b/utils.c
new file mode 100644
index 0000000..c985508
--- /dev/null
+++ b/utils.c
@@ -0,0 +1,326 @@
+/*
+ * 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 Code Aurora Forum, Inc. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_NIDEBUG 0
+
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "utils.h"
+#include "list.h"
+#include "hint-data.h"
+
+#define LOG_TAG "QCOM PowerHAL"
+#include <utils/Log.h>
+
+static int qcopt_handle_unavailable;
+static void *qcopt_handle;
+static int (*perf_vote_turnoff_ondemand_io_busy)(int vote);
+static int perf_vote_ondemand_io_busy_unavailable;
+static int (*perf_vote_lower_ondemand_sdf)(int vote);
+static int perf_vote_ondemand_sdf_unavailable;
+static int (*perf_lock_acq)(unsigned long handle, int duration,
+    int list[], int numArgs);
+static int perf_lock_acq_unavailable;
+static int (*perf_lock_rel)(unsigned long handle);
+static int perf_lock_rel_unavailable;
+static struct list_node active_hint_list_head;
+
+static void *get_qcopt_handle()
+{
+    if (qcopt_handle_unavailable) {
+        return NULL;
+    }
+
+    if (!qcopt_handle) {
+        char qcopt_lib_path[PATH_MAX] = {0};
+        dlerror();
+
+        if (property_get("ro.vendor.extension_library", qcopt_lib_path,
+                    NULL) != 0) {
+            if((qcopt_handle = dlopen(qcopt_lib_path, RTLD_NOW)) == NULL) {
+                qcopt_handle_unavailable = 1;
+                ALOGE("Unable to open %s: %s\n", qcopt_lib_path,
+                        dlerror());
+            }
+        } else {
+            qcopt_handle_unavailable = 1;
+            ALOGE("Property ro.vendor.extension_library does not exist.");
+        }
+    }
+
+    return qcopt_handle;
+}
+
+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;
+}
+
+void perform_hint_action(int hint_id, int resource_values[], int num_resources)
+{
+    void *handle;
+
+    if ((handle = get_qcopt_handle())) {
+        if (!perf_lock_acq_unavailable && !perf_lock_rel_unavailable) {
+            perf_lock_acq = dlsym(handle, "perf_lock_acq");
+
+            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. */
+                            perf_lock_rel(lock_handle);
+                            ALOGE("Failed to process hint.");
+                        }
+                    } else {
+                        /* Can't keep track of this lock. Release it. */
+                        perf_lock_rel(lock_handle);
+                        ALOGE("Failed to process hint.");
+                    }
+                }
+            } else {
+                perf_lock_acq_unavailable = 1;
+                ALOGE("Can't commit hint action. Lock acquire function is not available.");
+            }
+        }
+    }
+}
+
+void undo_hint_action(int hint_id)
+{
+    void *handle;
+
+    if ((handle = get_qcopt_handle())) {
+        if (!perf_lock_acq_unavailable && !perf_lock_rel_unavailable) {
+            perf_lock_rel = dlsym(handle, "perf_lock_rel");
+
+            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
+                };
+
+                if ((found_node = find_node(&active_hint_list_head,
+                    &temp_hint_data)) != NULL) {
+                    /* Release this lock. */
+                    struct hint_data *found_hint_data =
+                        (struct hint_data *)(found_node->data);
+
+                    if (found_hint_data &&
+                        (perf_lock_rel(found_hint_data->perflock_handle) == -1)) {
+                        ALOGE("Perflock release failed.");
+                    } else {
+                        ALOGI("Perflock released.");
+                    }
+
+                    if (found_node->data) {
+                        /* We can free the hint-data for this node. */
+                        free(found_node->data);
+                    }
+
+                    remove_list_node(&active_hint_list_head, found_node);
+                } else {
+                    ALOGE("Invalid hint ID.");
+                }
+            } else {
+                perf_lock_rel_unavailable = 1;
+                ALOGE("Can't undo hint action. Lock release function is not available.");
+            }
+        }
+    }
+}
+
+void vote_ondemand_io_busy_off()
+{
+    void *handle;
+
+    if ((handle = get_qcopt_handle())) {
+        if (!perf_vote_ondemand_io_busy_unavailable) {
+            perf_vote_turnoff_ondemand_io_busy = dlsym(handle,
+                    "perf_vote_turnoff_ondemand_io_busy");
+
+            if (perf_vote_turnoff_ondemand_io_busy) {
+                /* Vote to turn io_is_busy off */
+                perf_vote_turnoff_ondemand_io_busy(1);
+            } else {
+                perf_vote_ondemand_io_busy_unavailable = 1;
+                ALOGE("Can't set io_busy_status.");
+            }
+        }
+    }
+}
+
+void unvote_ondemand_io_busy_off()
+{
+    void *handle;
+
+    if ((handle = get_qcopt_handle())) {
+        if (!perf_vote_ondemand_io_busy_unavailable) {
+            perf_vote_turnoff_ondemand_io_busy = dlsym(handle,
+                    "perf_vote_turnoff_ondemand_io_busy");
+
+            if (perf_vote_turnoff_ondemand_io_busy) {
+                /* Remove vote to turn io_busy off. */
+                perf_vote_turnoff_ondemand_io_busy(0);
+            } else {
+                perf_vote_ondemand_io_busy_unavailable = 1;
+                ALOGE("Can't set io_busy_status.");
+            }
+        }
+    }
+}
+
+void vote_ondemand_sdf_low()
+{
+    void *handle;
+
+    if ((handle = get_qcopt_handle())) {
+        if (!perf_vote_ondemand_sdf_unavailable) {
+            perf_vote_lower_ondemand_sdf = dlsym(handle,
+                    "perf_vote_lower_ondemand_sdf");
+
+            if (perf_vote_lower_ondemand_sdf) {
+                perf_vote_lower_ondemand_sdf(1);
+            } else {
+                perf_vote_ondemand_sdf_unavailable = 1;
+                ALOGE("Can't set sampling_down_factor.");
+            }
+        }
+    }
+}
+
+void unvote_ondemand_sdf_low() {
+    void *handle;
+
+    if ((handle = get_qcopt_handle())) {
+        if (!perf_vote_ondemand_sdf_unavailable) {
+            perf_vote_lower_ondemand_sdf = dlsym(handle,
+                    "perf_vote_lower_ondemand_sdf");
+
+            if (perf_vote_lower_ondemand_sdf) {
+                /* Remove vote to lower sampling down factor. */
+                perf_vote_lower_ondemand_sdf(0);
+            } else {
+                perf_vote_ondemand_sdf_unavailable = 1;
+                ALOGE("Can't set sampling_down_factor.");
+            }
+        }
+    }
+}