2371 lines
56 KiB
C
2371 lines
56 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 <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"
|
|
|
|
|
|
#define API_READY 0
|
|
#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)
|
|
|
|
#define fpsgo_systrace_c_fstb_man(pid, val, fmt...) \
|
|
fpsgo_systrace_c(FPSGO_DEBUG_MANDATORY, pid, val, fmt)
|
|
|
|
|
|
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 long long FRAME_TIME_WINDOW_SIZE_US = 1000000;
|
|
static long long ADJUST_INTERVAL_US = 1000000;
|
|
static int margin_mode;
|
|
static int margin_mode_dbnc_a = 9;
|
|
static int margin_mode_dbnc_b = 1;
|
|
static int JUMP_CHECK_NUM = DEFAULT_JUMP_CHECK_NUM;
|
|
static int condition_get_fps;
|
|
|
|
DECLARE_WAIT_QUEUE_HEAD(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_levels[MAX_NR_FPS_LEVELS];
|
|
static int nr_fps_levels = MAX_NR_FPS_LEVELS;
|
|
|
|
static int fstb_fps_klog_on;
|
|
static int fstb_enable, fstb_active, fstb_idle_cnt;
|
|
static long long last_update_ts;
|
|
|
|
static void reset_fps_level(void);
|
|
static int set_soft_fps_level(int nr_level,
|
|
struct fps_level *level);
|
|
|
|
static DEFINE_MUTEX(fstb_lock);
|
|
static DEFINE_MUTEX(fstb_fps_active_time);
|
|
static DEFINE_MUTEX(fstb_cam_active_time);
|
|
|
|
void (*gbe_fstb2gbe_poll_fp)(struct hlist_head *list);
|
|
|
|
static void enable_fstb_timer(void)
|
|
{
|
|
ktime_t ktime;
|
|
|
|
ktime = ktime_set(0,
|
|
ADJUST_INTERVAL_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;
|
|
}
|
|
|
|
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_gblock(int tid, int start)
|
|
{
|
|
struct FSTB_FRAME_INFO *iter;
|
|
ktime_t cur_time;
|
|
unsigned long long cur_time_ns;
|
|
|
|
cur_time = ktime_get();
|
|
cur_time_ns = ktime_to_ns(cur_time);
|
|
|
|
mutex_lock(&fstb_lock);
|
|
|
|
if (!fstb_enable) {
|
|
mutex_unlock(&fstb_lock);
|
|
return 0;
|
|
}
|
|
|
|
hlist_for_each_entry(iter, &fstb_frame_infos, hlist) {
|
|
if (iter->pid == tid)
|
|
break;
|
|
}
|
|
|
|
if (iter == NULL) {
|
|
mutex_unlock(&fstb_lock);
|
|
return 0;
|
|
}
|
|
|
|
fpsgo_systrace_c_fstb(tid, 0, start, "gblock");
|
|
|
|
/* end */
|
|
if (!start && iter->gblock_b)
|
|
iter->gblock_time += cur_time_ns - iter->gblock_b;
|
|
|
|
/* start */
|
|
if (start)
|
|
iter->gblock_b = cur_time_ns;
|
|
|
|
mutex_unlock(&fstb_lock);
|
|
return 0;
|
|
}
|
|
|
|
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) {
|
|
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");
|
|
|
|
mtk_fstb_dprintk_always("%s %d\n",
|
|
__func__, fstb_active);
|
|
enable_fstb_timer();
|
|
}
|
|
|
|
int switch_sample_window(long long time_usec)
|
|
{
|
|
if (time_usec < 0 ||
|
|
time_usec > 1000000 * FRAME_TIME_BUFFER_SIZE / 120)
|
|
return -EINVAL;
|
|
|
|
FRAME_TIME_WINDOW_SIZE_US = ADJUST_INTERVAL_US = time_usec;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int switch_margin_mode(int mode)
|
|
{
|
|
if (mode > 2 || mode < 0)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&fstb_lock);
|
|
if (mode != margin_mode)
|
|
margin_mode = mode;
|
|
mutex_unlock(&fstb_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int switch_margin_mode_dbnc_a(int val)
|
|
{
|
|
if (val < 1)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&fstb_lock);
|
|
if (val != margin_mode_dbnc_a)
|
|
margin_mode_dbnc_a = val;
|
|
mutex_unlock(&fstb_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int switch_margin_mode_dbnc_b(int val)
|
|
{
|
|
if (val < 1)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&fstb_lock);
|
|
if (val != margin_mode_dbnc_b)
|
|
margin_mode_dbnc_b = val;
|
|
mutex_unlock(&fstb_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int switch_jump_check_num(int val)
|
|
{
|
|
if (val < 1 || val > JUMP_VOTE_MAX_I)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&fstb_lock);
|
|
if (val != JUMP_CHECK_NUM)
|
|
JUMP_CHECK_NUM = val;
|
|
mutex_unlock(&fstb_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int switch_fps_range(int nr_level, struct fps_level *level)
|
|
{
|
|
if (nr_level != 1)
|
|
return 1;
|
|
|
|
if (!set_soft_fps_level(nr_level, level))
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
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;
|
|
|
|
switch (iter->sbe_state) {
|
|
case -1:
|
|
ret = -1;
|
|
break;
|
|
case 0:
|
|
case 1:
|
|
iter->sbe_state = set_max;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
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;
|
|
}
|
|
|
|
int switch_thread_no_ctrl(int pid, int set_no_ctrl)
|
|
{
|
|
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;
|
|
switch (iter->sbe_state) {
|
|
case -1:
|
|
case 0:
|
|
iter->sbe_state = set_no_ctrl ? -1 : 0;
|
|
break;
|
|
case 1:
|
|
iter->sbe_state = set_no_ctrl ? -1 : 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
fpsgo_systrace_c_fstb_man(iter->pid, iter->bufid,
|
|
set_no_ctrl, "sbe_set_no_ctrl");
|
|
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);
|
|
}
|
|
|
|
int switch_fps_error_threhosld(int threshold)
|
|
{
|
|
if (threshold < 0 || threshold > 100)
|
|
return -EINVAL;
|
|
|
|
fps_error_threshold = threshold;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int switch_percentile_frametime(int ratio)
|
|
{
|
|
if (ratio < 0 || ratio > 100)
|
|
return -EINVAL;
|
|
|
|
QUANTILE = ratio;
|
|
|
|
return 0;
|
|
}
|
|
|
|
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 &&
|
|
nr_fps_levels >= 1) {
|
|
dfps_ceiling = fps;
|
|
max_fps_limit = min(dfps_ceiling,
|
|
fps_levels[0].start);
|
|
min_fps_limit = min(dfps_ceiling,
|
|
fps_levels[nr_fps_levels - 1].end);
|
|
|
|
mutex_unlock(&fstb_lock);
|
|
} else
|
|
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;
|
|
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_man(iter->pid, iter->bufid, ret,
|
|
"quantile_weighted_gpu_time");
|
|
return ret;
|
|
|
|
}
|
|
|
|
void (*eara_thrm_frame_start_fp)(int pid, unsigned long long bufID,
|
|
int cpu_time, int vpu_time, int mdla_time,
|
|
int cpu_cap, int vpu_cap, int mdla_cap,
|
|
int queuefps, unsigned long long q2q_time,
|
|
int AI_cross_vpu, int AI_cross_mdla, int AI_bg_vpu,
|
|
int AI_bg_mdla, ktime_t cur_time);
|
|
|
|
int fpsgo_fbt2fstb_update_cpu_frame_info(
|
|
int pid,
|
|
unsigned long long bufID,
|
|
int tgid,
|
|
int frame_type,
|
|
unsigned long long Q2Q_time,
|
|
unsigned long long Runnging_time,
|
|
unsigned int Curr_cap,
|
|
unsigned int Max_cap,
|
|
unsigned long long mid)
|
|
{
|
|
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, wvt = 0, wmt = 0;
|
|
long long vpu_time_ns = 0, mdla_time_ns = 0;
|
|
int vpu_boost = 0, mdla_boost = 0;
|
|
|
|
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;
|
|
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) {
|
|
wct = cpu_time_ns * max_current_cap;
|
|
do_div(wct, max_cpu_cap);
|
|
} else
|
|
wct = cpu_time_ns;
|
|
|
|
fpsgo_systrace_c_fstb_man(pid, iter->bufid, (int)wct,
|
|
"weighted_cpu_time");
|
|
|
|
if (vpu_time_ns && vpu_boost > 0 && vpu_boost <= VPU_MAX_CAP) {
|
|
wvt = vpu_time_ns * vpu_boost;
|
|
do_div(wvt, VPU_MAX_CAP);
|
|
fpsgo_systrace_c_fstb_man(pid, iter->bufid, (int)wvt,
|
|
"weighted_vpu_time");
|
|
}
|
|
|
|
if (mdla_time_ns && mdla_boost > 0 && mdla_boost <= MDLA_MAX_CAP) {
|
|
wmt = mdla_time_ns * mdla_boost;
|
|
do_div(wmt, MDLA_MAX_CAP);
|
|
fpsgo_systrace_c_fstb_man(pid, iter->bufid, (int)wmt,
|
|
"weighted_mdla_time");
|
|
}
|
|
|
|
iter->weighted_cpu_time[iter->weighted_cpu_time_end] =
|
|
wct + wvt + wmt;
|
|
|
|
iter->weighted_cpu_time_ts[iter->weighted_cpu_time_end] =
|
|
cur_time_us;
|
|
iter->weighted_cpu_time_end++;
|
|
|
|
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);
|
|
|
|
iter->m_c_time = (iter->m_c_time + cpu_time_ns) / 2;
|
|
iter->m_c_cap = (iter->m_c_cap + max_current_cap) / 2;
|
|
iter->m_v_time = (iter->m_v_time + vpu_time_ns) / 2;
|
|
iter->m_v_cap = (iter->m_v_cap + vpu_boost) / 2;
|
|
iter->m_m_time = (iter->m_m_time + mdla_time_ns) / 2;
|
|
iter->m_m_cap = (iter->m_m_cap + mdla_boost) / 2;
|
|
|
|
/* parse cpu time of each frame to ged */
|
|
iter->cpu_time = cpu_time_ns;
|
|
|
|
ged_kpi_set_target_FPS_margin(iter->bufid,
|
|
iter->target_fps, iter->target_fps_margin, iter->cpu_time);
|
|
|
|
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");
|
|
|
|
if (mid) {
|
|
fpsgo_systrace_c_fstb_man(pid, iter->bufid, (int)vpu_time_ns,
|
|
"t_vpu");
|
|
fpsgo_systrace_c_fstb(pid, iter->bufid, (int)vpu_boost,
|
|
"cur_vpu_cap");
|
|
fpsgo_systrace_c_fstb_man(pid, iter->bufid, (int)mdla_time_ns,
|
|
"t_mdla");
|
|
fpsgo_systrace_c_fstb(pid, iter->bufid, (int)mdla_boost,
|
|
"cur_mdla_cap");
|
|
fpsgo_systrace_c_fstb(pid, iter->bufid, (int)iter->m_c_time,
|
|
"avg_cpu_time");
|
|
fpsgo_systrace_c_fstb(pid, iter->bufid, (int)iter->m_c_cap,
|
|
"avg_cpu_cap");
|
|
fpsgo_systrace_c_fstb(pid, iter->bufid, (int)iter->m_v_time,
|
|
"avg_vpu_time");
|
|
fpsgo_systrace_c_fstb(pid, iter->bufid, (int)iter->m_v_cap,
|
|
"avg_vpu_cap");
|
|
fpsgo_systrace_c_fstb(pid, iter->bufid, (int)iter->m_m_time,
|
|
"avg_mdla_time");
|
|
fpsgo_systrace_c_fstb(pid, iter->bufid, (int)iter->m_m_cap,
|
|
"avg_mdla_cap");
|
|
}
|
|
|
|
if (eara_thrm_frame_start_fp) {
|
|
eara_thrm_frame_start_fp(pid, bufID, (int)Runnging_time,
|
|
(int)vpu_time_ns, (int)mdla_time_ns, Curr_cap,
|
|
vpu_boost, mdla_boost, iter->queue_fps,
|
|
Q2Q_time, 0, 0,
|
|
0, 0, cur_time);
|
|
}
|
|
|
|
mutex_unlock(&fstb_lock);
|
|
return 0;
|
|
}
|
|
|
|
void (*eara_thrm_enqueue_end_fp)(int pid, unsigned long long bufID,
|
|
int gpu_time, int gpu_freq, unsigned long long enq);
|
|
int fpsgo_comp2fstb_enq_end(int pid, unsigned long long bufID,
|
|
unsigned long long enq)
|
|
{
|
|
struct FSTB_FRAME_INFO *iter;
|
|
|
|
mutex_lock(&fstb_lock);
|
|
|
|
if (!fstb_enable) {
|
|
mutex_unlock(&fstb_lock);
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (eara_thrm_enqueue_end_fp)
|
|
eara_thrm_enqueue_end_fp(pid, bufID,
|
|
iter->gpu_time, iter->gpu_freq, enq);
|
|
|
|
mutex_unlock(&fstb_lock);
|
|
return 0;
|
|
}
|
|
|
|
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_man(iter->pid, iter->bufid, ret,
|
|
"quantile_weighted_cpu_time");
|
|
return ret;
|
|
|
|
}
|
|
|
|
/*
|
|
* check if camera is active
|
|
* if yes, apply g block c boost
|
|
*/
|
|
long long fstb_cam_active_ts;
|
|
int fstb_is_cam_active;
|
|
void fpsgo_comp2fstb_camera_active(int pid)
|
|
{
|
|
mutex_lock(&fstb_cam_active_time);
|
|
fstb_cam_active_ts = ktime_to_us(ktime_get());
|
|
mutex_unlock(&fstb_cam_active_time);
|
|
|
|
mtk_fstb_dprintk("camera_api pid %d\n", pid);
|
|
fpsgo_systrace_c_fstb(pid, 0, pid, "camera_active");
|
|
}
|
|
|
|
static void fstb_set_cam_active(int active)
|
|
{
|
|
if (fstb_is_cam_active == active)
|
|
return;
|
|
|
|
fstb_is_cam_active = active;
|
|
fpsgo_gpu_block_boost_enable_camera(active ? 0 : -1);
|
|
}
|
|
|
|
static void fstb_check_cam_status(void)
|
|
{
|
|
mutex_lock(&fstb_cam_active_time);
|
|
if (ktime_to_us(ktime_get()) - fstb_cam_active_ts < 1000000LL)
|
|
fstb_set_cam_active(1);
|
|
else
|
|
fstb_set_cam_active(0);
|
|
mutex_unlock(&fstb_cam_active_time);
|
|
}
|
|
|
|
static int fstb_get_queue_fps2(struct FSTB_FRAME_INFO *iter)
|
|
{
|
|
unsigned long long retval = 0;
|
|
unsigned long long duration = 0;
|
|
|
|
|
|
duration =
|
|
iter->queue_time_ts[iter->queue_time_end - 1] -
|
|
iter->queue_time_ts[iter->queue_time_end - JUMP_CHECK_NUM];
|
|
do_div(duration, 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)
|
|
{
|
|
struct FSTB_FRAME_INFO *iter;
|
|
ktime_t cur_time;
|
|
long long cur_time_us = 0;
|
|
struct task_struct *tsk = NULL, *gtsk = NULL;
|
|
|
|
mutex_lock(&fstb_lock);
|
|
|
|
if (!fstb_enable) {
|
|
mutex_unlock(&fstb_lock);
|
|
return;
|
|
}
|
|
|
|
if (!fstb_active) {
|
|
fstb_active = 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)
|
|
break;
|
|
}
|
|
|
|
|
|
if (iter == 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_margin = 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->sbe_state = 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->new_info = 1;
|
|
new_frame_info->m_c_time = 0;
|
|
new_frame_info->m_c_cap = 0;
|
|
new_frame_info->m_v_time = 0;
|
|
new_frame_info->m_v_cap = 0;
|
|
new_frame_info->m_m_time = 0;
|
|
new_frame_info->m_m_cap = 0;
|
|
new_frame_info->gblock_b = 0ULL;
|
|
new_frame_info->gblock_time = 0ULL;
|
|
new_frame_info->fps_raise_flag = 0;
|
|
new_frame_info->vote_i = 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;
|
|
}
|
|
|
|
iter = new_frame_info;
|
|
hlist_add_head(&iter->hlist, &fstb_frame_infos);
|
|
}
|
|
|
|
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;
|
|
|
|
if (iter->queue_time_end - iter->queue_time_begin >= 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_man(iter->pid, iter->bufid, tmp_q_fps,
|
|
"tmp_q_fps");
|
|
|
|
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 - JUMP_CHECK_NUM + 1]),
|
|
sizeof(int) * (JUMP_CHECK_NUM - 1));
|
|
iter->vote_i = JUMP_CHECK_NUM - 1;
|
|
iter->vote_fps[iter->vote_i] = tmp_target_fps;
|
|
iter->vote_i++;
|
|
}
|
|
|
|
if (iter->vote_i >= JUMP_CHECK_NUM) {
|
|
tmp_vote_fps =
|
|
mode(
|
|
&(iter->vote_fps[iter->vote_i - JUMP_CHECK_NUM]),
|
|
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;
|
|
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 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;
|
|
|
|
/* 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++;
|
|
}
|
|
}
|
|
|
|
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);
|
|
mtk_fstb_dprintk("%s %d %llu\n",
|
|
__func__, iter->pid, retval);
|
|
fpsgo_systrace_c_fstb_man(iter->pid, iter->bufid, (int)retval,
|
|
"queue_fps");
|
|
return retval;
|
|
}
|
|
mtk_fstb_dprintk("%s %d %d\n", __func__, iter->pid, 0);
|
|
fpsgo_systrace_c_fstb_man(iter->pid, iter->bufid, 0, "queue_fps");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fps_update(struct FSTB_FRAME_INFO *iter)
|
|
{
|
|
iter->queue_fps =
|
|
fstb_get_queue_fps1(iter, FRAME_TIME_WINDOW_SIZE_US);
|
|
|
|
return iter->queue_fps;
|
|
}
|
|
|
|
/* 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;
|
|
|
|
|
|
hlist_for_each_entry(rtfiter, &fstb_render_target_fps, hlist) {
|
|
mtk_fstb_dprintk("%s %s %d %s %d\n",
|
|
__func__, iter->proc_name, iter->pid,
|
|
rtfiter->process_name, rtfiter->pid);
|
|
|
|
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;
|
|
}
|
|
|
|
iter->target_fps_margin2 = asfc_turn ? RESET_TOLERENCE : 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);
|
|
|
|
#if API_READY
|
|
{
|
|
struct pob_fpsgo_fpsstats_info pffi = {0};
|
|
|
|
pffi.quantile_weighted_cpu_time = cur_cpu_time;
|
|
pffi.quantile_weighted_gpu_time = cur_gpu_time;
|
|
|
|
pob_fpsgo_fstb_stats_update(POB_FPSGO_FSTB_STATS_UPDATE, &pffi);
|
|
}
|
|
#endif
|
|
|
|
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) {
|
|
#if API_READY
|
|
fpsgo_fstb2eara_notify_fps_bound();
|
|
#endif
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
void (*eara_thrm_gblock_bypass_fp)(int pid, unsigned long long bufid,
|
|
int bypass);
|
|
#define FSTB_SEC_DIVIDER 1000000000
|
|
void fpsgo_fbt2fstb_query_fps(int pid, unsigned long long bufID,
|
|
int *target_fps, int *target_cpu_time,
|
|
int tgid, unsigned long long mid)
|
|
{
|
|
struct FSTB_FRAME_INFO *iter = NULL;
|
|
unsigned long long total_time, v_c_time;
|
|
int tolerence_fps = 0;
|
|
|
|
mutex_lock(&fstb_lock);
|
|
|
|
hlist_for_each_entry(iter, &fstb_frame_infos, hlist) {
|
|
if (iter->pid == pid && iter->bufid == bufID)
|
|
break;
|
|
}
|
|
|
|
if (!iter) {
|
|
*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;
|
|
|
|
} else {
|
|
switch (iter->sbe_state) {
|
|
case -1:
|
|
*target_fps = -1;
|
|
tolerence_fps = 0;
|
|
break;
|
|
case 0:
|
|
*target_fps = iter->target_fps;
|
|
tolerence_fps = iter->target_fps_margin;
|
|
break;
|
|
case 1:
|
|
*target_fps = max_fps_limit;
|
|
tolerence_fps = 0;
|
|
break;
|
|
default:
|
|
*target_fps = iter->target_fps;
|
|
tolerence_fps = iter->target_fps_margin;
|
|
break;
|
|
}
|
|
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);
|
|
|
|
if (total_time > 1000000ULL + iter->gblock_time &&
|
|
iter->gblock_time > 1000000ULL) {
|
|
fpsgo_systrace_c_fstb(pid, iter->bufid,
|
|
iter->gblock_time, "gblock_time");
|
|
total_time -= iter->gblock_time;
|
|
if (eara_thrm_gblock_bypass_fp)
|
|
eara_thrm_gblock_bypass_fp(iter->pid,
|
|
iter->bufid, 1);
|
|
} else {
|
|
if (eara_thrm_gblock_bypass_fp)
|
|
eara_thrm_gblock_bypass_fp(iter->pid,
|
|
iter->bufid, 0);
|
|
}
|
|
|
|
iter->gblock_time = 0ULL;
|
|
|
|
v_c_time = total_time;
|
|
|
|
}
|
|
|
|
*target_cpu_time = v_c_time;
|
|
|
|
mutex_unlock(&fstb_lock);
|
|
}
|
|
|
|
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 = iter->queue_fps > 0 ? iter->queue_fps : -1;
|
|
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 idle = 1;
|
|
int fstb_active2xgf;
|
|
int max_target_fps = -1;
|
|
int gpu_fps = max_fps_limit, tolerence_fps = 0;
|
|
|
|
if (work != &fps_stats_work)
|
|
kfree(work);
|
|
|
|
mutex_lock(&fstb_lock);
|
|
|
|
|
|
#if API_READY
|
|
pob_fpsgo_fstb_stats_update(POB_FPSGO_FSTB_STATS_START, NULL);
|
|
#endif
|
|
|
|
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);
|
|
|
|
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_margin2, "target_fps_margin2");
|
|
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");
|
|
|
|
gpu_fps = iter->target_fps;
|
|
tolerence_fps = iter->target_fps_margin;
|
|
|
|
switch (iter->sbe_state) {
|
|
case -1:
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
gpu_fps = max_fps_limit;
|
|
tolerence_fps = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
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;
|
|
/* if queue fps == 0, we delete that frame_info */
|
|
} else {
|
|
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 >= 2) {
|
|
fstb_active = 0;
|
|
fstb_idle_cnt = 0;
|
|
}
|
|
|
|
if (fstb_active)
|
|
fstb_active2xgf = 1;
|
|
else
|
|
fstb_active2xgf = 0;
|
|
|
|
if (fstb_enable && fstb_active)
|
|
enable_fstb_timer();
|
|
else
|
|
disable_fstb_timer();
|
|
|
|
|
|
mutex_unlock(&fstb_lock);
|
|
|
|
fstb_check_cam_status();
|
|
|
|
fpsgo_check_thread_status();
|
|
fpsgo_fstb2xgf_do_recycle(fstb_active2xgf);
|
|
fpsgo_create_render_dep();
|
|
}
|
|
|
|
static int set_soft_fps_level(int nr_level, struct fps_level *level)
|
|
{
|
|
mutex_lock(&fstb_lock);
|
|
|
|
if (nr_level != 1)
|
|
goto set_fps_level_err;
|
|
|
|
if (level->end > level->start)
|
|
goto set_fps_level_err;
|
|
|
|
memcpy(fps_levels, level, nr_level * sizeof(struct fps_level));
|
|
|
|
nr_fps_levels = nr_level;
|
|
max_fps_limit = min(dfps_ceiling, fps_levels->start);
|
|
min_fps_limit = min(dfps_ceiling, fps_levels->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 level[1];
|
|
|
|
level[0].start = CFG_MAX_FPS_LIMIT;
|
|
level[0].end = CFG_MIN_FPS_LIMIT;
|
|
|
|
set_soft_fps_level(1, level);
|
|
}
|
|
|
|
static ssize_t set_render_no_ctrl_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t set_render_no_ctrl_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char acBuffer[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
int arg;
|
|
int ret = 0;
|
|
|
|
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_no_ctrl");
|
|
if (arg > 0)
|
|
ret = switch_thread_no_ctrl(arg, 1);
|
|
else
|
|
ret = switch_thread_no_ctrl(-arg, 0);
|
|
}
|
|
}
|
|
|
|
out:
|
|
return count;
|
|
}
|
|
|
|
static KOBJ_ATTR_RW(set_render_no_ctrl);
|
|
|
|
static ssize_t set_render_max_fps_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t set_render_max_fps_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char acBuffer[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
int arg;
|
|
int ret = 0;
|
|
|
|
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:
|
|
return count;
|
|
}
|
|
|
|
static KOBJ_ATTR_RW(set_render_max_fps);
|
|
|
|
static ssize_t jump_check_num_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", JUMP_CHECK_NUM);
|
|
}
|
|
|
|
static ssize_t jump_check_num_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char acBuffer[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
int arg;
|
|
|
|
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)
|
|
switch_jump_check_num(arg);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static KOBJ_ATTR_RW(jump_check_num);
|
|
|
|
static ssize_t fstb_soft_level_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
char temp[FPSGO_SYSFS_MAX_BUFF_SIZE] = "";
|
|
int pos = 0;
|
|
int length;
|
|
int i;
|
|
|
|
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
|
|
"%d ", nr_fps_levels);
|
|
pos += length;
|
|
|
|
for (i = 0; i < nr_fps_levels; i++) {
|
|
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
|
|
"%d-%d ", fps_levels[i].start, fps_levels[i].end);
|
|
pos += length;
|
|
}
|
|
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
|
|
"\n");
|
|
pos += length;
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%s", temp);
|
|
}
|
|
|
|
static ssize_t fstb_soft_level_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char acBuffer[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
char *sepstr, *substr;
|
|
int ret = -EINVAL, new_nr_fps_levels, i, start_fps, end_fps;
|
|
struct fps_level *new_levels;
|
|
|
|
new_levels = kmalloc(sizeof(fps_levels), GFP_KERNEL);
|
|
if (new_levels == NULL) {
|
|
return count;
|
|
}
|
|
|
|
|
|
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 ||
|
|
kstrtoint(substr, 10,
|
|
&new_nr_fps_levels) != 0 ||
|
|
new_nr_fps_levels > MAX_NR_FPS_LEVELS) {
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < new_nr_fps_levels; i++) {
|
|
substr = strsep(&sepstr, " ");
|
|
if (!substr) {
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
/* maybe contiguous */
|
|
if (strchr(substr, '-')) {
|
|
if (sscanf(substr, "%d-%d",
|
|
&start_fps, &end_fps) != 2) {
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
new_levels[i].start = start_fps;
|
|
new_levels[i].end = end_fps;
|
|
} else { /* discrete */
|
|
if (kstrtoint(substr,
|
|
10, &start_fps) != 0) {
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
new_levels[i].start = start_fps;
|
|
new_levels[i].end = start_fps;
|
|
}
|
|
}
|
|
|
|
ret = !set_soft_fps_level(
|
|
new_nr_fps_levels, new_levels);
|
|
|
|
if (ret == 1)
|
|
ret = count;
|
|
else
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
|
|
err:
|
|
kfree(new_levels);
|
|
|
|
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[FPSGO_SYSFS_MAX_BUFF_SIZE] = "";
|
|
int pos = 0;
|
|
int length;
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%s", temp);
|
|
}
|
|
|
|
static ssize_t fstb_fps_list_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char acBuffer[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
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];
|
|
|
|
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:
|
|
return count;
|
|
}
|
|
|
|
static KOBJ_ATTR_RW(fstb_fps_list);
|
|
|
|
static ssize_t fstb_tune_window_size_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", FRAME_TIME_WINDOW_SIZE_US);
|
|
}
|
|
|
|
static ssize_t fstb_tune_window_size_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char acBuffer[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
int arg;
|
|
|
|
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)
|
|
switch_sample_window(arg);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static KOBJ_ATTR_RW(fstb_tune_window_size);
|
|
|
|
static ssize_t margin_mode_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", margin_mode);
|
|
}
|
|
|
|
static ssize_t margin_mode_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char acBuffer[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
int arg;
|
|
|
|
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)
|
|
switch_margin_mode(arg);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static KOBJ_ATTR_RW(margin_mode);
|
|
|
|
static ssize_t margin_mode_dbnc_a_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", margin_mode_dbnc_a);
|
|
}
|
|
|
|
static ssize_t margin_mode_dbnc_a_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char acBuffer[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
int arg;
|
|
|
|
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)
|
|
switch_margin_mode_dbnc_a(arg);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static KOBJ_ATTR_RW(margin_mode_dbnc_a);
|
|
|
|
static ssize_t margin_mode_dbnc_b_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", margin_mode_dbnc_b);
|
|
}
|
|
|
|
static ssize_t margin_mode_dbnc_b_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char acBuffer[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
int arg;
|
|
|
|
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)
|
|
switch_margin_mode_dbnc_b(arg);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static KOBJ_ATTR_RW(margin_mode_dbnc_b);
|
|
|
|
static ssize_t fstb_tune_quantile_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", QUANTILE);
|
|
}
|
|
|
|
static ssize_t fstb_tune_quantile_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char acBuffer[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
int arg;
|
|
|
|
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)
|
|
switch_percentile_frametime(arg);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static KOBJ_ATTR_RW(fstb_tune_quantile);
|
|
|
|
static ssize_t fstb_tune_error_threshold_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", fps_error_threshold);
|
|
}
|
|
|
|
static ssize_t fstb_tune_error_threshold_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char acBuffer[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
int arg;
|
|
|
|
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)
|
|
switch_fps_error_threhosld(arg);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static KOBJ_ATTR_RW(fstb_tune_error_threshold);
|
|
|
|
static ssize_t fstb_debug_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
char temp[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
int pos = 0;
|
|
int length;
|
|
|
|
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_idle_cnt %d\n", fstb_idle_cnt);
|
|
pos += length;
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%s", temp);
|
|
}
|
|
|
|
static ssize_t fstb_debug_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char acBuffer[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
int k_enable, klog_on;
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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[FPSGO_SYSFS_MAX_BUFF_SIZE];
|
|
int pos = 0;
|
|
int length;
|
|
|
|
mutex_lock(&fstb_lock);
|
|
|
|
if (!fstb_enable) {
|
|
mutex_unlock(&fstb_lock);
|
|
return 0;
|
|
}
|
|
|
|
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
|
|
"tid\tbufID\tname\t\tcurrentFPS\ttargetFPS\tFPS_margin\tsbe_state\n");
|
|
pos += length;
|
|
|
|
hlist_for_each_entry(iter, &fstb_frame_infos, hlist) {
|
|
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\n",
|
|
iter->pid,
|
|
iter->bufid,
|
|
iter->proc_name,
|
|
iter->queue_fps > max_fps_limit ?
|
|
max_fps_limit : iter->queue_fps,
|
|
iter->target_fps,
|
|
iter->target_fps_margin,
|
|
iter->sbe_state);
|
|
pos += length;
|
|
|
|
}
|
|
|
|
mutex_unlock(&fstb_lock);
|
|
|
|
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
|
|
"fstb_is_cam_active:%d\n", fstb_is_cam_active);
|
|
pos += length;
|
|
|
|
length = scnprintf(temp + pos, FPSGO_SYSFS_MAX_BUFF_SIZE - pos,
|
|
"dfps_ceiling:%d\n", dfps_ceiling);
|
|
pos += length;
|
|
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%s", temp);
|
|
}
|
|
static KOBJ_ATTR_RO(fpsgo_status);
|
|
|
|
int mtk_fstb_init(void)
|
|
{
|
|
int num_cluster = 0;
|
|
|
|
mtk_fstb_dprintk_always("init\n");
|
|
|
|
num_cluster = arch_nr_clusters();
|
|
|
|
ged_kpi_output_gfx_info2_fp = gpu_time_update;
|
|
|
|
|
|
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_fstb_tune_window_size);
|
|
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_set_render_max_fps);
|
|
fpsgo_sysfs_create_file(fstb_kobj,
|
|
&kobj_attr_set_render_no_ctrl);
|
|
}
|
|
|
|
reset_fps_level();
|
|
|
|
wq = create_singlethread_workqueue("mt_fstb");
|
|
if (!wq)
|
|
goto err;
|
|
|
|
hrtimer_init(&hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
hrt.function = &mt_fstb;
|
|
|
|
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_fstb_tune_window_size);
|
|
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_set_render_max_fps);
|
|
fpsgo_sysfs_remove_file(fstb_kobj,
|
|
&kobj_attr_set_render_no_ctrl);
|
|
|
|
fpsgo_sysfs_remove_dir(&fstb_kobj);
|
|
|
|
return 0;
|
|
}
|