unplugged-kernel/drivers/misc/mediatek/performance/fpsgo_v3/fstb/fstb.c

2371 lines
56 KiB
C
Raw Normal View History

// 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;
}