diff options
Diffstat (limited to 'pvr-source/services4/system/omap4/sgxfreq_on3demand.c')
-rw-r--r-- | pvr-source/services4/system/omap4/sgxfreq_on3demand.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/pvr-source/services4/system/omap4/sgxfreq_on3demand.c b/pvr-source/services4/system/omap4/sgxfreq_on3demand.c new file mode 100644 index 0000000..c4e4bd9 --- /dev/null +++ b/pvr-source/services4/system/omap4/sgxfreq_on3demand.c @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2012 Texas Instruments, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/sysfs.h> +#include <linux/vmalloc.h> +#include <asm/io.h> +#include "sgxfreq.h" + +static int on3demand_start(struct sgxfreq_sgx_data *data); +static void on3demand_stop(void); +static void on3demand_predict(void); +static void on3demand_frame_done(void); +static void on3demand_active(void); +static void on3demand_timeout(struct work_struct *work); + + +static struct sgxfreq_governor on3demand_gov = { + .name = "on3demand", + .gov_start = on3demand_start, + .gov_stop = on3demand_stop, + .sgx_frame_done = on3demand_frame_done, + .sgx_active = on3demand_active, +}; + +static struct on3demand_data { + unsigned int load; + unsigned int up_threshold; + unsigned int down_threshold; + unsigned int history_size; + unsigned long prev_total_idle; + unsigned long prev_total_active; + unsigned int low_load_cnt; + unsigned int poll_interval; + unsigned long delta_active; + unsigned long delta_idle; + bool polling_enabled; + struct delayed_work work; + struct mutex mutex; +} odd; + +#define ON3DEMAND_DEFAULT_UP_THRESHOLD 80 +#define ON3DEMAND_DEFAULT_DOWN_THRESHOLD 30 +#define ON3DEMAND_DEFAULT_HISTORY_SIZE_THRESHOLD 5 +/* For Live wallpaper frame done at interval of ~64ms */ +#define ON3DEMAND_DEFAULT_POLL_INTERVAL 75 + +/*FIXME: This should be dynamic and queried from platform */ +#define ON3DEMAND_FRAME_DONE_DEADLINE_MS 16 + + +/*********************** begin sysfs interface ***********************/ + +extern struct kobject *sgxfreq_kobj; + +static ssize_t show_down_threshold(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", odd.down_threshold); +} + +static ssize_t store_down_threshold(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned int thres; + + ret = sscanf(buf, "%u", &thres); + if (ret != 1) + return -EINVAL; + + mutex_lock(&odd.mutex); + + if (thres <= 100) { + odd.down_threshold = thres; + odd.low_load_cnt = 0; + } else { + return -EINVAL; + } + + mutex_unlock(&odd.mutex); + + return count; +} + +static ssize_t show_up_threshold(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", odd.up_threshold); +} + +static ssize_t store_up_threshold(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned int thres; + + ret = sscanf(buf, "%u", &thres); + if (ret != 1) + return -EINVAL; + + mutex_lock(&odd.mutex); + + if (thres <= 100) { + odd.up_threshold = thres; + odd.low_load_cnt = 0; + } else { + return -EINVAL; + } + + mutex_unlock(&odd.mutex); + + return count; +} + +static ssize_t show_history_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", odd.history_size); +} + +static ssize_t store_history_size(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned int size; + + ret = sscanf(buf, "%u", &size); + if (ret != 1) + return -EINVAL; + + mutex_lock(&odd.mutex); + + if (size >= 1) { + odd.history_size = size; + odd.low_load_cnt = 0; + } else { + return -EINVAL; + } + + mutex_unlock(&odd.mutex); + + return count; +} + +static ssize_t show_load(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", odd.load); +} + +static DEVICE_ATTR(down_threshold, 0644, + show_down_threshold, store_down_threshold); +static DEVICE_ATTR(up_threshold, 0644, + show_up_threshold, store_up_threshold); +static DEVICE_ATTR(history_size, 0644, + show_history_size, store_history_size); +static DEVICE_ATTR(load, 0444, + show_load, NULL); + +static struct attribute *on3demand_attributes[] = { + &dev_attr_down_threshold.attr, + &dev_attr_up_threshold.attr, + &dev_attr_history_size.attr, + &dev_attr_load.attr, + NULL +}; + +static struct attribute_group on3demand_attr_group = { + .attrs = on3demand_attributes, + .name = "on3demand", +}; +/************************ end sysfs interface ************************/ + +int on3demand_init(void) +{ + int ret; + + mutex_init(&odd.mutex); + + ret = sgxfreq_register_governor(&on3demand_gov); + if (ret) + return ret; + + return 0; +} + +int on3demand_deinit(void) +{ + return 0; +} + +static int on3demand_start(struct sgxfreq_sgx_data *data) +{ + int ret; + + odd.load = 0; + odd.up_threshold = ON3DEMAND_DEFAULT_UP_THRESHOLD; + odd.down_threshold = ON3DEMAND_DEFAULT_DOWN_THRESHOLD; + odd.history_size = ON3DEMAND_DEFAULT_HISTORY_SIZE_THRESHOLD; + odd.prev_total_active = 0; + odd.prev_total_idle = 0; + odd.low_load_cnt = 0; + odd.poll_interval = ON3DEMAND_DEFAULT_POLL_INTERVAL; + odd.polling_enabled = false; + + INIT_DELAYED_WORK(&odd.work, on3demand_timeout); + + ret = sysfs_create_group(sgxfreq_kobj, &on3demand_attr_group); + if (ret) + return ret; + + return 0; +} + +static void on3demand_stop(void) +{ + cancel_delayed_work_sync(&odd.work); + sysfs_remove_group(sgxfreq_kobj, &on3demand_attr_group); +} + +static void on3demand_predict(void) +{ + static unsigned short first_sample = 1; + unsigned long total_active, total_idle; + unsigned long freq; + + if (first_sample == 1) { + first_sample = 0; + odd.prev_total_active = sgxfreq_get_total_active_time(); + odd.prev_total_idle = sgxfreq_get_total_idle_time(); + return; + } + + /* Sample new active and idle times */ + total_active = sgxfreq_get_total_active_time(); + total_idle = sgxfreq_get_total_idle_time(); + + /* Compute load */ + odd.delta_active = __delta32(total_active, odd.prev_total_active); + odd.delta_idle = __delta32(total_idle, odd.prev_total_idle); + + /* + * If SGX was active for longer than frame display time (1/fps), + * scale to highest possible frequency. + */ + if (odd.delta_active > ON3DEMAND_FRAME_DONE_DEADLINE_MS) { + odd.low_load_cnt = 0; + sgxfreq_set_freq_request(sgxfreq_get_freq_max()); + } + + if ((odd.delta_active + odd.delta_idle)) + odd.load = (100 * odd.delta_active / (odd.delta_active + odd.delta_idle)); + + odd.prev_total_active = total_active; + odd.prev_total_idle = total_idle; + + /* Scale GPU frequency on purpose */ + if (odd.load >= odd.up_threshold) { + odd.low_load_cnt = 0; + sgxfreq_set_freq_request(sgxfreq_get_freq_max()); + } else if (odd.load <= odd.down_threshold) { + if (odd.low_load_cnt == odd.history_size) { + /* Convert load to frequency */ + freq = (sgxfreq_get_freq() * odd.load) / 100; + sgxfreq_set_freq_request(freq); + odd.low_load_cnt = 0; + } else { + odd.low_load_cnt++; + } + } else { + odd.low_load_cnt = 0; + } +} + + +static void on3demand_active(void) +{ + if (!odd.polling_enabled) { + sgxfreq_set_freq_request(sgxfreq_get_freq_max()); + odd.low_load_cnt = 0; + odd.polling_enabled = true; + schedule_delayed_work(&odd.work, odd.poll_interval * HZ/1000); + } + +} + +static void on3demand_frame_done(void) +{ + if (odd.polling_enabled) { + cancel_delayed_work_sync(&odd.work); + schedule_delayed_work(&odd.work, odd.poll_interval * HZ/1000); + } + on3demand_predict(); +} + +static void on3demand_timeout(struct work_struct *work) +{ + /* + * If sgx was idle all throughout timer disable polling and + * enable it on next sgx active event + */ + if (!odd.delta_active) { + sgxfreq_set_freq_request(sgxfreq_get_freq_min()); + odd.low_load_cnt = 0; + odd.polling_enabled = false; + } else { + on3demand_predict(); + odd.polling_enabled = true; + schedule_delayed_work(&odd.work, odd.poll_interval * HZ/1000); + } +} |