// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include /*kernel4.4 #include */ #include "mt-plat/mtk_thermal_monitor.h" /* fps update from display */ #ifdef CONFIG_MTK_FB #include "disp_session.h" #endif #define SUPPORT_SWITCH_DEV (0) #if SUPPORT_SWITCH_DEV /* switch device to sent the (fps limit)uevent */ #include #endif #include "mach/mtk_thermal.h" #include #ifdef CONFIG_MTK_GPU_SUPPORT #include "ged_dvfs.h" #endif #include /* 1: turn on adaptive fps cooler; 0: turn off */ #define ADAPTIVE_FPS_COOLER (1) #ifdef CONFIG_MTK_DYNAMIC_FPS_FRAMEWORK_SUPPORT #if defined(CONFIG_MTK_FPSGO) || defined(CONFIG_MTK_FPSGO_V3) #define FPS_COOLER_USE_DFPS (0) #else #define FPS_COOLER_USE_DFPS (1) #endif #else #define FPS_COOLER_USE_DFPS (0) #endif #if FPS_COOLER_USE_DFPS #include "dfrc.h" #include "dfrc_drv.h" #endif #define mtk_cooler_fps_dprintk_always(fmt, args...) \ pr_debug("[Thermal/TC/fps]" fmt, ##args) #define mtk_cooler_fps_dprintk(fmt, args...) \ do { \ if (cl_fps_klog_on == 1) \ pr_notice("[Thermal/TC/fps]" fmt, ##args); \ } while (0) #define MAX_NUM_INSTANCE_MTK_COOLER_FPS 4 #define MTK_CL_FPS_GET_CURR_STATE(curr_state, state) \ { curr_state = (((unsigned long) (state))&0xFFFF); } #define MTK_CL_FPS_SET_CURR_STATE(curr_state, state) \ do { \ if (curr_state == 0) \ state &= ~0x1; \ else \ state |= 0x1; \ } while (0) static kuid_t uid = KUIDT_INIT(0); static kgid_t gid = KGIDT_INIT(1000); static int cl_fps_klog_on; static struct thermal_cooling_device *cl_fps_dev[MAX_NUM_INSTANCE_MTK_COOLER_FPS] = { 0 }; static unsigned int cl_fps_param[MAX_NUM_INSTANCE_MTK_COOLER_FPS] = { 0 }; static unsigned long cl_fps_state[MAX_NUM_INSTANCE_MTK_COOLER_FPS] = { 0 }; static unsigned int cl_fps_cur_limit; static unsigned int tm_input_fps; #if SUPPORT_SWITCH_DEV static struct switch_dev fps_switch_data; #endif #define FPS_STATS_WAKEUP_TIME_MS (1000) #define FPS_STATS_START_TIME_MS (60000) struct delayed_work fps_stats_work; #if ADAPTIVE_FPS_COOLER /* TODO: TBD */ #define CFG_MAX_FPS_LIMIT 60 #define CFG_MIN_FPS_LIMIT 20 static int max_fps_limit = CFG_MAX_FPS_LIMIT; static int min_fps_limit = CFG_MIN_FPS_LIMIT; /* TODO: TBD */ #define MAX_NR_FPS_LEVELS 8 struct fps_level { int start; int end; }; struct fps_level fps_levels[MAX_NR_FPS_LEVELS]; static int nr_fps_levels = MAX_NR_FPS_LEVELS; #define MAX_FPS_SMA_LEN 10 static int fps_history[MAX_FPS_SMA_LEN] = {0}; static int fps_history_idx; static int fps_sma_len = MAX_FPS_SMA_LEN; #define MAX_TPCB_SMA_LEN 10 static int tpcb_history[MAX_TPCB_SMA_LEN] = {0}; static int tpcb_history_idx; static int tpcb_sma_len = MAX_TPCB_SMA_LEN; #define MAX_GPU_LOADING_SMA_LEN 10 static int gpu_loading_history[MAX_GPU_LOADING_SMA_LEN] = {0}; static int gpu_loading_history_idx; static int gpu_loading_sma_len = MAX_GPU_LOADING_SMA_LEN; static struct thermal_cooling_device *cl_adp_fps_dev; static unsigned int cl_adp_fps_state; static int cl_adp_fps_limit = CFG_MAX_FPS_LIMIT; #define GPU_LOADING_THRESHOLD 60 /* in percentage */ static int fps_error_threshold = 10; static int fps_target_bias = 5; /* in round */ static int fps_stable_period = 10; /* FPS is active when over stable tpcb or always */ static int fps_limit_always_on; static int in_game_mode; #if FPS_COOLER_USE_DFPS static unsigned int fps_target_adjust; #endif #endif #ifndef __GED_TYPE_H__ enum GED_INFO { GED_EVENT_GAS_MODE, GED_UNDEFINED }; enum { GAS_CATEGORY_GAME, GAS_CATEGORY_OTHERS, }; #endif int __attribute__ ((weak)) disp_mgr_get_session_info(struct disp_session_info *info) { pr_notice("E_WF: %s doesn't exist\n", __func__); return 0; } unsigned long __attribute__ ((weak)) ged_query_info(GED_INFO eType) { pr_notice("E_WF: %s doesn't exist\n", __func__); return 0; } bool __attribute__ ((weak)) mtk_get_gpu_loading(unsigned int *pLoading) { #ifdef CONFIG_MTK_GPU_SUPPORT pr_notice("E_WF: %s doesn't exist\n", __func__); #endif return 0; } #if FPS_COOLER_USE_DFPS void dfrc_fps_limit_cb(int fps_limit) { ktime_t cur_time; static ktime_t pre_time; static int pre_fps_limit; static bool fps_adjust_check; if ((in_game_mode) && (fps_limit != DFRC_DRV_FPS_NON_ASSIGN)) { cur_time = ktime_get(); if (fps_adjust_check) { if ((cl_fps_cur_limit > pre_fps_limit) & (pre_fps_limit != -1)) fps_target_adjust += ((cl_fps_cur_limit - pre_fps_limit) * ktime_to_ms(ktime_sub( cur_time, pre_time))); mtk_cooler_fps_dprintk( "[%s] dfrc fps: %d, current limit: %d, target adjuct: %d\n", __func__, pre_fps_limit, cl_fps_cur_limit, fps_target_adjust); } pre_fps_limit = fps_limit; pre_time = cur_time; fps_adjust_check = 1; } else { fps_target_adjust = 0; fps_adjust_check = 0; } } EXPORT_SYMBOL(dfrc_fps_limit_cb); #endif int clfps_get_game_mode(void) { return in_game_mode; } int clfps_get_disp_fps(void) { return tm_input_fps; } static int game_mode_check(void) { unsigned long result = ged_query_info(GED_EVENT_GAS_MODE); if (result == GAS_CATEGORY_GAME) in_game_mode = 1; else in_game_mode = 0; return 0; } static int fps_update(void) { #ifdef CONFIG_MTK_FB struct disp_session_info info; memset(&info, 0, sizeof(info)); info.session_id = MAKE_DISP_SESSION(DISP_SESSION_PRIMARY, 0); disp_mgr_get_session_info(&info); /* mtk_cooler_fps_dprintk("display update fps is: %d.%d\n", * info.updateFPS/100, info.updateFPS%100); */ /* mtk_cooler_fps_dprintk("is display fps stable: %d\n", * info.is_updateFPS_stable); */ #if 0 if (info.is_updateFPS_stable) tm_input_fps = info.updateFPS; else tm_input_fps = 0; #else tm_input_fps = info.updateFPS/100; #endif #endif return 0; } static void mtk_cl_fps_set_fps_limit(void) { int i = 0; int min_limit = 60; unsigned int min_param = 60; #if FPS_COOLER_USE_DFPS int ret = -1; #endif for (; i < MAX_NUM_INSTANCE_MTK_COOLER_FPS; i++) { unsigned long curr_state; MTK_CL_FPS_GET_CURR_STATE(curr_state, cl_fps_state[i]); if (curr_state == 1) { int limit = 0; limit = cl_fps_param[i]; /* a cooler with 0 fps is not allowed */ if (limit == 0) goto err_unreg; if (limit <= min_limit) { min_limit = limit; min_param = cl_fps_param[i]; } } } #if ADAPTIVE_FPS_COOLER if (cl_adp_fps_limit < min_param) min_param = cl_adp_fps_limit; #endif if (min_param != cl_fps_cur_limit) { cl_fps_cur_limit = min_param; #if FPS_COOLER_USE_DFPS ret = dfrc_set_kernel_policy(DFRC_DRV_API_THERMAL, ((cl_fps_cur_limit != 60) ? cl_fps_cur_limit : -1), DFRC_DRV_MODE_INTERNAL_SW, 0, 0); mtk_cooler_fps_dprintk_always("[DFPS] fps:%d, ret = %d\n", cl_fps_cur_limit, ret); #else #if SUPPORT_SWITCH_DEV switch_set_state(&fps_switch_data, cl_fps_cur_limit); #endif #endif mtk_cooler_fps_dprintk_always("[%s] fps limit: %d\n", __func__, cl_fps_cur_limit); } err_unreg: return; } static int mtk_cl_fps_get_max_state( struct thermal_cooling_device *cdev, unsigned long *state) { *state = 1; mtk_cooler_fps_dprintk("[%s] %s %lu\n", __func__, cdev->type, *state); return 0; } static int mtk_cl_fps_get_cur_state( struct thermal_cooling_device *cdev, unsigned long *state) { MTK_CL_FPS_GET_CURR_STATE(*state, *((unsigned long *) cdev->devdata)); mtk_cooler_fps_dprintk("[%s] %s %lu\n", __func__, cdev->type, *state); return 0; } static int mtk_cl_fps_set_cur_state( struct thermal_cooling_device *cdev, unsigned long state) { mtk_cooler_fps_dprintk("[%s] %s %lu\n", __func__, cdev->type, state); MTK_CL_FPS_SET_CURR_STATE(state, *((unsigned long *) cdev->devdata)); mtk_cl_fps_set_fps_limit(); return 0; } #if ADAPTIVE_FPS_COOLER static int adp_fps_get_max_state( struct thermal_cooling_device *cdev, unsigned long *state) { *state = 1; mtk_cooler_fps_dprintk("[%s] %s %lu\n", __func__, cdev->type, *state); return 0; } static int adp_fps_get_cur_state( struct thermal_cooling_device *cdev, unsigned long *state) { *state = cl_adp_fps_state; mtk_cooler_fps_dprintk("[%s] %s %lu\n", __func__, cdev->type, *state); return 0; } /* for gpu_loading, tpcb, fps */ static int get_sma_val(int vals[], int sma_len) { int i, v = 0; for (i = 0; i < sma_len; i++) v += vals[i]; v = v / sma_len; return v; } static void set_sma_val(int vals[], int sma_len, int *idx, int val) { vals[*idx] = val; *idx = (*idx + 1) % sma_len; } /* Increase FPS limit: current limit + 10% (fps_error_threshold) * @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 increase_fps_limit(void) { int i, target_limit, fps_limit = max_fps_limit; target_limit = cl_adp_fps_limit * (100 + fps_error_threshold) / 100; if (target_limit >= max_fps_limit) return max_fps_limit; for (i = nr_fps_levels - 1; i >= 0; i--) { if (fps_levels[i].start == fps_levels[i].end) { if (fps_levels[i].end >= target_limit) { fps_limit = fps_levels[i].end; break; } } else { if (fps_levels[i].start >= target_limit) { if (target_limit > fps_levels[i].end) fps_limit = target_limit; else fps_limit = fps_levels[i].end; break; } } } if (i < 0) fps_limit = max_fps_limit; return fps_limit; } /** * floor function applied to fps_levels * @retval new fps limit * search in descending order, find the range whose 'end' is smaller than fps * should consider fps_error_threshold */ int find_fps_floor(int fps) { int i; for (i = 0; i < nr_fps_levels; i++) { /* sma_fps >= fps_limit * 90% */ if (fps >= fps_levels[i].end * (100 - fps_error_threshold) / 100) { if (fps_levels[i].start >= fps && fps >= fps_levels[i].end) return fps; else return fps_levels[i].end; } } return min_fps_limit; } /** * We consider to decrease fps limit to avoid unstable fps only if the * system already utilizes its full capacity. * e.g., gpu utilization already reaches a threshold, or maybe other index? */ static bool is_system_too_busy(void) { int gpu_loading; /* GPU cases */ gpu_loading = get_sma_val(gpu_loading_history, gpu_loading_sma_len); mtk_cooler_fps_dprintk("[%s] gpu_loading = %d\n", __func__, gpu_loading); if (gpu_loading >= GPU_LOADING_THRESHOLD) return true; /* TBD: other cases? */ return false; } /* This function is actually an governor */ static int adp_calc_fps_limit(void) { static int last_change_tpcb; static int period; #if FPS_COOLER_USE_DFPS static int fixedT_period; #endif int sma_tpcb, tpcb_change, sma_fps; int fps_limit = cl_adp_fps_limit; if (period < fps_stable_period) { period++; return fps_limit; } period = 0; sma_tpcb = get_sma_val(tpcb_history, tpcb_sma_len); tpcb_change = sma_tpcb - last_change_tpcb; sma_fps = get_sma_val(fps_history, fps_sma_len); mtk_cooler_fps_dprintk( "[%s] sma_tpcb = %d, tpcb_change = %d, sma_fps = %d\n", __func__, sma_tpcb, tpcb_change, sma_fps); #if FPS_COOLER_USE_DFPS /* [Todo] adjust the fps target here */ /* fps_limit = fps_limit -(fps_target_adjust/(fps_stable_period*1000)); */ fps_target_adjust = 0; /* Increase fps limit when tpcb keep stable for 100 sec */ if (tpcb_change == 0) fixedT_period++; else fixedT_period = 0; #endif if (fps_limit_always_on || sma_tpcb >= mtk_thermal_get_tpcb_target()) { /* FPS variation is HIGH */ if (fps_limit - sma_fps > fps_limit * (fps_error_threshold + fps_target_bias) / 100) { /* TODO: TBD: is "sma_fpa < 40" still necessary? */ #if FPS_COOLER_USE_DFPS if (is_system_too_busy() && (!is_cpu_power_unlimit())) { sma_fps = sma_fps + fps_limit * fps_target_bias / 100; #else if (sma_fps < 40 && is_system_too_busy()) { #endif fps_limit = find_fps_floor(sma_fps); mtk_cooler_fps_dprintk( "[%s] new_fps_limit = %d\n", __func__, fps_limit); } } else { /* For always-on and low tpcb */ #if FPS_COOLER_USE_DFPS if (fixedT_period >= fps_stable_period || tpcb_change < 0) { fps_limit = increase_fps_limit(); fixedT_period = 0; } #else if (sma_tpcb < mtk_thermal_get_tpcb_target() && tpcb_change < 0) fps_limit = increase_fps_limit(); #endif } } else if (tpcb_change < 0) { /* not always-on and low tpcb */ if (fps_limit - sma_fps <= fps_limit * fps_error_threshold / 100) fps_limit = increase_fps_limit(); } if (tpcb_change) last_change_tpcb = sma_tpcb; return fps_limit; } static void clfps_fps_stats(struct work_struct *work) { /* check the fps update from display */ fps_update(); set_sma_val(fps_history, fps_sma_len, &fps_history_idx, tm_input_fps); queue_delayed_work(system_unbound_wq, &fps_stats_work, msecs_to_jiffies(FPS_STATS_WAKEUP_TIME_MS)); } static int adp_fps_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) { int gpu_loading; if ((state != 0) && (state != 1)) { mtk_cooler_fps_dprintk( "[%s] invalid input (0: no thro; 1: adp fps thro on)\n", __func__); return 0; } mtk_cooler_fps_dprintk("[%s] %s %lu\n", __func__, cdev->type, state); cl_adp_fps_state = state; /* game? */ game_mode_check(); set_sma_val(tpcb_history, tpcb_sma_len, &tpcb_history_idx, mtk_thermal_get_temp(MTK_THERMAL_SENSOR_AP)); if (!mtk_get_gpu_loading(&gpu_loading)) gpu_loading = 0; set_sma_val(gpu_loading_history, gpu_loading_sma_len, &gpu_loading_history_idx, gpu_loading); /* 1. update the parameter of "cl_adp_fps_limit" */ /* we do not limit FPS if not in game */ if (in_game_mode && (cl_adp_fps_state || fps_limit_always_on)) cl_adp_fps_limit = adp_calc_fps_limit(); else cl_adp_fps_limit = max_fps_limit; /* 2. set the the limit */ mtk_cl_fps_set_fps_limit(); return 0; } static struct thermal_cooling_device_ops mtk_cl_adp_fps_ops = { .get_max_state = adp_fps_get_max_state, .get_cur_state = adp_fps_get_cur_state, .set_cur_state = adp_fps_set_cur_state, }; static void reset_fps_level(void) { int i, fps; for (i = 0; i < 4; i++) { fps = CFG_MAX_FPS_LIMIT / (i + 1); fps_levels[i].start = fps; fps_levels[i].end = fps; } nr_fps_levels = 4; } static int clfps_level_read(struct seq_file *m, void *v) { int i; seq_printf(m, "%d ", nr_fps_levels); for (i = 0; i < nr_fps_levels; i++) seq_printf(m, "%d-%d ", fps_levels[i].start, fps_levels[i].end); seq_puts(m, "\n"); return 0; } /* format example: 4 60-45 30-30 20-20 15-15 * compatible: 4 60 30 20 15 * mixed: 4 60-45 30 20 15 */ static ssize_t clfps_level_write(struct file *file, const char __user *buffer, size_t count, loff_t *data) { char *buf, *sepstr, *substr; int ret = -EINVAL, new_nr_fps_levels, i, start_fps, end_fps; struct fps_level *new_levels; /* we do not allow change fps_level during fps throttling, * because fps_levels would be changed. */ if (cl_adp_fps_limit != max_fps_limit) return -EAGAIN; if (count >= 128 || count < 1) return -EINVAL; buf = kmalloc(count + 1, GFP_KERNEL); if (buf == NULL) return -ENOMEM; new_levels = kmalloc(sizeof(fps_levels), GFP_KERNEL); if (new_levels == NULL) { ret = -ENOMEM; goto err_freebuf; } if (copy_from_user(buf, buffer, count)) { ret = -EFAULT; goto err; } buf[count] = '\0'; sepstr = buf; substr = strsep(&sepstr, " "); if (kstrtoint(substr, 10, &new_nr_fps_levels) != 0 || ((new_nr_fps_levels > MAX_NR_FPS_LEVELS) || (new_nr_fps_levels <= 0))) { ret = -EINVAL; goto err; } for (i = 0; i < new_nr_fps_levels; i++) { substr = strsep(&sepstr, " "); if (!substr) { ret = -EINVAL; goto err; } if (strchr(substr, '-')) { /* maybe contiguous */ 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; } } for (i = 0; i < new_nr_fps_levels; i++) { /* check if they are interleaving */ if (new_levels[i].end > new_levels[i].start || (i > 0 && new_levels[i].start > new_levels[i - 1].end)) { ret = -EINVAL; goto err; } } ret = count; nr_fps_levels = new_nr_fps_levels; memcpy(fps_levels, new_levels, sizeof(fps_levels)); max_fps_limit = fps_levels[0].start; min_fps_limit = fps_levels[nr_fps_levels - 1].end; err: kfree(new_levels); err_freebuf: kfree(buf); return ret; } static int clfps_level_open(struct inode *inode, struct file *file) { return single_open(file, clfps_level_read, NULL); } static const struct file_operations clfps_level_fops = { .owner = THIS_MODULE, .open = clfps_level_open, .read = seq_read, .llseek = seq_lseek, .write = clfps_level_write, .release = single_release, }; #endif /* bind fan callbacks to fan device */ static struct thermal_cooling_device_ops mtk_cl_fps_ops = { .get_max_state = mtk_cl_fps_get_max_state, .get_cur_state = mtk_cl_fps_get_cur_state, .set_cur_state = mtk_cl_fps_set_cur_state, }; static int mtk_cooler_fps_register_ltf(void) { int i; mtk_cooler_fps_dprintk("register ltf\n"); for (i = MAX_NUM_INSTANCE_MTK_COOLER_FPS; i-- > 0; ) { char temp[20] = { 0 }; sprintf(temp, "mtk-cl-fps%02d", i); /* put fps state to cooler devdata */ cl_fps_dev[i] = mtk_thermal_cooling_device_register( temp, (void *) &cl_fps_state[i], &mtk_cl_fps_ops); } #if ADAPTIVE_FPS_COOLER cl_adp_fps_dev = mtk_thermal_cooling_device_register("mtk-cl-adp-fps", NULL, &mtk_cl_adp_fps_ops); #endif return 0; } static void mtk_cooler_fps_unregister_ltf(void) { int i; mtk_cooler_fps_dprintk("unregister ltf\n"); for (i = MAX_NUM_INSTANCE_MTK_COOLER_FPS; i-- > 0; ) { if (cl_fps_dev[i]) { mtk_thermal_cooling_device_unregister(cl_fps_dev[i]); cl_fps_dev[i] = NULL; cl_fps_state[i] = 0; } } #if ADAPTIVE_FPS_COOLER if (cl_adp_fps_dev) { mtk_thermal_cooling_device_unregister(cl_adp_fps_dev); cl_adp_fps_dev = NULL; cl_adp_fps_state = 0; } #endif } static int mtk_cl_fps_proc_read(struct seq_file *m, void *v) { /** * The format to print out: * kernel_log <0 or 1> * > * .. */ { int i = 0; seq_printf(m, "klog %d\n", cl_fps_klog_on); seq_printf(m, "curr_limit %d\n", cl_fps_cur_limit); for (; i < MAX_NUM_INSTANCE_MTK_COOLER_FPS; i++) { unsigned int active; unsigned long curr_state; active = cl_fps_param[i]; MTK_CL_FPS_GET_CURR_STATE(curr_state, cl_fps_state[i]); seq_printf(m, "mtk-cl-fps%02d %u 0x%x, state %lu\n", i, active, cl_fps_param[i], curr_state); } } return 0; } static ssize_t mtk_cl_fps_proc_write( struct file *filp, const char __user *buffer, size_t count, loff_t *data) { int len = 0; char desc[128]; int klog_on, fps0, fps1, fps2, fps3; len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1); if (copy_from_user(desc, buffer, len)) return 0; desc[len] = '\0'; /* * sscanf format * can only be 0 or 1 */ if (data == NULL) { mtk_cooler_fps_dprintk("[%s] null data\n", __func__); return -EINVAL; } /* WARNING: Modify here if MAX_NUM_INSTANCE_MTK_COOLER_FPS * is changed to other than 4 */ #if (MAX_NUM_INSTANCE_MTK_COOLER_FPS == 4) if (sscanf(desc, "%d %d %d %d %d", &klog_on, &fps0, &fps1, &fps2, &fps3) >= 1) { if (klog_on == 0 || klog_on == 1) cl_fps_klog_on = klog_on; /* [Fix me] debug only */ fps_limit_always_on = 0; if (klog_on == 2) fps_limit_always_on = 1; if (fps0 == 0) cl_fps_param[0] = 0; else if (fps0 >= 10 && fps0 <= 60) cl_fps_param[0] = fps0; if (fps1 == 0) cl_fps_param[1] = 0; else if (fps1 >= 10 && fps1 <= 60) cl_fps_param[1] = fps1; if (fps2 == 0) cl_fps_param[2] = 0; else if (fps2 >= 10 && fps2 <= 60) cl_fps_param[2] = fps2; if (fps3 == 0) cl_fps_param[3] = 0; else if (fps3 >= 10 && fps3 <= 60) cl_fps_param[3] = fps3; return count; } #else #error \ "Change correspondent part when changing MAX_NUM_INSTANCE_MTK_COOLER_FPS!" #endif mtk_cooler_fps_dprintk("[%s] bad arg\n", __func__); return -EINVAL; } static int mtk_cl_fps_proc_open(struct inode *inode, struct file *file) { return single_open(file, mtk_cl_fps_proc_read, NULL); } static const struct file_operations cl_fps_fops = { .owner = THIS_MODULE, .open = mtk_cl_fps_proc_open, .read = seq_read, .llseek = seq_lseek, .write = mtk_cl_fps_proc_write, .release = single_release, }; static ssize_t fps_tm_count_write( struct file *filp, const char __user *buf, size_t len, loff_t *data) { char tmp[32] = {0}; len = (len < (sizeof(tmp) - 1)) ? len : (sizeof(tmp) - 1); /* write data to the buffer */ if (copy_from_user(tmp, buf, len)) return -EFAULT; if (kstrtoint(tmp, 10, &tm_input_fps) == 0) { mtk_cooler_fps_dprintk("[%s] = %d\n", __func__, tm_input_fps); return len; } mtk_cooler_fps_dprintk("[%s] invalid input\n", __func__); return -EINVAL; } static int fps_tm_count_read(struct seq_file *m, void *v) { seq_printf(m, "%d,%d,%d\n", tm_input_fps, cl_fps_cur_limit, in_game_mode); mtk_cooler_fps_dprintk("[%s] %d\n", __func__, tm_input_fps); return 0; } static int fps_tm_count_open(struct inode *inode, struct file *file) { return single_open(file, fps_tm_count_read, PDE_DATA(inode)); } static const struct file_operations tm_fps_fops = { .owner = THIS_MODULE, .open = fps_tm_count_open, .read = seq_read, .llseek = seq_lseek, .write = fps_tm_count_write, .release = single_release, }; /* ====FPS_DEBUGFS========= *#define debugfs_entry(name) \ *do { \ * dentry_f = debugfs_create_u32 * (#name, S_IWUSR | S_IRUGO, _d, &name); \ * if (IS_ERR_OR_NULL(dentry_f)) { \ * pr_notice("Unable to create debugfsfile: " #name "\n"); \ * return; \ * } \ *} while (0) * *static void create_debugfs_entries(void) *{ * struct dentry *dentry_f; * struct dentry *_d; * * _d = debugfs_create_dir("clfps", NULL); * if (IS_ERR_OR_NULL(_d)) { * pr_info("unable to create debugfs directory\n"); * return; * } * * debugfs_entry(fps_error_threshold); * debugfs_entry(fps_stable_period); * debugfs_entry(in_game_mode); * debugfs_entry(fps_limit_always_on); *} *#undef debugfs_entry *====FPS_DEBUGFS=========== */ static int __init mtk_cooler_fps_init(void) { #if SUPPORT_SWITCH_DEV int ret = 0; #endif int err = 0; int i; for (i = MAX_NUM_INSTANCE_MTK_COOLER_FPS; i-- > 0; ) { cl_fps_dev[i] = NULL; cl_fps_state[i] = 0; } mtk_cooler_fps_dprintk("init\n"); err = mtk_cooler_fps_register_ltf(); if (err) goto err_unreg; #if SUPPORT_SWITCH_DEV /* switch device to sent the (fps limit)uevent */ fps_switch_data.name = "fps"; fps_switch_data.index = 0; fps_switch_data.state = 60; /* original 60 frames */ ret = switch_dev_register(&fps_switch_data); if (ret) mtk_cooler_fps_dprintk_always( "[%s] switch_dev_register failed, returned:%d!\n", __func__, ret); #endif /* create a proc file */ { struct proc_dir_entry *entry = NULL; struct proc_dir_entry *dir_entry = NULL; struct proc_dir_entry *fps_tm_proc_dir = NULL; fps_tm_proc_dir = proc_mkdir("fps_tm", NULL); if (!fps_tm_proc_dir) mtk_cooler_fps_dprintk_always( "[%s]: mkdir /proc/fps_tm failed\n", __func__); else { entry = proc_create("fps_count", 0664, fps_tm_proc_dir, &tm_fps_fops); if (entry) proc_set_user(entry, uid, gid); } dir_entry = mtk_thermal_get_proc_drv_therm_dir_entry(); if (!dir_entry) mtk_cooler_fps_dprintk_always( "[%s]: mkdir /proc/driver/thermal failed\n", __func__); else { entry = proc_create("clfps", 0664, dir_entry, &cl_fps_fops); if (entry) proc_set_user(entry, uid, gid); } #if ADAPTIVE_FPS_COOLER reset_fps_level(); if (dir_entry) { entry = proc_create("clfps_level", 0664, dir_entry, &clfps_level_fops); if (entry) proc_set_user(entry, uid, gid); } /* ====FPS_DEBUGFS========= * create_debugfs_entries(); *====FPS_DEBUGFS=========== */ #endif INIT_DEFERRABLE_WORK(&fps_stats_work, clfps_fps_stats); queue_delayed_work(system_unbound_wq, &fps_stats_work, msecs_to_jiffies(FPS_STATS_START_TIME_MS)); return 0; } err_unreg: mtk_cooler_fps_unregister_ltf(); return err; } static void __exit mtk_cooler_fps_exit(void) { mtk_cooler_fps_dprintk("exit\n"); if (delayed_work_pending(&fps_stats_work)) cancel_delayed_work(&fps_stats_work); /* remove the proc file */ remove_proc_entry("clfps", NULL); mtk_cooler_fps_unregister_ltf(); } module_init(mtk_cooler_fps_init); module_exit(mtk_cooler_fps_exit);