kernel-brax3-ubuntu-touch/drivers/misc/mediatek/performance/fpsgo_v3/fstb/fstb.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

3142 lines
80 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/wait.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/err.h>
#include <linux/syscalls.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/string.h>
#include <linux/average.h>
#include <linux/topology.h>
#include <linux/vmalloc.h>
#include <linux/sched/clock.h>
#include <linux/sched.h>
#include <asm/div64.h>
#include <mt-plat/fpsgo_common.h>
#include "../fbt/include/fbt_cpu.h"
#include "../fbt/include/xgf.h"
#include "fpsgo_base.h"
#include "fpsgo_sysfs.h"
#include "fstb.h"
#include "fstb_usedext.h"
#include "fpsgo_usedext.h"
int (*fpsgo2msync_hint_frameinfo_fp)(unsigned int render_tid, unsigned int reader_bufID,
unsigned int target_fps, unsigned long q2q_time, unsigned long q2q_time2);
EXPORT_SYMBOL(fpsgo2msync_hint_frameinfo_fp);
#if IS_ENABLED(CONFIG_MTK_GPU_SUPPORT)
#include "ged_kpi.h"
#endif
#define mtk_fstb_dprintk_always(fmt, args...) \
pr_debug("[FSTB]" fmt, ##args)
#define mtk_fstb_dprintk(fmt, args...) \
do { \
if (fstb_fps_klog_on == 1) \
pr_debug("[FSTB]" fmt, ##args); \
} while (0)
static struct kobject *fstb_kobj;
static int max_fps_limit = DEFAULT_DFPS;
static int dfps_ceiling = DEFAULT_DFPS;
static int min_fps_limit = CFG_MIN_FPS_LIMIT;
static int fps_error_threshold = 10;
static int QUANTILE = 50;
static int margin_mode = 2;
static int margin_mode_gpu = 2;
static int margin_mode_dbnc_a = 9;
static int margin_mode_dbnc_b = 1;
static int margin_mode_gpu_dbnc_a = 9;
static int margin_mode_gpu_dbnc_b = 1;
static int RESET_TOLERENCE = DEFAULT_RESET_TOLERENCE;
static int JUMP_CHECK_NUM = DEFAULT_JUMP_CHECK_NUM;
static int JUMP_CHECK_Q_PCT = DEFAULT_JUMP_CHECK_Q_PCT;
static int adopt_low_fps = 1;
static int condition_get_fps;
static int condition_fstb_active;
static long long FRAME_TIME_WINDOW_SIZE_US = USEC_PER_SEC;
static int gpu_slowdown_check;
int fstb_no_r_timer_enable;
EXPORT_SYMBOL(fstb_no_r_timer_enable);
module_param(gpu_slowdown_check, int, 0644);
DECLARE_WAIT_QUEUE_HEAD(queue);
DECLARE_WAIT_QUEUE_HEAD(active_queue);
static void fstb_fps_stats(struct work_struct *work);
static DECLARE_WORK(fps_stats_work,
(void *) fstb_fps_stats);
static HLIST_HEAD(fstb_frame_infos);
static HLIST_HEAD(fstb_render_target_fps);
static struct hrtimer hrt;
static struct workqueue_struct *wq;
static struct fps_level fps_global_level;
static int fstb_fps_klog_on;
static int fstb_enable, fstb_active, fstb_active_dbncd, fstb_idle_cnt;
static int fstb_self_ctrl_fps_enable;
static long long last_update_ts;
static int fps_bypass_max = 150;
static int fps_bypass_min = 50;
static int total_fstb_policy_cmd_num;
static int fstb_max_dep_path_num = DEFAULT_MAX_DEP_PATH_NUM;
static int fstb_max_dep_task_num = DEFAULT_MAX_DEP_TASK_NUM;
static void reset_fps_level(void);
static int set_soft_fps_level(struct fps_level level);
static DEFINE_MUTEX(fstb_lock);
static DEFINE_MUTEX(fstb_fps_active_time);
static DEFINE_MUTEX(fstb_ko_lock);
static struct rb_root video_pid_tree;
static DEFINE_MUTEX(fstb_video_pid_tree_lock);
static struct rb_root fstb_policy_cmd_tree;
static DEFINE_MUTEX(fstb_policy_cmd_lock);
void (*gbe_fstb2gbe_poll_fp)(struct hlist_head *list);
int (*fstb_get_target_fps_fp)(int pid, unsigned long long bufID, int tgid,
int dfps_ceiling, int max_dep_path_num, int max_dep_task_num,
int *target_fps_margin, int *ctrl_fps_tid, int *ctrl_fps_flag,
unsigned long long cur_queue_end_ts, int eara_is_active);
EXPORT_SYMBOL(fstb_get_target_fps_fp);
void (*fstb_check_render_info_status_fp)(int clear, unsigned long long cur_ts);
EXPORT_SYMBOL(fstb_check_render_info_status_fp);
static void enable_fstb_timer(void)
{
ktime_t ktime;
ktime = ktime_set(0,
FRAME_TIME_WINDOW_SIZE_US * 1000);
hrtimer_start(&hrt, ktime, HRTIMER_MODE_REL);
}
static void disable_fstb_timer(void)
{
hrtimer_cancel(&hrt);
}
static enum hrtimer_restart mt_fstb(struct hrtimer *timer)
{
if (wq)
queue_work(wq, &fps_stats_work);
return HRTIMER_NORESTART;
}
int is_fstb_enable(void)
{
return fstb_enable;
}
int is_fstb_active(long long time_diff)
{
int active = 0;
ktime_t cur_time;
long long cur_time_us;
cur_time = ktime_get();
cur_time_us = ktime_to_us(cur_time);
mutex_lock(&fstb_fps_active_time);
if (cur_time_us - last_update_ts < time_diff)
active = 1;
mutex_unlock(&fstb_fps_active_time);
return active;
}
EXPORT_SYMBOL(is_fstb_active);
struct k_list {
struct list_head queue_list;
int fpsgo2pwr_pid;
int fpsgo2pwr_fps;
};
static LIST_HEAD(head);
static DEFINE_MUTEX(fpsgo2pwr_lock);
static DECLARE_WAIT_QUEUE_HEAD(pwr_queue);
static void fstb_sentcmd(int pid, int fps)
{
static struct k_list *node;
mutex_lock(&fpsgo2pwr_lock);
node = kmalloc(sizeof(*node), GFP_KERNEL);
if (node == NULL)
goto out;
node->fpsgo2pwr_pid = pid;
node->fpsgo2pwr_fps = fps;
list_add_tail(&node->queue_list, &head);
condition_get_fps = 1;
out:
mutex_unlock(&fpsgo2pwr_lock);
wake_up_interruptible(&pwr_queue);
}
void fpsgo_ctrl2fstb_get_fps(int *pid, int *fps)
{
static struct k_list *node;
wait_event_interruptible(pwr_queue, condition_get_fps);
mutex_lock(&fpsgo2pwr_lock);
if (!list_empty(&head)) {
node = list_first_entry(&head, struct k_list, queue_list);
*pid = node->fpsgo2pwr_pid;
*fps = node->fpsgo2pwr_fps;
list_del(&node->queue_list);
kfree(node);
}
if (list_empty(&head))
condition_get_fps = 0;
mutex_unlock(&fpsgo2pwr_lock);
}
int fpsgo_ctrl2fstb_wait_fstb_active(void)
{
wait_event_interruptible(active_queue, condition_fstb_active);
mutex_lock(&fstb_lock);
condition_fstb_active = 0;
mutex_unlock(&fstb_lock);
return 0;
}
static int fstb_enter_delete_render_info(int clear)
{
int ret = 0;
unsigned long long cur_ts = fpsgo_get_time();
mutex_lock(&fstb_ko_lock);
if (fstb_check_render_info_status_fp)
fstb_check_render_info_status_fp(clear, cur_ts);
else {
ret = -ENOENT;
mtk_fstb_dprintk_always("fstb_check_render_info_status_fp is NULL\n");
}
mutex_unlock(&fstb_ko_lock);
return ret;
}
static int fstb_enter_get_target_fps(int pid, unsigned long long bufID, int tgid,
int *target_fps_margin, unsigned long long cur_queue_end_ts,
int eara_is_active)
{
int ret = 0;
int ctrl_fps_tid = 0, ctrl_fps_flag = 0;
mutex_lock(&fstb_ko_lock);
if (fstb_get_target_fps_fp)
ret = fstb_get_target_fps_fp(pid, bufID, tgid,
dfps_ceiling, fstb_max_dep_path_num, fstb_max_dep_task_num,
target_fps_margin, &ctrl_fps_tid, &ctrl_fps_flag,
cur_queue_end_ts, eara_is_active);
else {
ret = -ENOENT;
mtk_fstb_dprintk_always("fstb_get_target_fps_fp is NULL\n");
}
fpsgo_systrace_c_fstb_man(pid, bufID, ctrl_fps_tid, "ctrl_fps_tid");
fpsgo_systrace_c_fstb_man(pid, bufID, ctrl_fps_flag, "ctrl_fps_flag");
mutex_unlock(&fstb_ko_lock);
return ret;
}
int fpsgo_ctrl2fstb_switch_fstb(int enable)
{
struct FSTB_FRAME_INFO *iter;
struct hlist_node *t;
mutex_lock(&fstb_lock);
if (fstb_enable == enable) {
mutex_unlock(&fstb_lock);
return 0;
}
fstb_enable = enable;
fpsgo_systrace_c_fstb(-200, 0, fstb_enable, "fstb_enable");
mtk_fstb_dprintk_always("%s %d\n", __func__, fstb_enable);
if (!fstb_enable) {
hlist_for_each_entry_safe(iter, t,
&fstb_frame_infos, hlist) {
fstb_enter_delete_render_info(1);
hlist_del(&iter->hlist);
vfree(iter);
}
} else {
if (wq) {
struct work_struct *psWork =
kmalloc(sizeof(struct work_struct), GFP_ATOMIC);
if (psWork) {
INIT_WORK(psWork, fstb_fps_stats);
queue_work(wq, psWork);
}
}
}
mutex_unlock(&fstb_lock);
return 0;
}
static void switch_fstb_active(void)
{
fpsgo_systrace_c_fstb(-200, 0,
fstb_active, "fstb_active");
fpsgo_systrace_c_fstb(-200, 0,
fstb_active_dbncd, "fstb_active_dbncd");
mtk_fstb_dprintk_always("%s %d %d\n",
__func__, fstb_active, fstb_active_dbncd);
enable_fstb_timer();
}
int switch_fps_range(int nr_level, struct fps_level *level)
{
struct fps_level global_level;
if (nr_level != 1)
return 1;
global_level.start = level->start;
global_level.end = level->end;
if (!set_soft_fps_level(global_level))
return 0;
else
return 1;
}
static struct FSTB_FRAME_INFO *add_new_frame_info(int pid, unsigned long long bufID,
int hwui_flag)
{
struct task_struct *tsk = NULL, *gtsk = NULL;
struct FSTB_FRAME_INFO *new_frame_info;
new_frame_info = vmalloc(sizeof(*new_frame_info));
if (new_frame_info == NULL)
goto out;
new_frame_info->pid = pid;
new_frame_info->target_fps = max_fps_limit;
new_frame_info->target_fps_v2 = max_fps_limit;
new_frame_info->target_fps_margin_v2 = 0;
new_frame_info->target_fps_margin = 0;
new_frame_info->target_fps_margin_gpu = 0;
new_frame_info->target_fps_margin2 = 0;
new_frame_info->target_fps_margin_dbnc_a = margin_mode_dbnc_a;
new_frame_info->target_fps_margin_dbnc_b = margin_mode_dbnc_b;
new_frame_info->target_fps_margin_gpu_dbnc_a = margin_mode_gpu_dbnc_a;
new_frame_info->target_fps_margin_gpu_dbnc_b = margin_mode_gpu_dbnc_b;
new_frame_info->target_fps_diff = 0;
new_frame_info->target_fps_notifying = 0;
new_frame_info->queue_fps = max_fps_limit;
new_frame_info->bufid = bufID;
new_frame_info->queue_time_begin = 0;
new_frame_info->queue_time_end = 0;
new_frame_info->weighted_cpu_time_begin = 0;
new_frame_info->weighted_cpu_time_end = 0;
new_frame_info->weighted_gpu_time_begin = 0;
new_frame_info->weighted_gpu_time_end = 0;
new_frame_info->quantile_cpu_time = -1;
new_frame_info->quantile_gpu_time = -1;
new_frame_info->fps_raise_flag = 0;
new_frame_info->vote_i = 0;
new_frame_info->render_idle_cnt = 0;
new_frame_info->hwui_flag = hwui_flag;
new_frame_info->sbe_state = 0;
new_frame_info->self_ctrl_fps_enable = 0;
new_frame_info->tfb_enable = 0;
new_frame_info->notify_target_fps = 0;
rcu_read_lock();
tsk = find_task_by_vpid(pid);
if (tsk) {
get_task_struct(tsk);
gtsk = find_task_by_vpid(tsk->tgid);
put_task_struct(tsk);
if (gtsk)
get_task_struct(gtsk);
}
rcu_read_unlock();
if (gtsk) {
strncpy(new_frame_info->proc_name, gtsk->comm, 16);
new_frame_info->proc_name[15] = '\0';
new_frame_info->proc_id = gtsk->pid;
put_task_struct(gtsk);
} else {
new_frame_info->proc_name[0] = '\0';
new_frame_info->proc_id = 0;
}
out:
return new_frame_info;
}
static void fstb_update_policy_cmd(struct FSTB_FRAME_INFO *iter,
struct FSTB_POLICY_CMD *policy, unsigned long long ts)
{
if (policy) {
iter->self_ctrl_fps_enable = policy->self_ctrl_fps_enable;
iter->tfb_enable = policy->tfb_enable;
iter->notify_target_fps = policy->notify_target_fps;
if (policy->self_ctrl_fps_enable || policy->tfb_enable ||
policy->notify_target_fps)
policy->ts = ts;
} else {
iter->self_ctrl_fps_enable = fstb_self_ctrl_fps_enable ? 1 : 0;
iter->tfb_enable = 0;
iter->notify_target_fps = 0;
}
}
static void fstb_delete_policy_cmd(struct FSTB_POLICY_CMD *iter)
{
unsigned long long min_ts = ULLONG_MAX;
struct FSTB_POLICY_CMD *tmp_iter = NULL, *min_iter = NULL;
struct rb_node *rbn = NULL;
if (iter) {
if (!iter->self_ctrl_fps_enable &&
!iter->tfb_enable && !iter->notify_target_fps) {
min_iter = iter;
goto delete;
} else
return;
}
if (RB_EMPTY_ROOT(&fstb_policy_cmd_tree))
return;
rbn = rb_first(&fstb_policy_cmd_tree);
while (rbn) {
tmp_iter = rb_entry(rbn, struct FSTB_POLICY_CMD, rb_node);
if (tmp_iter->ts < min_ts) {
min_ts = tmp_iter->ts;
min_iter = tmp_iter;
}
rbn = rb_next(rbn);
}
if (!min_iter)
return;
delete:
rb_erase(&min_iter->rb_node, &fstb_policy_cmd_tree);
kfree(min_iter);
total_fstb_policy_cmd_num--;
}
static struct FSTB_POLICY_CMD *fstb_get_policy_cmd(int tgid,
unsigned long long ts, int force)
{
struct rb_node **p = &fstb_policy_cmd_tree.rb_node;
struct rb_node *parent = NULL;
struct FSTB_POLICY_CMD *iter = NULL;
while (*p) {
parent = *p;
iter = rb_entry(parent, struct FSTB_POLICY_CMD, rb_node);
if (tgid < iter->tgid)
p = &(*p)->rb_left;
else if (tgid > iter->tgid)
p = &(*p)->rb_right;
else
return iter;
}
if (!force)
return NULL;
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
if (!iter)
return NULL;
iter->tgid = tgid;
iter->self_ctrl_fps_enable = 0;
iter->tfb_enable = 0;
iter->notify_target_fps = 0;
iter->ts = ts;
rb_link_node(&iter->rb_node, parent, p);
rb_insert_color(&iter->rb_node, &fstb_policy_cmd_tree);
total_fstb_policy_cmd_num++;
if (total_fstb_policy_cmd_num > MAX_FSTB_POLICY_CMD_NUM)
fstb_delete_policy_cmd(NULL);
return iter;
}
static void fstb_set_policy_cmd(int cmd, int value, int tgid,
unsigned long long ts, int op)
{
struct FSTB_POLICY_CMD *iter;
iter = fstb_get_policy_cmd(tgid, ts, op);
if (iter) {
if (cmd == 0)
iter->self_ctrl_fps_enable = value;
else if (cmd == 2)
iter->notify_target_fps = value;
if (!op)
fstb_delete_policy_cmd(iter);
}
}
int switch_thread_max_fps(int pid, int set_max)
{
struct FSTB_FRAME_INFO *iter;
int ret = 0;
mutex_lock(&fstb_lock);
hlist_for_each_entry(iter, &fstb_frame_infos, hlist) {
if (iter->pid != pid)
continue;
iter->sbe_state = set_max;
fpsgo_systrace_c_fstb_man(iter->pid, iter->bufid,
set_max, "sbe_set_max_fps");
fpsgo_systrace_c_fstb_man(iter->pid, iter->bufid,
iter->sbe_state, "sbe_state");
}
mutex_unlock(&fstb_lock);
return ret;
}
static int switch_redner_fps_range(char *proc_name,
pid_t pid, int nr_level, struct fps_level *level)
{
int ret = 0;
int i;
int mode;
struct FSTB_RENDER_TARGET_FPS *rtfiter = NULL;
struct hlist_node *n;
if (nr_level > MAX_NR_RENDER_FPS_LEVELS || nr_level < 0)
return -EINVAL;
/* check if levels are interleaving */
for (i = 0; i < nr_level; i++) {
if (level[i].end > level[i].start ||
(i > 0 && level[i].start > level[i - 1].end)) {
return -EINVAL;
}
}
if (proc_name != NULL && pid == 0)
/*process mode*/
mode = 0;
else if (proc_name == NULL && pid > 0)
/*thread mode*/
mode = 1;
else
return -EINVAL;
mutex_lock(&fstb_lock);
hlist_for_each_entry_safe(rtfiter, n, &fstb_render_target_fps, hlist) {
if ((mode == 0 && !strncmp(
proc_name, rtfiter->process_name, 16)) ||
(mode == 1 && pid == rtfiter->pid)) {
if (nr_level == 0) {
/* delete render target fps*/
hlist_del(&rtfiter->hlist);
kfree(rtfiter);
} else {
/* reassign render target fps */
rtfiter->nr_level = nr_level;
memcpy(rtfiter->level, level,
nr_level * sizeof(struct fps_level));
}
break;
}
}
if (rtfiter == NULL && nr_level) {
/* create new render target fps */
struct FSTB_RENDER_TARGET_FPS *new_render_target_fps;
new_render_target_fps =
kzalloc(sizeof(*new_render_target_fps), GFP_KERNEL);
if (new_render_target_fps == NULL) {
ret = -ENOMEM;
goto err;
}
if (mode == 0) {
new_render_target_fps->pid = 0;
if (!strncpy(
new_render_target_fps->process_name,
proc_name, 16)) {
kfree(new_render_target_fps);
ret = -ENOMEM;
goto err;
}
new_render_target_fps->process_name[15] = '\0';
} else if (mode == 1) {
new_render_target_fps->pid = pid;
new_render_target_fps->process_name[0] = '\0';
}
new_render_target_fps->nr_level = nr_level;
memcpy(new_render_target_fps->level,
level, nr_level * sizeof(struct fps_level));
hlist_add_head(&new_render_target_fps->hlist,
&fstb_render_target_fps);
}
err:
mutex_unlock(&fstb_lock);
return ret;
}
int switch_process_fps_range(char *proc_name,
int nr_level, struct fps_level *level)
{
return switch_redner_fps_range(proc_name, 0, nr_level, level);
}
int switch_thread_fps_range(pid_t pid, int nr_level, struct fps_level *level)
{
return switch_redner_fps_range(NULL, pid, nr_level, level);
}
static int cmplonglong(const void *a, const void *b)
{
return *(long long *)a - *(long long *)b;
}
void fpsgo_ctrl2fstb_dfrc_fps(int fps)
{
mutex_lock(&fstb_lock);
if (fps <= CFG_MAX_FPS_LIMIT && fps >= CFG_MIN_FPS_LIMIT) {
dfps_ceiling = fps;
max_fps_limit = min(dfps_ceiling,
fps_global_level.start);
min_fps_limit = min(dfps_ceiling,
fps_global_level.end);
}
mutex_unlock(&fstb_lock);
}
void gpu_time_update(long long t_gpu, unsigned int cur_freq,
unsigned int cur_max_freq, u64 ulID)
{
struct FSTB_FRAME_INFO *iter;
ktime_t cur_time;
long long cur_time_us;
mutex_lock(&fstb_lock);
if (!fstb_enable) {
mutex_unlock(&fstb_lock);
return;
}
if (!fstb_active)
fstb_active = 1;
if (!fstb_active_dbncd) {
fstb_active_dbncd = 1;
switch_fstb_active();
}
hlist_for_each_entry(iter, &fstb_frame_infos, hlist) {
if (iter->bufid == ulID)
break;
}
if (iter == NULL) {
mutex_unlock(&fstb_lock);
return;
}
iter->gpu_time = t_gpu;
iter->gpu_freq = cur_freq;
if (iter->weighted_gpu_time_begin < 0 ||
iter->weighted_gpu_time_end < 0 ||
iter->weighted_gpu_time_begin > iter->weighted_gpu_time_end ||
iter->weighted_gpu_time_end >= FRAME_TIME_BUFFER_SIZE) {
/* purge all data */
iter->weighted_gpu_time_begin = iter->weighted_gpu_time_end = 0;
}
/*get current time*/
cur_time = ktime_get();
cur_time_us = ktime_to_us(cur_time);
/*remove old entries*/
while (iter->weighted_gpu_time_begin < iter->weighted_gpu_time_end) {
if (iter->weighted_gpu_time_ts[iter->weighted_gpu_time_begin] <
cur_time_us - FRAME_TIME_WINDOW_SIZE_US)
iter->weighted_gpu_time_begin++;
else
break;
}
if (iter->weighted_gpu_time_begin == iter->weighted_gpu_time_end &&
iter->weighted_gpu_time_end == FRAME_TIME_BUFFER_SIZE - 1)
iter->weighted_gpu_time_begin = iter->weighted_gpu_time_end = 0;
/*insert entries to weighted_gpu_time*/
/*if buffer full --> move array align first*/
if (iter->weighted_gpu_time_begin < iter->weighted_gpu_time_end &&
iter->weighted_gpu_time_end == FRAME_TIME_BUFFER_SIZE - 1) {
memmove(iter->weighted_gpu_time,
&(iter->weighted_gpu_time[iter->weighted_gpu_time_begin]),
sizeof(long long) *
(iter->weighted_gpu_time_end - iter->weighted_gpu_time_begin));
memmove(iter->weighted_gpu_time_ts,
&(iter->weighted_gpu_time_ts[iter->weighted_gpu_time_begin]),
sizeof(long long) *
(iter->weighted_gpu_time_end - iter->weighted_gpu_time_begin));
/*reset index*/
iter->weighted_gpu_time_end =
iter->weighted_gpu_time_end - iter->weighted_gpu_time_begin;
iter->weighted_gpu_time_begin = 0;
}
if (cur_max_freq > 0 && cur_max_freq >= cur_freq
&& t_gpu > 0LL && t_gpu < 1000000000LL) {
iter->weighted_gpu_time[iter->weighted_gpu_time_end] =
t_gpu * cur_freq;
do_div(iter->weighted_gpu_time[iter->weighted_gpu_time_end],
cur_max_freq);
iter->weighted_gpu_time_ts[iter->weighted_gpu_time_end] =
cur_time_us;
fpsgo_systrace_c_fstb_man(iter->pid, iter->bufid,
(int)iter->weighted_gpu_time[iter->weighted_gpu_time_end],
"weighted_gpu_time");
iter->weighted_gpu_time_end++;
}
mtk_fstb_dprintk(
"fstb: time %lld %lld t_gpu %lld cur_freq %u cur_max_freq %u\n",
cur_time_us, ktime_to_us(ktime_get())-cur_time_us,
t_gpu, cur_freq, cur_max_freq);
fpsgo_systrace_c_fstb_man(iter->pid, iter->bufid, (int)t_gpu, "t_gpu");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
(int)cur_freq, "cur_gpu_cap");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
(int)cur_max_freq, "max_gpu_cap");
mutex_unlock(&fstb_lock);
}
static int get_gpu_frame_time(struct FSTB_FRAME_INFO *iter)
{
int ret = INT_MAX;
/*copy entries to temp array*/
/*sort this array*/
if (iter->weighted_gpu_time_end - iter->weighted_gpu_time_begin
> 0 &&
iter->weighted_gpu_time_end - iter->weighted_gpu_time_begin
< FRAME_TIME_BUFFER_SIZE) {
memcpy(iter->sorted_weighted_gpu_time,
&(iter->weighted_gpu_time[iter->weighted_gpu_time_begin]),
sizeof(long long) *
(iter->weighted_gpu_time_end - iter->weighted_gpu_time_begin));
sort(iter->sorted_weighted_gpu_time,
iter->weighted_gpu_time_end - iter->weighted_gpu_time_begin,
sizeof(long long), cmplonglong, NULL);
}
/*update nth value*/
if (iter->weighted_gpu_time_end - iter->weighted_gpu_time_begin) {
if (
iter->sorted_weighted_gpu_time[
QUANTILE*
(iter->weighted_gpu_time_end-
iter->weighted_gpu_time_begin)/100]
> INT_MAX)
ret = INT_MAX;
else
ret =
iter->sorted_weighted_gpu_time[
QUANTILE*
(iter->weighted_gpu_time_end-
iter->weighted_gpu_time_begin)/100];
} else
ret = -1;
fpsgo_systrace_c_fstb(iter->pid, iter->bufid, ret,
"quantile_weighted_gpu_time");
return ret;
}
void eara2fstb_get_tfps(int max_cnt, int *is_camera, int *pid, unsigned long long *buf_id,
int *tfps, int *rfps, int *hwui, char name[][16])
{
int count = 0;
struct FSTB_FRAME_INFO *iter;
struct hlist_node *n;
mutex_lock(&fstb_lock);
*is_camera = 0;
hlist_for_each_entry_safe(iter, n, &fstb_frame_infos, hlist) {
if (count == max_cnt)
break;
if (!iter->target_fps || iter->target_fps == -1)
continue;
pid[count] = iter->pid;
hwui[count] = iter->hwui_flag;
buf_id[count] = iter->bufid;
rfps[count] = iter->queue_fps;
if (!iter->target_fps_notifying
|| iter->target_fps_notifying == -1) {
if (!iter->self_ctrl_fps_enable)
tfps[count] = iter->target_fps;
else
tfps[count] = iter->target_fps_v2;
}
else
tfps[count] = iter->target_fps_notifying;
if (name)
strncpy(name[count], iter->proc_name, 16);
count++;
}
mutex_unlock(&fstb_lock);
}
EXPORT_SYMBOL(eara2fstb_get_tfps);
void eara2fstb_tfps_mdiff(int pid, unsigned long long buf_id, int diff,
int tfps)
{
int tmp_target_fps;
struct FSTB_FRAME_INFO *iter;
struct hlist_node *n;
mutex_lock(&fstb_lock);
hlist_for_each_entry_safe(iter, n, &fstb_frame_infos, hlist) {
if (pid == iter->pid && buf_id == iter->bufid) {
if (!iter->self_ctrl_fps_enable)
tmp_target_fps = iter->target_fps;
else
tmp_target_fps = iter->target_fps_v2;
if (tfps != iter->target_fps_notifying
&& tfps != tmp_target_fps)
break;
iter->target_fps_diff = diff;
fpsgo_systrace_c_fstb_man(pid, buf_id, diff, "eara_diff");
if (iter->target_fps_notifying
&& tfps == iter->target_fps_notifying) {
if (!iter->self_ctrl_fps_enable)
iter->target_fps = iter->target_fps_notifying;
else
iter->target_fps_v2 = iter->target_fps_notifying;
iter->target_fps_notifying = 0;
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
iter->target_fps_v2, "fstb_target_fps1");
fpsgo_systrace_c_fstb_man(iter->pid, iter->bufid,
0, "fstb_notifying");
}
break;
}
}
mutex_unlock(&fstb_lock);
}
EXPORT_SYMBOL(eara2fstb_tfps_mdiff);
int (*eara_pre_change_fp)(void);
EXPORT_SYMBOL(eara_pre_change_fp);
int (*eara_pre_change_single_fp)(int pid, unsigned long long bufID,
int target_fps);
EXPORT_SYMBOL(eara_pre_change_single_fp);
static void fstb_change_tfps(struct FSTB_FRAME_INFO *iter, int target_fps,
int notify_eara)
{
int ret = -1;
if (notify_eara && eara_pre_change_single_fp)
ret = eara_pre_change_single_fp(iter->pid, iter->bufid, target_fps);
if ((notify_eara && (ret == -1))
|| iter->target_fps_notifying == target_fps) {
if (!iter->self_ctrl_fps_enable)
iter->target_fps = target_fps;
else
iter->target_fps_v2 = target_fps;
iter->target_fps_notifying = 0;
fpsgo_systrace_c_fstb_man(iter->pid, iter->bufid,
0, "fstb_notifying");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
iter->target_fps_v2, "fstb_target_fps1");
} else {
iter->target_fps_notifying = target_fps;
fpsgo_systrace_c_fstb_man(iter->pid, iter->bufid,
iter->target_fps_notifying, "fstb_notifying");
}
}
int fpsgo_fbt2fstb_update_cpu_frame_info(
int pid,
unsigned long long bufID,
int tgid,
int frame_type,
unsigned long long Q2Q_time,
long long Runnging_time,
int Target_time,
unsigned int Curr_cap,
unsigned int Max_cap,
unsigned long long enqueue_length,
unsigned long long dequeue_length)
{
long long cpu_time_ns = (long long)Runnging_time;
unsigned int max_current_cap = Curr_cap;
unsigned int max_cpu_cap = Max_cap;
unsigned long long wct = 0;
int eara_fps, tolerence_fps;
struct FSTB_FRAME_INFO *iter;
ktime_t cur_time;
long long cur_time_us;
mutex_lock(&fstb_lock);
if (!fstb_enable) {
mutex_unlock(&fstb_lock);
return 0;
}
if (!fstb_active)
fstb_active = 1;
if (!fstb_active_dbncd) {
fstb_active_dbncd = 1;
switch_fstb_active();
}
hlist_for_each_entry(iter, &fstb_frame_infos, hlist) {
if (iter->pid == pid && iter->bufid == bufID)
break;
}
if (iter == NULL) {
mutex_unlock(&fstb_lock);
return 0;
}
mtk_fstb_dprintk(
"pid %d Q2Q_time %lld Runnging_time %lld Curr_cap %u Max_cap %u\n",
pid, Q2Q_time, Runnging_time, Curr_cap, Max_cap);
if (iter->weighted_cpu_time_begin < 0 ||
iter->weighted_cpu_time_end < 0 ||
iter->weighted_cpu_time_begin > iter->weighted_cpu_time_end ||
iter->weighted_cpu_time_end >= FRAME_TIME_BUFFER_SIZE) {
/* purge all data */
iter->weighted_cpu_time_begin = iter->weighted_cpu_time_end = 0;
}
/*get current time*/
cur_time = ktime_get();
cur_time_us = ktime_to_us(cur_time);
/*remove old entries*/
while (iter->weighted_cpu_time_begin < iter->weighted_cpu_time_end) {
if (iter->weighted_cpu_time_ts[iter->weighted_cpu_time_begin] <
cur_time_us - FRAME_TIME_WINDOW_SIZE_US)
iter->weighted_cpu_time_begin++;
else
break;
}
if (iter->weighted_cpu_time_begin == iter->weighted_cpu_time_end &&
iter->weighted_cpu_time_end == FRAME_TIME_BUFFER_SIZE - 1)
iter->weighted_cpu_time_begin = iter->weighted_cpu_time_end = 0;
/*insert entries to weighted_cpu_time*/
/*if buffer full --> move array align first*/
if (iter->weighted_cpu_time_begin < iter->weighted_cpu_time_end &&
iter->weighted_cpu_time_end == FRAME_TIME_BUFFER_SIZE - 1) {
memmove(iter->weighted_cpu_time,
&(iter->weighted_cpu_time[iter->weighted_cpu_time_begin]),
sizeof(long long) *
(iter->weighted_cpu_time_end -
iter->weighted_cpu_time_begin));
memmove(iter->weighted_cpu_time_ts,
&(iter->weighted_cpu_time_ts[iter->weighted_cpu_time_begin]),
sizeof(long long) *
(iter->weighted_cpu_time_end -
iter->weighted_cpu_time_begin));
/*reset index*/
iter->weighted_cpu_time_end =
iter->weighted_cpu_time_end - iter->weighted_cpu_time_begin;
iter->weighted_cpu_time_begin = 0;
}
if (max_cpu_cap > 0 && Max_cap > Curr_cap
&& cpu_time_ns > 0LL && cpu_time_ns < 1000000000LL) {
wct = cpu_time_ns * max_current_cap;
do_div(wct, max_cpu_cap);
} else
goto out;
fpsgo_systrace_c_fstb_man(pid, iter->bufid, (int)wct,
"weighted_cpu_time");
iter->weighted_cpu_time[iter->weighted_cpu_time_end] =
wct;
iter->weighted_cpu_time_ts[iter->weighted_cpu_time_end] =
cur_time_us;
iter->weighted_cpu_time_end++;
out:
mtk_fstb_dprintk(
"pid %d fstb: time %lld %lld cpu_time_ns %lld max_current_cap %u max_cpu_cap %u\n"
, pid, cur_time_us, ktime_to_us(ktime_get())-cur_time_us,
cpu_time_ns, max_current_cap, max_cpu_cap);
/* parse cpu time of each frame to ged_kpi */
iter->cpu_time = cpu_time_ns;
if (!iter->self_ctrl_fps_enable) {
eara_fps = iter->target_fps;
if (iter->target_fps && iter->target_fps != -1 && iter->target_fps_diff
&& !iter->target_fps_margin && !iter->target_fps_margin_gpu) {
eara_fps = iter->target_fps * 1000 + iter->target_fps_diff;
eara_fps /= 1000;
eara_fps = clamp(eara_fps, min_fps_limit, max_fps_limit);
}
tolerence_fps = iter->target_fps_margin_gpu;
} else {
eara_fps = iter->target_fps_v2;
if (iter->target_fps_v2 && iter->target_fps_v2 != -1 && iter->target_fps_diff
&& !iter->target_fps_margin_v2) {
eara_fps = iter->target_fps_v2 * 1000 + iter->target_fps_diff;
eara_fps /= 1000;
eara_fps = clamp(eara_fps, min_fps_limit, max_fps_limit);
}
tolerence_fps = iter->target_fps_margin_v2;
}
switch (iter->sbe_state) {
case 0:
break;
case 1:
eara_fps = max_fps_limit;
tolerence_fps = 0;
break;
default:
break;
}
if (gpu_slowdown_check && !iter->target_fps_diff
&& iter->cpu_time > Target_time && iter->cpu_time > iter->gpu_time)
eara_fps = iter->target_fps;
ged_kpi_set_target_FPS_margin(iter->bufid, eara_fps, tolerence_fps,
iter->target_fps_diff, iter->cpu_time);
if (fpsgo2msync_hint_frameinfo_fp)
fpsgo2msync_hint_frameinfo_fp((unsigned int)iter->pid, iter->bufid,
iter->target_fps, Q2Q_time, Q2Q_time - enqueue_length - dequeue_length);
fpsgo_systrace_c_fstb_man(pid, iter->bufid, (int)cpu_time_ns, "t_cpu");
fpsgo_systrace_c_fstb(pid, iter->bufid, (int)max_current_cap,
"cur_cpu_cap");
fpsgo_systrace_c_fstb(pid, iter->bufid, (int)max_cpu_cap,
"max_cpu_cap");
mutex_unlock(&fstb_lock);
return 0;
}
static void fstb_calculate_target_fps(int tgid, int pid,
unsigned long long bufID, unsigned long long cur_queue_end_ts)
{
int i, target_fps, margin = 0, eara_is_active = 0;
int target_fps_old = max_fps_limit, target_fps_new = max_fps_limit;
struct FSTB_FRAME_INFO *iter;
struct FSTB_RENDER_TARGET_FPS *rtfiter;
mutex_lock(&fstb_lock);
hlist_for_each_entry(iter, &fstb_frame_infos, hlist) {
if (iter->pid == pid && iter->bufid == bufID)
break;
}
if (!iter)
goto out;
margin = iter->target_fps_margin_v2;
if (iter->target_fps_diff)
eara_is_active = 1;
else
eara_is_active = 0;
mutex_unlock(&fstb_lock);
target_fps = fstb_enter_get_target_fps(pid, bufID, tgid,
&margin, cur_queue_end_ts, eara_is_active);
mutex_lock(&fstb_lock);
hlist_for_each_entry(iter, &fstb_frame_infos, hlist) {
if (iter->pid == pid && iter->bufid == bufID)
break;
}
if (!iter)
goto out;
if (target_fps <= 0) {
fstb_change_tfps(iter, iter->target_fps, 1);
fpsgo_main_trace("[fstb][%d][0x%llx] | back to v1 (%d)",
iter->pid, iter->bufid, iter->target_fps);
} else {
target_fps_old = iter->target_fps_v2;
target_fps_new = target_fps;
if (iter->notify_target_fps > 0) {
target_fps_new = iter->notify_target_fps;
margin = 0;
}
hlist_for_each_entry(rtfiter, &fstb_render_target_fps, hlist) {
if (!strncmp(iter->proc_name, rtfiter->process_name, 16) ||
rtfiter->pid == iter->pid) {
for (i = rtfiter->nr_level-1; i >= 0; i--) {
if (rtfiter->level[i].start >= target_fps_new) {
target_fps_new =
target_fps_new >= rtfiter->level[i].end ?
target_fps_new : rtfiter->level[i].end;
break;
}
}
if (i < 0)
target_fps_new = rtfiter->level[0].start;
if (target_fps_new == rtfiter->level[0].start && margin)
margin = 0;
}
}
if (target_fps_new > max_fps_limit)
target_fps_new = max_fps_limit;
if (target_fps_new < min_fps_limit)
target_fps_new = min_fps_limit;
if (target_fps_old != target_fps_new)
fstb_change_tfps(iter, target_fps_new, 1);
}
iter->target_fps_margin_v2 = margin;
fpsgo_main_trace("[fstb][%d][0x%llx] | target_fps:%d(%d)(%d)(%d)(%d)",
iter->pid, iter->bufid, iter->target_fps_v2,
target_fps_old, target_fps_new, target_fps, iter->notify_target_fps);
fpsgo_main_trace("[fstb][%d][0x%llx] | dfrc:%d eara:%d margin:%d",
iter->pid, iter->bufid,
dfps_ceiling, eara_is_active, iter->target_fps_margin_v2);
fpsgo_systrace_c_fstb(iter->pid, iter->bufid, iter->target_fps_v2, "target_fps_v2");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid, iter->target_fps_margin_v2,
"target_fps_margin_v2");
out:
mutex_unlock(&fstb_lock);
}
#define FSTB_CONTAINER_OF(ptr, type, member) \
((type *)(((char *)ptr) - offsetof(type, member)))
static void fstb_notifier_wq_cb(struct work_struct *psWork)
{
struct FSTB_NOTIFIER_PUSH_TAG *vpPush =
FSTB_CONTAINER_OF(psWork,
struct FSTB_NOTIFIER_PUSH_TAG, sWork);
if (!vpPush)
return;
fstb_calculate_target_fps(vpPush->tgid, vpPush->pid, vpPush->bufid,
vpPush->cur_queue_end_ts);
kfree(vpPush);
}
void fpsgo_comp2fstb_prepare_calculate_target_fps(int pid, unsigned long long bufID,
unsigned long long cur_queue_end_ts)
{
struct FSTB_FRAME_INFO *iter;
struct FSTB_NOTIFIER_PUSH_TAG *vpPush;
mutex_lock(&fstb_lock);
hlist_for_each_entry(iter, &fstb_frame_infos, hlist) {
if (iter->pid == pid && iter->bufid == bufID)
break;
}
if (!iter)
goto out;
if (!iter->self_ctrl_fps_enable)
iter->self_ctrl_fps_enable = fstb_self_ctrl_fps_enable ? 1 : 0;
if (!iter->self_ctrl_fps_enable)
goto out;
vpPush =
(struct FSTB_NOTIFIER_PUSH_TAG *)
kmalloc(sizeof(struct FSTB_NOTIFIER_PUSH_TAG), GFP_ATOMIC);
if (!vpPush)
goto out;
if (!wq) {
kfree(vpPush);
goto out;
}
vpPush->tgid = iter->proc_id;
vpPush->pid = pid;
vpPush->bufid = bufID;
vpPush->cur_queue_end_ts = cur_queue_end_ts;
INIT_WORK(&vpPush->sWork, fstb_notifier_wq_cb);
queue_work(wq, &vpPush->sWork);
out:
mutex_unlock(&fstb_lock);
}
static long long get_cpu_frame_time(struct FSTB_FRAME_INFO *iter)
{
long long ret = INT_MAX;
/*copy entries to temp array*/
/*sort this array*/
if (iter->weighted_cpu_time_end - iter->weighted_cpu_time_begin > 0 &&
iter->weighted_cpu_time_end - iter->weighted_cpu_time_begin <
FRAME_TIME_BUFFER_SIZE) {
memcpy(iter->sorted_weighted_cpu_time,
&(iter->weighted_cpu_time[iter->weighted_cpu_time_begin]),
sizeof(long long) *
(iter->weighted_cpu_time_end -
iter->weighted_cpu_time_begin));
sort(iter->sorted_weighted_cpu_time,
iter->weighted_cpu_time_end -
iter->weighted_cpu_time_begin,
sizeof(long long), cmplonglong, NULL);
}
/*update nth value*/
if (iter->weighted_cpu_time_end - iter->weighted_cpu_time_begin) {
if (
iter->sorted_weighted_cpu_time[
QUANTILE*
(iter->weighted_cpu_time_end-
iter->weighted_cpu_time_begin)/100]
> INT_MAX)
ret = INT_MAX;
else
ret =
iter->sorted_weighted_cpu_time[
QUANTILE*
(iter->weighted_cpu_time_end-
iter->weighted_cpu_time_begin)/100];
} else
ret = -1;
fpsgo_systrace_c_fstb(iter->pid, iter->bufid, ret,
"quantile_weighted_cpu_time");
return ret;
}
void fstb_set_video_pid(int pid)
{
struct video_info *video_info_instance;
if (pid == 0)
return;
mutex_lock(&fstb_video_pid_tree_lock);
video_info_instance = fstb_search_and_add_video_info(pid, 1);
mutex_unlock(&fstb_video_pid_tree_lock);
if (video_info_instance)
fpsgo_systrace_c_fstb_man(-100, 0,
video_info_instance->count_instance, "video_pid[%d]", pid);
fpsgo_main_trace("[FSTB_Video]: pid=%d, %s", pid, __func__);
}
void fstb_clear_video_pid(int pid)
{
if (pid == 0)
return;
mutex_lock(&fstb_video_pid_tree_lock);
fstb_delete_video_info(pid);
mutex_unlock(&fstb_video_pid_tree_lock);
fpsgo_main_trace("[FSTB_Video]: pid=%d, %s", pid, __func__);
}
static int fstb_get_queue_fps2(struct FSTB_FRAME_INFO *iter)
{
unsigned long long retval = 0;
unsigned long long duration = 0;
int tmp_jump_check_num = 0;
tmp_jump_check_num = min(JUMP_CHECK_NUM,
iter->target_fps * JUMP_CHECK_Q_PCT / 100);
tmp_jump_check_num =
tmp_jump_check_num <= 1 ? 2 : tmp_jump_check_num;
duration =
iter->queue_time_ts[iter->queue_time_end - 1] -
iter->queue_time_ts[iter->queue_time_end - tmp_jump_check_num];
do_div(duration, tmp_jump_check_num - 1);
retval = 1000000000ULL;
do_div(retval, duration);
return (int)retval;
}
static int calculate_fps_limit(struct FSTB_FRAME_INFO *iter, int target_fps);
static int mode(int a[], int n)
{
int maxValue = 0, maxCount = 0, i, j;
for (i = 0; i < n; ++i) {
int count = 0;
for (j = 0; j < n; ++j) {
if (a[j] == a[i])
++count;
}
if (count > maxCount) {
maxCount = count;
maxValue = a[i];
}
}
if (maxCount)
return maxValue;
else
return a[n-1];
}
void fpsgo_comp2fstb_queue_time_update(int pid, unsigned long long bufID,
int frame_type, unsigned long long ts,
int api, int hwui_flag)
{
struct FSTB_FRAME_INFO *iter;
struct FSTB_POLICY_CMD *policy;
ktime_t cur_time;
long long cur_time_us = 0;
int tmp_jump_check_num = 0;
mutex_lock(&fstb_lock);
if (!fstb_enable) {
mutex_unlock(&fstb_lock);
return;
}
if (!fstb_active)
fstb_active = 1;
if (!fstb_active_dbncd) {
fstb_active_dbncd = 1;
switch_fstb_active();
}
cur_time = ktime_get();
cur_time_us = ktime_to_us(cur_time);
hlist_for_each_entry(iter, &fstb_frame_infos, hlist) {
if (iter->pid == pid && (iter->bufid == bufID ||
iter->bufid == 0))
break;
}
if (iter == NULL) {
struct FSTB_FRAME_INFO *new_frame_info;
new_frame_info = add_new_frame_info(pid, bufID, hwui_flag);
if (new_frame_info == NULL)
goto out;
iter = new_frame_info;
hlist_add_head(&iter->hlist, &fstb_frame_infos);
}
if (iter->bufid == 0)
iter->bufid = bufID;
iter->hwui_flag = hwui_flag;
if (wq_has_sleeper(&active_queue)) {
condition_fstb_active = 1;
wake_up_interruptible(&active_queue);
}
mutex_lock(&fstb_policy_cmd_lock);
policy = fstb_get_policy_cmd(iter->proc_id, ts, 0);
if (!policy)
fstb_update_policy_cmd(iter, NULL, ts);
else
fstb_update_policy_cmd(iter, policy, ts);
mutex_unlock(&fstb_policy_cmd_lock);
if (iter->queue_time_begin < 0 ||
iter->queue_time_end < 0 ||
iter->queue_time_begin > iter->queue_time_end ||
iter->queue_time_end >= FRAME_TIME_BUFFER_SIZE) {
/* purge all data */
iter->queue_time_begin = iter->queue_time_end = 0;
}
/*remove old entries*/
while (iter->queue_time_begin < iter->queue_time_end) {
if (iter->queue_time_ts[iter->queue_time_begin] < ts -
(long long)FRAME_TIME_WINDOW_SIZE_US * 1000)
iter->queue_time_begin++;
else
break;
}
if (iter->queue_time_begin == iter->queue_time_end &&
iter->queue_time_end == FRAME_TIME_BUFFER_SIZE - 1)
iter->queue_time_begin = iter->queue_time_end = 0;
/*insert entries to weighted_display_time*/
/*if buffer full --> move array align first*/
if (iter->queue_time_begin < iter->queue_time_end &&
iter->queue_time_end == FRAME_TIME_BUFFER_SIZE - 1) {
memmove(iter->queue_time_ts,
&(iter->queue_time_ts[iter->queue_time_begin]),
sizeof(unsigned long long) *
(iter->queue_time_end - iter->queue_time_begin));
/*reset index*/
iter->queue_time_end =
iter->queue_time_end - iter->queue_time_begin;
iter->queue_time_begin = 0;
}
iter->queue_time_ts[iter->queue_time_end] = ts;
iter->queue_time_end++;
if (!JUMP_CHECK_NUM)
goto out;
tmp_jump_check_num = min(JUMP_CHECK_NUM,
iter->target_fps * JUMP_CHECK_Q_PCT / 100);
tmp_jump_check_num =
tmp_jump_check_num <= 1 ? 2 : tmp_jump_check_num;
if (iter->queue_time_end - iter->queue_time_begin >= tmp_jump_check_num) {
int tmp_q_fps = fstb_get_queue_fps2(iter);
int tmp_target_fps = iter->target_fps;
int tmp_vote_fps = iter->target_fps;
fpsgo_systrace_c_fstb(iter->pid, iter->bufid, tmp_q_fps,
"tmp_q_fps");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid, tmp_jump_check_num,
"tmp_jump_check_num");
if (tmp_q_fps >= iter->target_fps +
iter->target_fps_margin2) {
tmp_target_fps =
calculate_fps_limit(iter, tmp_q_fps);
}
if (iter->vote_i < JUMP_VOTE_MAX_I) {
iter->vote_fps[iter->vote_i] = tmp_target_fps;
iter->vote_i++;
} else {
memmove(iter->vote_fps,
&(iter->vote_fps[JUMP_VOTE_MAX_I - tmp_jump_check_num + 1]),
sizeof(int) * (tmp_jump_check_num - 1));
iter->vote_i = tmp_jump_check_num - 1;
iter->vote_fps[iter->vote_i] = tmp_target_fps;
iter->vote_i++;
}
if (iter->vote_i >= tmp_jump_check_num) {
tmp_vote_fps =
mode(
&(iter->vote_fps[iter->vote_i - tmp_jump_check_num]),
tmp_jump_check_num);
fpsgo_main_trace("fstb_vote_target_fps %d",
tmp_vote_fps);
}
if (tmp_vote_fps > iter->target_fps) {
iter->fps_raise_flag = 1;
if (!iter->self_ctrl_fps_enable)
fstb_change_tfps(iter, tmp_vote_fps, 1);
else
iter->target_fps = tmp_vote_fps;
}
}
out:
mutex_unlock(&fstb_lock);
mutex_lock(&fstb_fps_active_time);
if (cur_time_us)
last_update_ts = cur_time_us;
mutex_unlock(&fstb_fps_active_time);
}
static int fstb_get_queue_fps1(struct FSTB_FRAME_INFO *iter,
long long interval, int *is_fps_update)
{
int i = iter->queue_time_begin, j;
unsigned long long queue_fps;
unsigned long long frame_interval_count = 0;
unsigned long long avg_frame_interval = 0;
unsigned long long retval = 0;
int frame_count = 0;
/* remove old entries */
while (i < iter->queue_time_end) {
if (iter->queue_time_ts[i] < sched_clock() - interval * 1000)
i++;
else
break;
}
/* filter and asfc evaluation*/
for (j = i + 1; j < iter->queue_time_end; j++) {
if ((iter->queue_time_ts[j] -
iter->queue_time_ts[j - 1]) <
DISPLAY_FPS_FILTER_NS) {
avg_frame_interval +=
(iter->queue_time_ts[j] -
iter->queue_time_ts[j - 1]);
frame_interval_count++;
}
frame_count++;
}
*is_fps_update = frame_count ? 1 : 0;
queue_fps = (long long)(iter->queue_time_end - i) * 1000000LL;
do_div(queue_fps, (unsigned long long)interval);
if (avg_frame_interval != 0) {
retval = 1000000000ULL * frame_interval_count;
do_div(retval, avg_frame_interval);
if (frame_interval_count < DISPLAY_FPS_FILTER_NUM)
retval = -1;
fpsgo_systrace_c_fstb_man(iter->pid, iter->bufid, (int)retval,
"queue_fps");
return retval;
}
if (iter->queue_fps == -1 || frame_count)
retval = -1;
else
retval = 0;
fpsgo_systrace_c_fstb_man(iter->pid, iter->bufid,
retval, "queue_fps");
return retval;
}
static int fps_update(struct FSTB_FRAME_INFO *iter)
{
int is_fps_update = 0;
iter->queue_fps =
fstb_get_queue_fps1(iter, FRAME_TIME_WINDOW_SIZE_US, &is_fps_update);
return is_fps_update;
}
/* Calculate FPS limit:
* @retval new fps limit
*
* search in ascending order
* For discrete range:
* same as before, we select the upper one level that
* is just larger than current target.
* For contiguous range:
* if the new limit is between [start,end], use new limit
*/
static int calculate_fps_limit(struct FSTB_FRAME_INFO *iter, int target_fps)
{
int ret_fps = target_fps;
int asfc_turn = 0;
int i;
struct FSTB_RENDER_TARGET_FPS *rtfiter = NULL;
if (iter->notify_target_fps > 0)
ret_fps = iter->notify_target_fps;
hlist_for_each_entry(rtfiter, &fstb_render_target_fps, hlist) {
if (!strncmp(iter->proc_name, rtfiter->process_name, 16)
|| rtfiter->pid == iter->pid) {
for (i = rtfiter->nr_level - 1; i >= 0; i--) {
if (rtfiter->level[i].start >= target_fps) {
ret_fps =
target_fps >=
rtfiter->level[i].end ?
target_fps :
rtfiter->level[i].end;
break;
}
}
if (i < 0)
ret_fps = rtfiter->level[0].start;
else if (i && ret_fps == rtfiter->level[i].start)
asfc_turn = 1;
break;
}
}
if (ret_fps == 30 && max_fps_limit > 30) {
if (rtfiter && rtfiter->level[0].start > 30)
asfc_turn = 1;
else if (!rtfiter)
asfc_turn = 1;
} else if (ret_fps == 60 && max_fps_limit > 60) {
if (rtfiter && rtfiter->level[0].start > 60)
asfc_turn = 1;
else if (!rtfiter)
asfc_turn = 1;
} else if (ret_fps == 90 && max_fps_limit > 90) {
if (rtfiter && rtfiter->level[0].start > 90)
asfc_turn = 1;
else if (!rtfiter)
asfc_turn = 1;
}
switch (margin_mode) {
case 0:
iter->target_fps_margin =
ret_fps >= max_fps_limit ? 0 : RESET_TOLERENCE;
break;
case 1:
if (ret_fps >= max_fps_limit)
iter->target_fps_margin = 0;
else if (asfc_turn)
iter->target_fps_margin = RESET_TOLERENCE;
else
iter->target_fps_margin = 0;
break;
case 2:
if (ret_fps >= max_fps_limit) {
iter->target_fps_margin = 0;
iter->target_fps_margin_dbnc_a =
margin_mode_dbnc_a;
iter->target_fps_margin_dbnc_b =
margin_mode_dbnc_b;
} else if (asfc_turn) {
if (iter->target_fps_margin_dbnc_a > 0) {
iter->target_fps_margin = 0;
iter->target_fps_margin_dbnc_a--;
} else if (iter->target_fps_margin_dbnc_b > 0) {
iter->target_fps_margin = RESET_TOLERENCE;
iter->target_fps_margin_dbnc_b--;
if (iter->target_fps_margin_dbnc_b <= 0) {
iter->target_fps_margin_dbnc_a =
margin_mode_dbnc_a;
iter->target_fps_margin_dbnc_b =
margin_mode_dbnc_b;
}
} else {
iter->target_fps_margin = RESET_TOLERENCE;
iter->target_fps_margin_dbnc_a =
margin_mode_dbnc_a;
iter->target_fps_margin_dbnc_b =
margin_mode_dbnc_b;
}
} else {
iter->target_fps_margin = 0;
iter->target_fps_margin_dbnc_a =
margin_mode_dbnc_a;
iter->target_fps_margin_dbnc_b =
margin_mode_dbnc_b;
}
break;
default:
iter->target_fps_margin =
ret_fps >= max_fps_limit ? 0 : RESET_TOLERENCE;
break;
}
switch (margin_mode_gpu) {
case 0:
iter->target_fps_margin_gpu =
ret_fps >= max_fps_limit ? 0 : RESET_TOLERENCE;
break;
case 1:
if (ret_fps >= max_fps_limit)
iter->target_fps_margin_gpu = 0;
else if (asfc_turn)
iter->target_fps_margin_gpu = RESET_TOLERENCE;
else
iter->target_fps_margin_gpu = 0;
break;
case 2:
if (ret_fps >= max_fps_limit) {
iter->target_fps_margin_gpu = 0;
iter->target_fps_margin_gpu_dbnc_a =
margin_mode_gpu_dbnc_a;
iter->target_fps_margin_gpu_dbnc_b =
margin_mode_gpu_dbnc_b;
} else if (asfc_turn) {
if (iter->target_fps_margin_gpu_dbnc_a > 0) {
iter->target_fps_margin_gpu = 0;
iter->target_fps_margin_gpu_dbnc_a--;
} else if (iter->target_fps_margin_gpu_dbnc_b > 0) {
iter->target_fps_margin_gpu = RESET_TOLERENCE;
iter->target_fps_margin_gpu_dbnc_b--;
if (iter->target_fps_margin_gpu_dbnc_b <= 0) {
iter->target_fps_margin_gpu_dbnc_a =
margin_mode_gpu_dbnc_a;
iter->target_fps_margin_gpu_dbnc_b =
margin_mode_gpu_dbnc_b;
}
} else {
iter->target_fps_margin_gpu = RESET_TOLERENCE;
iter->target_fps_margin_gpu_dbnc_a =
margin_mode_gpu_dbnc_a;
iter->target_fps_margin_gpu_dbnc_b =
margin_mode_gpu_dbnc_b;
}
} else {
iter->target_fps_margin_gpu = 0;
iter->target_fps_margin_gpu_dbnc_a =
margin_mode_gpu_dbnc_a;
iter->target_fps_margin_gpu_dbnc_b =
margin_mode_gpu_dbnc_b;
}
break;
default:
iter->target_fps_margin_gpu =
ret_fps >= max_fps_limit ? 0 : RESET_TOLERENCE;
break;
}
iter->target_fps_margin2 = asfc_turn ? RESET_TOLERENCE : 0;
if (iter->notify_target_fps > 0) {
if (iter->target_fps_margin)
iter->target_fps_margin = 0;
if (iter->target_fps_margin_gpu)
iter->target_fps_margin_gpu = 0;
}
if (ret_fps >= max_fps_limit)
return max_fps_limit;
if (ret_fps <= min_fps_limit)
return min_fps_limit;
return ret_fps;
}
static int cal_target_fps(struct FSTB_FRAME_INFO *iter)
{
long long target_limit = max_fps_limit;
int cur_cpu_time, cur_gpu_time;
cur_cpu_time = get_cpu_frame_time(iter);
cur_gpu_time = get_gpu_frame_time(iter);
iter->quantile_cpu_time = cur_cpu_time;
iter->quantile_gpu_time = cur_gpu_time;
if (iter->fps_raise_flag == 1) {
target_limit = iter->target_fps;
/*decrease*/
} else if (iter->target_fps - iter->queue_fps >
iter->target_fps * fps_error_threshold / 100) {
target_limit = iter->queue_fps;
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
(int)target_limit, "tmp_target_limit");
} else {
target_limit = iter->target_fps;
}
iter->fps_raise_flag = 0;
return target_limit;
}
#define FSTB_SEC_DIVIDER 1000000000
#define FSTB_MSEC_DIVIDER 1000000000000ULL
void fpsgo_fbt2fstb_query_fps(int pid, unsigned long long bufID,
int *target_fps, int *target_cpu_time, int *fps_margin,
int tgid, int *quantile_cpu_time,
int *quantile_gpu_time, int *target_fpks, int *cooler_on)
{
struct FSTB_FRAME_INFO *iter = NULL;
unsigned long long total_time, v_c_time;
int tolerence_fps = 0;
int eara_fps = max_fps_limit;
mutex_lock(&fstb_lock);
hlist_for_each_entry(iter, &fstb_frame_infos, hlist) {
if (iter->pid == pid && iter->bufid == bufID)
break;
}
*cooler_on = 0;
if (!iter) {
(*quantile_cpu_time) = -1;
(*quantile_gpu_time) = -1;
*target_fps = max_fps_limit;
tolerence_fps = 0;
total_time = (int)FSTB_SEC_DIVIDER;
total_time =
div64_u64(total_time,
(*target_fps) + tolerence_fps > max_fps_limit ?
max_fps_limit : (*target_fps) + tolerence_fps);
v_c_time = total_time;
eara_fps = *target_fps * 1000;
} else {
(*quantile_cpu_time) = iter->quantile_cpu_time;
(*quantile_gpu_time) = iter->quantile_gpu_time;
if (!iter->self_ctrl_fps_enable) {
if (iter->target_fps && iter->target_fps != -1
&& iter->target_fps_diff
&& !iter->target_fps_margin
&& !iter->target_fps_margin_gpu) {
int max_mlimit = max_fps_limit * 1000;
int min_mlimit = min_fps_limit * 1000;
eara_fps = iter->target_fps * 1000;
fpsgo_systrace_c_fstb_man(pid, iter->bufid,
iter->target_fps_diff, "eara_diff");
eara_fps += iter->target_fps_diff;
eara_fps = clamp(eara_fps, min_mlimit, max_mlimit);
*cooler_on = 1;
*target_fps = eara_fps / 1000;
tolerence_fps = iter->target_fps_margin * 1000;
total_time = (unsigned long long)FSTB_MSEC_DIVIDER;
total_time =
div64_u64(total_time,
(eara_fps + tolerence_fps) > max_mlimit ?
max_mlimit : (eara_fps + tolerence_fps));
} else {
switch (iter->sbe_state) {
case 1:
*target_fps = max_fps_limit;
tolerence_fps = 0;
break;
case 0:
default:
*target_fps = iter->target_fps;
tolerence_fps = iter->target_fps_margin;
if ((iter->queue_fps >
fps_global_level.start * fps_bypass_max / 100 ||
iter->queue_fps <
fps_global_level.end *
fps_bypass_min / 100) &&
iter->queue_fps > 0)
*target_fps = -1;
break;
}
eara_fps = *target_fps * 1000;
total_time = (int)FSTB_SEC_DIVIDER;
total_time =
div64_u64(total_time,
(*target_fps) + tolerence_fps > max_fps_limit ?
max_fps_limit : (*target_fps) + tolerence_fps);
}
} else {
if (iter->target_fps_v2 && iter->target_fps_v2 != -1
&& iter->target_fps_diff
&& !iter->target_fps_margin_v2) {
int max_mlimit = max_fps_limit * 1000;
int min_mlimit = min_fps_limit * 1000;
eara_fps = iter->target_fps_v2 * 1000;
fpsgo_systrace_c_fstb_man(pid, iter->bufid,
iter->target_fps_diff, "eara_diff");
eara_fps += iter->target_fps_diff;
eara_fps = clamp(eara_fps, min_mlimit, max_mlimit);
*cooler_on = 1;
*target_fps = eara_fps / 1000;
tolerence_fps = iter->target_fps_margin_v2 * 1000;
total_time = (unsigned long long)FSTB_MSEC_DIVIDER;
total_time =
div64_u64(total_time,
(eara_fps + tolerence_fps) > max_mlimit ?
max_mlimit : (eara_fps + tolerence_fps));
} else {
*target_fps = iter->target_fps_v2;
tolerence_fps = iter->target_fps_margin_v2;
total_time = (int)FSTB_SEC_DIVIDER;
total_time =
div64_u64(total_time,
(*target_fps) + tolerence_fps > max_fps_limit ?
max_fps_limit : (*target_fps) + tolerence_fps);
eara_fps = *target_fps * 1000;
}
}
v_c_time = total_time;
if (!adopt_low_fps && iter->queue_fps == -1)
*target_fps = -1;
}
*target_cpu_time = v_c_time;
*fps_margin = tolerence_fps;
*target_fpks = eara_fps;
mutex_unlock(&fstb_lock);
}
static int fpsgo_power2fstb_get_target_fps(struct FSTB_FRAME_INFO *iter)
{
int i;
int ret = -1;
int target_fps_arb_arr[4];
if (!iter)
goto out;
if (iter->self_ctrl_fps_enable || fstb_self_ctrl_fps_enable) {
target_fps_arb_arr[0] = 2;
target_fps_arb_arr[1] = iter->target_fps_v2;
} else {
target_fps_arb_arr[0] = 1;
target_fps_arb_arr[1] = iter->target_fps;
}
target_fps_arb_arr[2] = -1;
if (iter->sbe_state == 1)
target_fps_arb_arr[2] = max_fps_limit;
if (fps_global_level.start == CFG_MAX_FPS_LIMIT &&
fps_global_level.end == CFG_MIN_FPS_LIMIT)
target_fps_arb_arr[3] = -1;
else
target_fps_arb_arr[3] = fps_global_level.start;
if (target_fps_arb_arr[0] == 2)
ret = target_fps_arb_arr[1];
for (i = 2; i < 4; i++) {
if (target_fps_arb_arr[i] > 0)
ret = target_fps_arb_arr[i];
}
out:
return ret;
}
static int cmp_powerfps(const void *x1, const void *x2)
{
const struct FSTB_POWERFPS_LIST *r1 = x1;
const struct FSTB_POWERFPS_LIST *r2 = x2;
if (r1->pid == 0)
return 1;
else if (r1->pid == -1)
return 1;
else if (r1->pid < r2->pid)
return -1;
else if (r1->pid == r2->pid && r1->fps < r2->fps)
return -1;
else if (r1->pid == r2->pid && r1->fps == r2->fps)
return 0;
return 1;
}
struct FSTB_POWERFPS_LIST powerfps_arrray[64];
void fstb_cal_powerhal_fps(void)
{
struct FSTB_FRAME_INFO *iter;
int i = 0, j = 0;
memset(powerfps_arrray, 0, 64 * sizeof(struct FSTB_POWERFPS_LIST));
hlist_for_each_entry(iter, &fstb_frame_infos, hlist) {
powerfps_arrray[i].pid = iter->proc_id;
powerfps_arrray[i].fps = fpsgo_power2fstb_get_target_fps(iter);
i++;
if (i >= 64) {
i = 63;
break;
}
}
powerfps_arrray[i].pid = -1;
sort(powerfps_arrray, i, sizeof(struct FSTB_POWERFPS_LIST), cmp_powerfps, NULL);
for (j = 0; j < i; j++) {
if (powerfps_arrray[j].pid != powerfps_arrray[j + 1].pid) {
mtk_fstb_dprintk_always("%s %d %d %d\n",
__func__, j, powerfps_arrray[j].pid, powerfps_arrray[j].fps);
fstb_sentcmd(powerfps_arrray[j].pid, powerfps_arrray[j].fps);
}
}
}
static void fstb_fps_stats(struct work_struct *work)
{
struct FSTB_FRAME_INFO *iter;
struct hlist_node *n;
int target_fps = max_fps_limit;
int target_fps_old, target_fps_new;
int idle = 1;
int fstb_active2xgf;
int max_target_fps = -1;
int eara_ret = -1;
if (work != &fps_stats_work)
kfree(work);
mutex_lock(&fstb_lock);
if (gbe_fstb2gbe_poll_fp)
gbe_fstb2gbe_poll_fp(&fstb_frame_infos);
hlist_for_each_entry_safe(iter, n, &fstb_frame_infos, hlist) {
/* if this process did queue buffer while last polling window */
if (fps_update(iter)) {
idle = 0;
target_fps = cal_target_fps(iter);
if (!iter->self_ctrl_fps_enable) {
target_fps_old = iter->target_fps;
target_fps_new = calculate_fps_limit(iter, target_fps);
if (target_fps_old != target_fps_new)
fstb_change_tfps(iter, target_fps_new, 1);
} else
iter->target_fps = calculate_fps_limit(iter, target_fps);
iter->vote_i = 0;
fpsgo_systrace_c_fstb_man(iter->pid, 0,
dfps_ceiling, "dfrc");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
iter->target_fps, "fstb_target_fps1");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
iter->target_fps_margin, "target_fps_margin");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
iter->target_fps_margin_gpu, "target_fps_margin_gpu");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
iter->target_fps_margin2, "target_fps_thrs");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
iter->target_fps_margin_dbnc_a,
"target_fps_margin_dbnc_a");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
iter->target_fps_margin_dbnc_b,
"target_fps_margin_dbnc_b");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
iter->target_fps_margin_gpu_dbnc_a,
"target_fps_margin_gpu_dbnc_a");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
iter->target_fps_margin_gpu_dbnc_b,
"target_fps_margin_gpu_dbnc_b");
mtk_fstb_dprintk(
"%s pid:%d target_fps:%d\n",
__func__, iter->pid,
iter->target_fps);
if (max_target_fps < iter->target_fps)
max_target_fps = iter->target_fps;
iter->render_idle_cnt = 0;
/* if queue fps == 0, we delete that frame_info */
} else {
iter->render_idle_cnt++;
if (iter->render_idle_cnt < FSTB_IDLE_DBNC) {
iter->target_fps = calculate_fps_limit(iter, target_fps);
mtk_fstb_dprintk(
"%s pid:%d target_fps:%d\n",
__func__, iter->pid,
iter->target_fps);
continue;
}
hlist_del(&iter->hlist);
vfree(iter);
}
}
fstb_cal_powerhal_fps();
/* check idle twice to avoid fstb_active ping-pong */
if (idle)
fstb_idle_cnt++;
else
fstb_idle_cnt = 0;
if (fstb_idle_cnt >= FSTB_IDLE_DBNC) {
fstb_active_dbncd = 0;
fstb_idle_cnt = 0;
} else if (fstb_idle_cnt >= 2) {
fstb_active = 0;
}
if (fstb_active)
fstb_active2xgf = 1;
else
fstb_active2xgf = 0;
if (fstb_enable && fstb_active_dbncd)
enable_fstb_timer();
else
disable_fstb_timer();
mutex_unlock(&fstb_lock);
if (eara_pre_change_fp)
eara_ret = eara_pre_change_fp();
if (eara_ret == -1) {
mutex_lock(&fstb_lock);
hlist_for_each_entry_safe(iter, n, &fstb_frame_infos, hlist) {
if (!iter->target_fps_notifying
|| iter->target_fps_notifying == -1)
continue;
if (!iter->self_ctrl_fps_enable)
iter->target_fps = iter->target_fps_notifying;
else
iter->target_fps_v2 = iter->target_fps_notifying;
iter->target_fps_notifying = 0;
fpsgo_systrace_c_fstb_man(iter->pid, iter->bufid,
0, "fstb_notifying");
fpsgo_systrace_c_fstb(iter->pid, iter->bufid,
iter->target_fps_v2, "fstb_target_fps1");
}
mutex_unlock(&fstb_lock);
}
fpsgo_check_thread_status();
fstb_enter_delete_render_info(0);
fpsgo_fstb2xgf_do_recycle(fstb_active2xgf);
}
struct video_info *fstb_search_and_add_video_info(int pid, int add_node)
{
struct rb_node **rb_ptr = &video_pid_tree.rb_node;
struct rb_node *parent = NULL;
struct video_info *ptr_video_info = NULL;
WARN_ON(!mutex_is_locked(&fstb_video_pid_tree_lock));
while (*rb_ptr) {
parent = *rb_ptr;
ptr_video_info = rb_entry(parent, struct video_info, entry);
if (pid < ptr_video_info->pid)
rb_ptr = &(*rb_ptr)->rb_left;
else if (pid > ptr_video_info->pid)
rb_ptr = &(*rb_ptr)->rb_right;
else
break;
}
/* search only */
if (!add_node) {
if (*rb_ptr)
return ptr_video_info;
else
return NULL;
}
/* add node */
if (*rb_ptr) {
ptr_video_info->count_instance++;
return ptr_video_info;
}
ptr_video_info = kzalloc(sizeof(*ptr_video_info), GFP_KERNEL);
ptr_video_info->pid = pid;
ptr_video_info->count_instance = 1U;
rb_link_node(&ptr_video_info->entry, parent, rb_ptr);
rb_insert_color(&ptr_video_info->entry, &video_pid_tree);
return ptr_video_info;
}
void fstb_delete_video_info(int pid)
{
struct video_info *video_info_instance;
WARN_ON(!mutex_is_locked(&fstb_video_pid_tree_lock));
video_info_instance = fstb_search_and_add_video_info(pid, 0);
if (!video_info_instance)
return;
if (video_info_instance->count_instance > 1) {
video_info_instance->count_instance--;
fpsgo_systrace_c_fstb_man(-100, 0,
video_info_instance->count_instance, "video_pid[%d]", pid);
return;
}
fpsgo_systrace_c_fstb_man(-100, 0, 0, "video_pid[%d]", pid);
rb_erase(&video_info_instance->entry, &video_pid_tree);
kfree(video_info_instance);
}
static int set_soft_fps_level(struct fps_level level)
{
mutex_lock(&fstb_lock);
if (level.end > level.start ||
level.start <= 0 || level.end <= 0)
goto set_fps_level_err;
fps_global_level.start = level.start;
fps_global_level.end = level.end;
max_fps_limit = min(dfps_ceiling, fps_global_level.start);
min_fps_limit = min(dfps_ceiling, fps_global_level.end);
mutex_unlock(&fstb_lock);
return 0;
set_fps_level_err:
mutex_unlock(&fstb_lock);
return -EINVAL;
}
static void reset_fps_level(void)
{
struct fps_level global_level;
global_level.start = CFG_MAX_FPS_LIMIT;
global_level.end = CFG_MIN_FPS_LIMIT;
set_soft_fps_level(global_level);
}
#define FSTB_SYSFS_READ(name, show, variable); \
static ssize_t name##_show(struct kobject *kobj, \
struct kobj_attribute *attr, \
char *buf) \
{ \
if ((show)) \
return scnprintf(buf, PAGE_SIZE, "%d\n", (variable)); \
else \
return 0; \
}
#define FSTB_SYSFS_WRITE_VALUE(name, variable, min, max); \
static ssize_t name##_store(struct kobject *kobj, \
struct kobj_attribute *attr, \
const char *buf, size_t count) \
{ \
char *acBuffer = NULL; \
int arg; \
\
acBuffer = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL); \
if (!acBuffer) \
goto out; \
\
if ((count > 0) && (count < FPSGO_SYSFS_MAX_BUFF_SIZE)) { \
if (scnprintf(acBuffer, FPSGO_SYSFS_MAX_BUFF_SIZE, "%s", buf)) { \
if (kstrtoint(acBuffer, 0, &arg) == 0) { \
if (arg >= (min) && arg <= (max)) { \
mutex_lock(&fstb_lock); \
(variable) = arg; \
mutex_unlock(&fstb_lock); \
} \
} \
} \
} \
\
out: \
kfree(acBuffer); \
return count; \
}
#define FSTB_SYSFS_WRITE_POLICY_CMD(name, cmd, min, max); \
static ssize_t name##_store(struct kobject *kobj, \
struct kobj_attribute *attr, \
const char *buf, size_t count) \
{ \
char *acBuffer = NULL; \
int tgid; \
int arg; \
unsigned long long ts = fpsgo_get_time(); \
\
acBuffer = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL); \
if (!acBuffer) \
goto out; \
\
if ((count > 0) && (count < FPSGO_SYSFS_MAX_BUFF_SIZE)) { \
if (scnprintf(acBuffer, FPSGO_SYSFS_MAX_BUFF_SIZE, "%s", buf)) { \
if (sscanf(acBuffer, "%d %d", &tgid, &arg) == 2) { \
mutex_lock(&fstb_policy_cmd_lock); \
if (arg >= (min) && arg <= (max)) \
fstb_set_policy_cmd(cmd, arg, tgid, ts, 1); \
else \
fstb_set_policy_cmd(cmd, 0, tgid, ts, 0); \
mutex_unlock(&fstb_policy_cmd_lock); \
} \
} \
} \
\
out: \
kfree(acBuffer); \
return count; \
}
/*
* PowerHAL API for videoplayback hint.
*
* Two write-only nodes to set/clear video pid to the list:
* 1. set_video_pid
* 2. clear_video_pid
* e.g.
* echo $pid > set_video_pid
* One read-only node to show the video pid list:
* 1. fstb_video_pid_list
* e.g.
* cat fstb_video_pid_list
*/
static ssize_t fstb_video_pid_list_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct rb_node *n;
struct video_info *video_instance;
char *pid_str = NULL;
int pos = 0;
int length = 0;
pid_str = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL);
if (!pid_str)
goto out;
length = scnprintf(pid_str + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"tgid\tcount_instance\n");
pos += length;
rcu_read_lock();
for (n = rb_first(&video_pid_tree); n != NULL; n = rb_next(n)) {
video_instance = rb_entry(n, struct video_info, entry);
length = scnprintf(pid_str + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"%d\t%u\n", video_instance->pid, video_instance->count_instance);
pos += length;
}
rcu_read_unlock();
length = scnprintf(buf, PAGE_SIZE, "%s", pid_str);
out:
kfree(pid_str);
return length;
}
static KOBJ_ATTR_RO(fstb_video_pid_list);
static ssize_t set_video_pid_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *acBuffer = NULL;
int arg = -1;
acBuffer = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL);
if (!acBuffer)
goto out;
if ((count > 0) && (count < FPSGO_SYSFS_MAX_BUFF_SIZE)) {
if (scnprintf(acBuffer, FPSGO_SYSFS_MAX_BUFF_SIZE, "%s", buf)) {
if (kstrtoint(acBuffer, 0, &arg) != 0)
goto out;
mtk_fstb_dprintk_always("%s %d\n", __func__, arg);
if (arg != -1)
fstb_set_video_pid(arg);
}
}
out:
kfree(acBuffer);
return count;
}
static ssize_t set_video_pid_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct rb_node *n;
struct video_info *video_instance;
char *pid_str = NULL;
int pos = 0;
int length = 0;
pid_str = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL);
if (!pid_str)
goto out;
length = scnprintf(pid_str + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"tgid\tcount_instance\n");
pos += length;
rcu_read_lock();
for (n = rb_first(&video_pid_tree); n != NULL; n = rb_next(n)) {
video_instance = rb_entry(n, struct video_info, entry);
length = scnprintf(pid_str + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"%d\t%u\n", video_instance->pid, video_instance->count_instance);
pos += length;
}
rcu_read_unlock();
length = scnprintf(buf, PAGE_SIZE, "%s", pid_str);
out:
kfree(pid_str);
return length;
}
static KOBJ_ATTR_RW(set_video_pid);
static ssize_t clear_video_pid_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *acBuffer = NULL;
int arg = -1;
acBuffer = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL);
if (!acBuffer)
goto out;
if ((count > 0) && (count < FPSGO_SYSFS_MAX_BUFF_SIZE)) {
if (scnprintf(acBuffer, FPSGO_SYSFS_MAX_BUFF_SIZE, "%s", buf)) {
if (kstrtoint(acBuffer, 0, &arg) != 0)
goto out;
mtk_fstb_dprintk_always("%s %d\n", __func__, arg);
if (arg != -1)
fstb_clear_video_pid(arg);
}
}
out:
kfree(acBuffer);
return count;
}
static ssize_t clear_video_pid_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct rb_node *n;
struct video_info *video_instance;
char *pid_str = NULL;
int pos = 0;
int length = 0;
pid_str = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL);
if (!pid_str)
goto out;
length = scnprintf(pid_str + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"tgid\tcount_instance\n");
pos += length;
rcu_read_lock();
for (n = rb_first(&video_pid_tree); n != NULL; n = rb_next(n)) {
video_instance = rb_entry(n, struct video_info, entry);
length = scnprintf(pid_str + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"%d\t%u\n", video_instance->pid, video_instance->count_instance);
pos += length;
}
rcu_read_unlock();
length = scnprintf(buf, PAGE_SIZE, "%s", pid_str);
out:
kfree(pid_str);
return length;
}
static KOBJ_ATTR_RW(clear_video_pid);
FSTB_SYSFS_READ(set_render_max_fps, 0, 0);
static ssize_t set_render_max_fps_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *acBuffer = NULL;
int arg;
int ret = 0;
acBuffer = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL);
if (!acBuffer)
goto out;
if ((count > 0) && (count < FPSGO_SYSFS_MAX_BUFF_SIZE)) {
if (scnprintf(acBuffer, FPSGO_SYSFS_MAX_BUFF_SIZE, "%s", buf)) {
if (kstrtoint(acBuffer, 0, &arg) != 0)
goto out;
mtk_fstb_dprintk_always("%s %d\n", __func__, arg);
fpsgo_systrace_c_fstb_man(arg > 0 ? arg : -arg,
0, arg > 0, "force_max_fps");
if (arg > 0)
ret = switch_thread_max_fps(arg, 1);
else
ret = switch_thread_max_fps(-arg, 0);
}
}
out:
kfree(acBuffer);
return count;
}
static KOBJ_ATTR_RW(set_render_max_fps);
static ssize_t fstb_soft_level_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "1 %d-%d\n",
fps_global_level.start, fps_global_level.end);
}
static ssize_t fstb_soft_level_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *acBuffer = NULL;
char *sepstr, *substr;
int start_fps, end_fps;
struct fps_level new_fps_global_level;
acBuffer = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL);
if (!acBuffer)
goto err;
if ((count > 0) && (count < FPSGO_SYSFS_MAX_BUFF_SIZE)) {
if (scnprintf(acBuffer, FPSGO_SYSFS_MAX_BUFF_SIZE, "%s", buf)) {
acBuffer[count] = '\0';
sepstr = acBuffer;
substr = strsep(&sepstr, " ");
substr = sepstr;
if (!substr)
goto err;
if (strchr(substr, '-')) {
if (sscanf(substr, "%d-%d",
&start_fps, &end_fps) != 2)
goto err;
new_fps_global_level.start = start_fps;
new_fps_global_level.end = end_fps;
} else {
if (kstrtoint(substr,
10, &start_fps) != 0)
goto err;
new_fps_global_level.start = start_fps;
new_fps_global_level.end = start_fps;
}
set_soft_fps_level(new_fps_global_level);
}
}
err:
kfree(acBuffer);
return count;
}
static KOBJ_ATTR_RW(fstb_soft_level);
static ssize_t fstb_fps_list_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
int i;
struct FSTB_RENDER_TARGET_FPS *rtfiter = NULL;
char *temp = NULL;
int pos = 0;
int length = 0;
temp = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL);
if (!temp)
goto out;
hlist_for_each_entry(rtfiter, &fstb_render_target_fps, hlist) {
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"%s %d %d ",
rtfiter->process_name,
rtfiter->pid,
rtfiter->nr_level);
pos += length;
for (i = 0; i < rtfiter->nr_level; i++) {
length = scnprintf(temp + pos,
FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"%d-%d ",
rtfiter->level[i].start,
rtfiter->level[i].end);
pos += length;
}
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"\n");
pos += length;
}
length = scnprintf(buf, PAGE_SIZE, "%s", temp);
out:
kfree(temp);
return length;
}
static ssize_t fstb_fps_list_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *acBuffer = NULL;
char *sepstr, *substr;
char proc_name[16];
int i;
int nr_level, start_fps, end_fps;
int mode = 1;
int pid = 0;
int ret = 0;
struct fps_level level[MAX_NR_RENDER_FPS_LEVELS];
acBuffer = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL);
if (!acBuffer)
goto err;
if ((count > 0) && (count < FPSGO_SYSFS_MAX_BUFF_SIZE)) {
if (scnprintf(acBuffer, FPSGO_SYSFS_MAX_BUFF_SIZE, "%s", buf)) {
acBuffer[count] = '\0';
sepstr = acBuffer;
substr = strsep(&sepstr, " ");
if (!substr || !strncpy(proc_name, substr, 16)) {
ret = -EINVAL;
goto err;
}
proc_name[15] = '\0';
if (kstrtoint(proc_name, 10, &pid) != 0)
mode = 0; /* process mode*/
substr = strsep(&sepstr, " ");
if (!substr || kstrtoint(substr, 10, &nr_level) != 0 ||
nr_level > MAX_NR_RENDER_FPS_LEVELS ||
nr_level < 0) {
ret = -EINVAL;
goto err;
}
for (i = 0; i < nr_level; i++) {
substr = strsep(&sepstr, " ");
if (!substr) {
ret = -EINVAL;
goto err;
}
if (sscanf(substr, "%d-%d",
&start_fps, &end_fps) != 2) {
ret = -EINVAL;
goto err;
}
level[i].start = start_fps;
level[i].end = end_fps;
}
if (mode == 0) {
if (switch_process_fps_range(proc_name,
nr_level, level))
ret = -EINVAL;
} else {
if (switch_thread_fps_range(pid,
nr_level, level))
ret = -EINVAL;
}
}
}
err:
kfree(acBuffer);
return count;
}
static KOBJ_ATTR_RW(fstb_fps_list);
FSTB_SYSFS_READ(jump_check_q_pct, 1, JUMP_CHECK_Q_PCT);
FSTB_SYSFS_WRITE_VALUE(jump_check_q_pct, JUMP_CHECK_Q_PCT, 1, 100);
static KOBJ_ATTR_RW(jump_check_q_pct);
FSTB_SYSFS_READ(jump_check_num, 1, JUMP_CHECK_NUM);
FSTB_SYSFS_WRITE_VALUE(jump_check_num, JUMP_CHECK_NUM, 0, JUMP_VOTE_MAX_I);
static KOBJ_ATTR_RW(jump_check_num);
FSTB_SYSFS_READ(margin_mode, 1, margin_mode);
FSTB_SYSFS_WRITE_VALUE(margin_mode, margin_mode, 1, INT_MAX);
static KOBJ_ATTR_RW(margin_mode);
FSTB_SYSFS_READ(margin_mode_dbnc_a, 1, margin_mode_dbnc_a);
FSTB_SYSFS_WRITE_VALUE(margin_mode_dbnc_a, margin_mode_dbnc_a, 1, INT_MAX);
static KOBJ_ATTR_RW(margin_mode_dbnc_a);
FSTB_SYSFS_READ(margin_mode_dbnc_b, 1, margin_mode_dbnc_b);
FSTB_SYSFS_WRITE_VALUE(margin_mode_dbnc_b, margin_mode_dbnc_b, 1, INT_MAX);
static KOBJ_ATTR_RW(margin_mode_dbnc_b);
FSTB_SYSFS_READ(margin_mode_gpu, 1, margin_mode_gpu);
FSTB_SYSFS_WRITE_VALUE(margin_mode_gpu, margin_mode_gpu, 1, INT_MAX);
static KOBJ_ATTR_RW(margin_mode_gpu);
FSTB_SYSFS_READ(margin_mode_gpu_dbnc_a, 1, margin_mode_gpu_dbnc_a);
FSTB_SYSFS_WRITE_VALUE(margin_mode_gpu_dbnc_a, margin_mode_gpu_dbnc_a, 1, INT_MAX);
static KOBJ_ATTR_RW(margin_mode_gpu_dbnc_a);
FSTB_SYSFS_READ(margin_mode_gpu_dbnc_b, 1, margin_mode_gpu_dbnc_b);
FSTB_SYSFS_WRITE_VALUE(margin_mode_gpu_dbnc_b, margin_mode_gpu_dbnc_b, 1, INT_MAX);
static KOBJ_ATTR_RW(margin_mode_gpu_dbnc_b);
FSTB_SYSFS_READ(fstb_reset_tolerence, 1, RESET_TOLERENCE);
FSTB_SYSFS_WRITE_VALUE(fstb_reset_tolerence, RESET_TOLERENCE, 0, INT_MAX);
static KOBJ_ATTR_RW(fstb_reset_tolerence);
FSTB_SYSFS_READ(fstb_tune_quantile, 1, QUANTILE);
FSTB_SYSFS_WRITE_VALUE(fstb_tune_quantile, QUANTILE, 0, 100);
static KOBJ_ATTR_RW(fstb_tune_quantile);
FSTB_SYSFS_READ(fstb_tune_error_threshold, 1, fps_error_threshold);
FSTB_SYSFS_WRITE_VALUE(fstb_tune_error_threshold, fps_error_threshold, 0, 100);
static KOBJ_ATTR_RW(fstb_tune_error_threshold);
FSTB_SYSFS_READ(adopt_low_fps, 1, adopt_low_fps);
FSTB_SYSFS_WRITE_VALUE(adopt_low_fps, adopt_low_fps, 0, 1);
static KOBJ_ATTR_RW(adopt_low_fps);
FSTB_SYSFS_READ(fstb_fps_bypass_min, 1, fps_bypass_min);
FSTB_SYSFS_WRITE_VALUE(fstb_fps_bypass_min, fps_bypass_min, 0, 200);
static KOBJ_ATTR_RW(fstb_fps_bypass_min);
FSTB_SYSFS_READ(fstb_fps_bypass_max, 1, fps_bypass_max);
FSTB_SYSFS_WRITE_VALUE(fstb_fps_bypass_max, fps_bypass_max, 0, 200);
static KOBJ_ATTR_RW(fstb_fps_bypass_max);
FSTB_SYSFS_READ(fstb_self_ctrl_fps_enable, 1, fstb_self_ctrl_fps_enable);
FSTB_SYSFS_WRITE_VALUE(fstb_self_ctrl_fps_enable, fstb_self_ctrl_fps_enable, 0, 1);
static KOBJ_ATTR_RW(fstb_self_ctrl_fps_enable);
static ssize_t fstb_debug_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
char *temp = NULL;
int pos = 0;
int length = 0;
temp = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL);
if (!temp)
goto out;
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"fstb_enable %d\n", fstb_enable);
pos += length;
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"fstb_log %d\n", fstb_fps_klog_on);
pos += length;
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"fstb_active %d\n", fstb_active);
pos += length;
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"fstb_active_dbncd %d\n", fstb_active_dbncd);
pos += length;
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"fstb_idle_cnt %d\n", fstb_idle_cnt);
pos += length;
length = scnprintf(buf, PAGE_SIZE, "%s", temp);
out:
kfree(temp);
return length;
}
static ssize_t fstb_debug_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *acBuffer = NULL;
int k_enable, klog_on;
acBuffer = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL);
if (!acBuffer)
goto out;
if ((count > 0) && (count < FPSGO_SYSFS_MAX_BUFF_SIZE)) {
if (scnprintf(acBuffer, FPSGO_SYSFS_MAX_BUFF_SIZE, "%s", buf)) {
if (sscanf(acBuffer, "%d %d",
&k_enable, &klog_on) >= 1) {
if (k_enable == 0 || k_enable == 1)
fpsgo_ctrl2fstb_switch_fstb(k_enable);
if (klog_on == 0 || klog_on == 1)
fstb_fps_klog_on = klog_on;
}
}
}
out:
kfree(acBuffer);
return count;
}
static KOBJ_ATTR_RW(fstb_debug);
static ssize_t fpsgo_status_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct FSTB_FRAME_INFO *iter;
char *temp = NULL;
int pos = 0;
int length = 0;
temp = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL);
if (!temp)
goto out;
mutex_lock(&fstb_lock);
if (!fstb_enable) {
mutex_unlock(&fstb_lock);
goto out;
}
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"tid\tbufID\t\tname\t\tcurrentFPS\ttargetFPS\tFPS_margin\tFPS_margin_GPU\tFPS_margin_thrs\tsbe_state\tHWUI\tt_gpu\t\tpolicy\n");
pos += length;
hlist_for_each_entry(iter, &fstb_frame_infos, hlist) {
if (iter) {
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"%d\t0x%llx\t%s\t%d\t\t%d\t\t%d\t\t%d\t\t%d\t\t%d\t\t%d\t%lld\t\t(%d,%d,%d)\n",
iter->pid,
iter->bufid,
iter->proc_name,
iter->queue_fps > max_fps_limit ?
max_fps_limit : iter->queue_fps,
iter->self_ctrl_fps_enable ?
iter->target_fps_v2 : iter->target_fps,
iter->self_ctrl_fps_enable ?
iter->target_fps_margin_v2 : iter->target_fps_margin,
iter->self_ctrl_fps_enable ?
-1 : iter->target_fps_margin_gpu,
iter->self_ctrl_fps_enable ?
iter->target_fps_margin_v2 : iter->target_fps_margin2,
iter->sbe_state,
iter->hwui_flag,
iter->gpu_time,
iter->self_ctrl_fps_enable,
iter->tfb_enable,
iter->notify_target_fps);
pos += length;
}
}
mutex_unlock(&fstb_lock);
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"dfps_ceiling:%d\n", dfps_ceiling);
pos += length;
length = scnprintf(buf, PAGE_SIZE, "%s", temp);
out:
kfree(temp);
return length;
}
static KOBJ_ATTR_ROO(fpsgo_status);
static ssize_t fstb_policy_cmd_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
char *temp = NULL;
int i = 1;
int pos = 0;
int length = 0;
struct FSTB_POLICY_CMD *iter;
struct rb_root *rbr;
struct rb_node *rbn;
temp = kcalloc(FPSGO_SYSFS_MAX_BUFF_SIZE, sizeof(char), GFP_KERNEL);
if (!temp)
goto out;
mutex_lock(&fstb_policy_cmd_lock);
rbr = &fstb_policy_cmd_tree;
for (rbn = rb_first(rbr); rbn; rbn = rb_next(rbn)) {
iter = rb_entry(rbn, struct FSTB_POLICY_CMD, rb_node);
length = scnprintf(temp + pos,
FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
"%dth\ttgid:%d\tfstb_self_ctrl_fps_enable:%d\ttfb_enable:%d\tnotify_target_fps:%d\tts:%llu\n",
i,
iter->tgid,
iter->self_ctrl_fps_enable,
iter->tfb_enable,
iter->notify_target_fps,
iter->ts);
pos += length;
i++;
}
mutex_unlock(&fstb_policy_cmd_lock);
length = scnprintf(buf, PAGE_SIZE, "%s", temp);
out:
kfree(temp);
return length;
}
static KOBJ_ATTR_RO(fstb_policy_cmd);
FSTB_SYSFS_READ(fstb_self_ctrl_fps_enable_by_pid, 0, 0);
FSTB_SYSFS_WRITE_POLICY_CMD(fstb_self_ctrl_fps_enable_by_pid, 0, 1, 1);
static KOBJ_ATTR_RW(fstb_self_ctrl_fps_enable_by_pid);
FSTB_SYSFS_READ(notify_fstb_target_fps_by_pid, 0, 0);
FSTB_SYSFS_WRITE_POLICY_CMD(notify_fstb_target_fps_by_pid, 2, 1, CFG_MAX_FPS_LIMIT);
static KOBJ_ATTR_RW(notify_fstb_target_fps_by_pid);
int mtk_fstb_init(void)
{
mtk_fstb_dprintk_always("init\n");
#if defined(CONFIG_MTK_GPU_COMMON_DVFS_SUPPORT)
ged_kpi_output_gfx_info2_fp = gpu_time_update;
#endif
if (!fpsgo_sysfs_create_dir(NULL, "fstb", &fstb_kobj)) {
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_fpsgo_status);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_fstb_debug);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_fstb_tune_error_threshold);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_fstb_tune_quantile);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_margin_mode_dbnc_b);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_margin_mode_dbnc_a);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_margin_mode);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_margin_mode_gpu_dbnc_b);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_margin_mode_gpu_dbnc_a);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_margin_mode_gpu);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_fstb_reset_tolerence);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_fstb_fps_list);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_fstb_soft_level);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_jump_check_num);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_jump_check_q_pct);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_adopt_low_fps);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_set_render_max_fps);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_fstb_self_ctrl_fps_enable);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_fstb_fps_bypass_max);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_fstb_fps_bypass_min);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_set_video_pid);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_clear_video_pid);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_fstb_video_pid_list);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_fstb_policy_cmd);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_fstb_self_ctrl_fps_enable_by_pid);
fpsgo_sysfs_create_file(fstb_kobj,
&kobj_attr_notify_fstb_target_fps_by_pid);
}
reset_fps_level();
wq = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM | WQ_HIGHPRI, "mt_fstb");
if (!wq)
goto err;
hrtimer_init(&hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
hrt.function = &mt_fstb;
fstb_policy_cmd_tree = RB_ROOT;
mtk_fstb_dprintk_always("init done\n");
return 0;
err:
return -1;
}
int __exit mtk_fstb_exit(void)
{
mtk_fstb_dprintk("exit\n");
disable_fstb_timer();
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_fpsgo_status);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_fstb_debug);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_fstb_tune_error_threshold);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_fstb_tune_quantile);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_margin_mode_dbnc_b);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_margin_mode_dbnc_a);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_margin_mode);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_margin_mode_gpu_dbnc_b);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_margin_mode_gpu_dbnc_a);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_margin_mode_gpu);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_fstb_reset_tolerence);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_fstb_fps_list);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_fstb_soft_level);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_jump_check_num);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_jump_check_q_pct);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_adopt_low_fps);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_set_render_max_fps);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_fstb_self_ctrl_fps_enable);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_fstb_fps_bypass_max);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_fstb_fps_bypass_min);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_set_video_pid);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_clear_video_pid);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_fstb_video_pid_list);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_fstb_policy_cmd);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_fstb_self_ctrl_fps_enable_by_pid);
fpsgo_sysfs_remove_file(fstb_kobj,
&kobj_attr_notify_fstb_target_fps_by_pid);
fpsgo_sysfs_remove_dir(&fstb_kobj);
return 0;
}