// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include "mt-plat/fpsgo_common.h" #include "fpsgo_base.h" #include "fpsgo_sysfs.h" #include "fpsgo_usedext.h" #include "fbt_cpu.h" #include "fstb.h" #include "fps_composer.h" #include "xgf.h" #ifdef CONFIG_DRM_MEDIATEK #include "mtk_drm_arr.h" #endif #define CREATE_TRACE_POINTS #include #define API_READY 0 #define TARGET_UNLIMITED_FPS 240 enum FPSGO_NOTIFIER_PUSH_TYPE { FPSGO_NOTIFIER_SWITCH_FPSGO = 0x00, FPSGO_NOTIFIER_QUEUE_DEQUEUE = 0x01, FPSGO_NOTIFIER_CONNECT = 0x02, FPSGO_NOTIFIER_DFRC_FPS = 0x03, FPSGO_NOTIFIER_BQID = 0x04, FPSGO_NOTIFIER_NN_JOB_BEGIN = 0x05, FPSGO_NOTIFIER_NN_JOB_END = 0x06, FPSGO_NOTIFIER_GPU_BLOCK = 0x07, FPSGO_NOTIFIER_VSYNC = 0x08, }; /* TODO: use union*/ struct FPSGO_NOTIFIER_PUSH_TAG { enum FPSGO_NOTIFIER_PUSH_TYPE ePushType; int pid; unsigned long long cur_ts; int enable; int qudeq_cmd; unsigned int queue_arg; unsigned long long bufID; int connectedAPI; int queue_SF; unsigned long long identifier; int create; int dfrc_fps; int num_step; __s32 *device; __s32 *boost; __u64 *exec_time; int tid; int start; struct work_struct sWork; }; static struct mutex notify_lock; struct workqueue_struct *g_psNotifyWorkQueue; static int fpsgo_enable; static int fpsgo_force_onoff; static int gpu_boost_enable_perf; static int gpu_boost_enable_camera; void (*rsu_cpufreq_notifier_fp)(int cluster_id, unsigned long freq); /* TODO: event register & dispatch */ int fpsgo_is_enable(void) { int enable; mutex_lock(¬ify_lock); enable = fpsgo_enable; mutex_unlock(¬ify_lock); FPSGO_LOGI("[FPSGO_CTRL] isenable %d\n", enable); return enable; } static void fpsgo_notifier_wq_cb_vsync(unsigned long long ts) { FPSGO_LOGI("[FPSGO_CB] vsync: %llu\n", ts); if (!fpsgo_is_enable()) return; fpsgo_ctrl2fbt_vsync(ts); } static void fpsgo_notifier_wq_cb_dfrc_fps(int dfrc_fps) { FPSGO_LOGI("[FPSGO_CB] dfrc_fps %d\n", dfrc_fps); fpsgo_ctrl2fstb_dfrc_fps(dfrc_fps); fpsgo_ctrl2fbt_dfrc_fps(dfrc_fps); } static void fpsgo_notifier_wq_cb_connect(int pid, int connectedAPI, unsigned long long id) { FPSGO_LOGI( "[FPSGO_CB] connect: pid %d, API %d, id %llu\n", pid, connectedAPI, id); if (connectedAPI == WINDOW_DISCONNECT) fpsgo_ctrl2comp_disconnect_api(pid, connectedAPI, id); else fpsgo_ctrl2comp_connect_api(pid, connectedAPI, id); } static void fpsgo_notifier_wq_cb_bqid(int pid, unsigned long long bufID, int queue_SF, unsigned long long id, int create) { FPSGO_LOGI( "[FPSGO_CB] bqid: pid %d, bufID %llu, queue_SF %d, id %llu, create %d\n", pid, bufID, queue_SF, id, create); fpsgo_ctrl2comp_bqid(pid, bufID, queue_SF, id, create); } static void fpsgo_notifier_wq_cb_gblock(int tid, int start) { FPSGO_LOGI( "[FPSGO_CB] gblock: tid %d, start %d\n", tid, start); if (!fpsgo_is_enable()) return; fpsgo_ctrl2fstb_gblock(tid, start); } static void fpsgo_notifier_wq_cb_qudeq(int qudeq, unsigned int startend, int cur_pid, unsigned long long curr_ts, unsigned long long id) { FPSGO_LOGI("[FPSGO_CB] qudeq: %d-%d, pid %d, ts %llu, id %llu\n", qudeq, startend, cur_pid, curr_ts, id); if (!fpsgo_is_enable()) return; switch (qudeq) { case 1: if (startend) { FPSGO_LOGI("[FPSGO_CB] QUEUE Start: pid %d\n", cur_pid); fpsgo_ctrl2comp_enqueue_start(cur_pid, curr_ts, id); } else { FPSGO_LOGI("[FPSGO_CB] QUEUE End: pid %d\n", cur_pid); fpsgo_ctrl2comp_enqueue_end(cur_pid, curr_ts, id); } break; case 0: if (startend) { FPSGO_LOGI("[FPSGO_CB] DEQUEUE Start: pid %d\n", cur_pid); fpsgo_ctrl2comp_dequeue_start(cur_pid, curr_ts, id); } else { FPSGO_LOGI("[FPSGO_CB] DEQUEUE End: pid %d\n", cur_pid); fpsgo_ctrl2comp_dequeue_end(cur_pid, curr_ts, id); } break; default: break; } } static void fpsgo_notifier_wq_cb_enable(int enable) { FPSGO_LOGI( "[FPSGO_CB] enable %d, fpsgo_enable %d, force_onoff %d\n", enable, fpsgo_enable, fpsgo_force_onoff); mutex_lock(¬ify_lock); if (enable == fpsgo_enable) { mutex_unlock(¬ify_lock); return; } if (fpsgo_force_onoff != FPSGO_FREE && enable != fpsgo_force_onoff) { mutex_unlock(¬ify_lock); return; } fpsgo_ctrl2fbt_switch_fbt(enable); fpsgo_ctrl2fstb_switch_fstb(enable); fpsgo_ctrl2xgf_switch_xgf(enable); fpsgo_enable = enable; if (!fpsgo_enable) fpsgo_clear(); FPSGO_LOGI("[FPSGO_CB] fpsgo_enable %d\n", fpsgo_enable); mutex_unlock(¬ify_lock); } static void fpsgo_notifier_wq_cb(struct work_struct *psWork) { struct FPSGO_NOTIFIER_PUSH_TAG *vpPush = FPSGO_CONTAINER_OF(psWork, struct FPSGO_NOTIFIER_PUSH_TAG, sWork); if (!vpPush) { FPSGO_LOGE("[FPSGO_CTRL] ERROR\n"); return; } FPSGO_LOGI("[FPSGO_CTRL] push type = %d\n", vpPush->ePushType); switch (vpPush->ePushType) { case FPSGO_NOTIFIER_SWITCH_FPSGO: fpsgo_notifier_wq_cb_enable(vpPush->enable); break; case FPSGO_NOTIFIER_QUEUE_DEQUEUE: fpsgo_notifier_wq_cb_qudeq(vpPush->qudeq_cmd, vpPush->queue_arg, vpPush->pid, vpPush->cur_ts, vpPush->identifier); break; case FPSGO_NOTIFIER_CONNECT: fpsgo_notifier_wq_cb_connect(vpPush->pid, vpPush->connectedAPI, vpPush->identifier); break; case FPSGO_NOTIFIER_DFRC_FPS: fpsgo_notifier_wq_cb_dfrc_fps(vpPush->dfrc_fps); break; case FPSGO_NOTIFIER_BQID: fpsgo_notifier_wq_cb_bqid(vpPush->pid, vpPush->bufID, vpPush->queue_SF, vpPush->identifier, vpPush->create); break; case FPSGO_NOTIFIER_GPU_BLOCK: fpsgo_notifier_wq_cb_gblock(vpPush->tid, vpPush->start); break; case FPSGO_NOTIFIER_VSYNC: fpsgo_notifier_wq_cb_vsync(vpPush->cur_ts); break; default: FPSGO_LOGE("[FPSGO_CTRL] unhandled push type = %d\n", vpPush->ePushType); break; } fpsgo_free(vpPush, sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); } void fpsgo_notify_qudeq(int qudeq, unsigned int startend, int pid, unsigned long long id) { unsigned long long cur_ts; struct FPSGO_NOTIFIER_PUSH_TAG *vpPush; FPSGO_LOGI("[FPSGO_CTRL] qudeq %d-%d, id %llu pid %d\n", qudeq, startend, id, pid); if (!fpsgo_is_enable()) return; vpPush = (struct FPSGO_NOTIFIER_PUSH_TAG *) fpsgo_alloc_atomic(sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); if (!vpPush) { FPSGO_LOGE("[FPSGO_CTRL] OOM\n"); return; } if (!g_psNotifyWorkQueue) { FPSGO_LOGE("[FPSGO_CTRL] NULL WorkQueue\n"); fpsgo_free(vpPush, sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); return; } cur_ts = fpsgo_get_time(); vpPush->ePushType = FPSGO_NOTIFIER_QUEUE_DEQUEUE; vpPush->pid = pid; vpPush->cur_ts = cur_ts; vpPush->qudeq_cmd = qudeq; vpPush->queue_arg = startend; vpPush->identifier = id; INIT_WORK(&vpPush->sWork, fpsgo_notifier_wq_cb); queue_work(g_psNotifyWorkQueue, &vpPush->sWork); } void fpsgo_notify_connect(int pid, int connectedAPI, unsigned long long id) { struct FPSGO_NOTIFIER_PUSH_TAG *vpPush; FPSGO_LOGI( "[FPSGO_CTRL] connect pid %d, id %llu, API %d\n", pid, id, connectedAPI); vpPush = (struct FPSGO_NOTIFIER_PUSH_TAG *) fpsgo_alloc_atomic(sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); if (!vpPush) { FPSGO_LOGE("[FPSGO_CTRL] OOM\n"); return; } if (!g_psNotifyWorkQueue) { FPSGO_LOGE("[FPSGO_CTRL] NULL WorkQueue\n"); fpsgo_free(vpPush, sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); return; } vpPush->ePushType = FPSGO_NOTIFIER_CONNECT; vpPush->pid = pid; vpPush->connectedAPI = connectedAPI; vpPush->identifier = id; INIT_WORK(&vpPush->sWork, fpsgo_notifier_wq_cb); queue_work(g_psNotifyWorkQueue, &vpPush->sWork); } void fpsgo_notify_bqid(int pid, unsigned long long bufID, int queue_SF, unsigned long long id, int create) { struct FPSGO_NOTIFIER_PUSH_TAG *vpPush; FPSGO_LOGI("[FPSGO_CTRL] bqid pid %d, buf %llu, queue_SF %d, id %llu\n", pid, bufID, queue_SF, id); vpPush = (struct FPSGO_NOTIFIER_PUSH_TAG *) fpsgo_alloc_atomic(sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); if (!vpPush) { FPSGO_LOGE("[FPSGO_CTRL] OOM\n"); return; } if (!g_psNotifyWorkQueue) { FPSGO_LOGE("[FPSGO_CTRL] NULL WorkQueue\n"); fpsgo_free(vpPush, sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); return; } vpPush->ePushType = FPSGO_NOTIFIER_BQID; vpPush->pid = pid; vpPush->bufID = bufID; vpPush->queue_SF = queue_SF; vpPush->identifier = id; vpPush->create = create; INIT_WORK(&vpPush->sWork, fpsgo_notifier_wq_cb); queue_work(g_psNotifyWorkQueue, &vpPush->sWork); } int fpsgo_is_gpu_block_boost_enable(void) { int enable; mutex_lock(¬ify_lock); enable = max(gpu_boost_enable_camera, gpu_boost_enable_perf); mutex_unlock(¬ify_lock); return enable; } int fpsgo_is_gpu_block_boost_perf_enable(void) { int enable; mutex_lock(¬ify_lock); enable = gpu_boost_enable_perf; mutex_unlock(¬ify_lock); return enable; } int fpsgo_is_gpu_block_boost_camera_enable(void) { int enable; mutex_lock(¬ify_lock); enable = gpu_boost_enable_camera; mutex_unlock(¬ify_lock); return enable; } void fpsgo_gpu_block_boost_enable_perf(int enable) { mutex_lock(¬ify_lock); gpu_boost_enable_perf = enable; mutex_unlock(¬ify_lock); } void fpsgo_gpu_block_boost_enable_camera(int enable) { mutex_lock(¬ify_lock); gpu_boost_enable_camera = enable; mutex_unlock(¬ify_lock); } int fpsgo_notify_gpu_block(int tid, unsigned long long mid, int start) { struct FPSGO_NOTIFIER_PUSH_TAG *vpPush; int g_enable; FPSGO_LOGI("[FPSGO_CTRL] gblock pid %d, start %d\n", tid, start); g_enable = fpsgo_is_gpu_block_boost_enable(); if (g_enable < 0 || g_enable > 100) return -1; vpPush = (struct FPSGO_NOTIFIER_PUSH_TAG *) fpsgo_alloc_atomic(sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); if (!vpPush) { FPSGO_LOGE("[FPSGO_CTRL] OOM\n"); return -1; } if (!g_psNotifyWorkQueue) { FPSGO_LOGE("[FPSGO_CTRL] NULL WorkQueue\n"); fpsgo_free(vpPush, sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); return -1; } vpPush->ePushType = FPSGO_NOTIFIER_GPU_BLOCK; vpPush->tid = tid; vpPush->start = start; INIT_WORK(&vpPush->sWork, fpsgo_notifier_wq_cb); queue_work(g_psNotifyWorkQueue, &vpPush->sWork); return g_enable; } void fpsgo_notify_vsync(void) { struct FPSGO_NOTIFIER_PUSH_TAG *vpPush; FPSGO_LOGI("[FPSGO_CTRL] vsync\n"); if (!fpsgo_is_enable()) return; vpPush = (struct FPSGO_NOTIFIER_PUSH_TAG *) fpsgo_alloc_atomic(sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); if (!vpPush) { FPSGO_LOGE("[FPSGO_CTRL] OOM\n"); return; } if (!g_psNotifyWorkQueue) { FPSGO_LOGE("[FPSGO_CTRL] NULL WorkQueue\n"); fpsgo_free(vpPush, sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); return; } vpPush->ePushType = FPSGO_NOTIFIER_VSYNC; vpPush->cur_ts = fpsgo_get_time(); INIT_WORK(&vpPush->sWork, fpsgo_notifier_wq_cb); queue_work(g_psNotifyWorkQueue, &vpPush->sWork); } void fpsgo_get_fps(int *pid, int *fps) { //int pid = -1, fps = -1; fpsgo_ctrl2fstb_get_fps(pid, fps); FPSGO_LOGE("[FPSGO_CTRL] get_fps %d %d\n", *pid, *fps); //return fps; } void fpsgo_notify_cpufreq(int cid, unsigned long freq) { FPSGO_LOGI("[FPSGO_CTRL] cid %d, cpufreq %lu\n", cid, freq); if (rsu_cpufreq_notifier_fp) rsu_cpufreq_notifier_fp(cid, freq); if (!fpsgo_enable) return; fpsgo_ctrl2fbt_cpufreq_cb(cid, freq); } void dfrc_fps_limit_cb(unsigned int fps_limit) { unsigned int vTmp = TARGET_UNLIMITED_FPS; struct FPSGO_NOTIFIER_PUSH_TAG *vpPush; if (!fpsgo_is_enable()) return; if (fps_limit > 0 && fps_limit <= TARGET_UNLIMITED_FPS) vTmp = fps_limit; FPSGO_LOGI("[FPSGO_CTRL] dfrc_fps %d\n", vTmp); vpPush = (struct FPSGO_NOTIFIER_PUSH_TAG *) fpsgo_alloc_atomic(sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); if (!vpPush) { FPSGO_LOGE("[FPSGO_CTRL] OOM\n"); return; } if (!g_psNotifyWorkQueue) { FPSGO_LOGE("[FPSGO_CTRL] NULL WorkQueue\n"); fpsgo_free(vpPush, sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); return; } vpPush->ePushType = FPSGO_NOTIFIER_DFRC_FPS; vpPush->dfrc_fps = vTmp; INIT_WORK(&vpPush->sWork, fpsgo_notifier_wq_cb); queue_work(g_psNotifyWorkQueue, &vpPush->sWork); } /* FPSGO control */ void fpsgo_switch_enable(int enable) { struct FPSGO_NOTIFIER_PUSH_TAG *vpPush = NULL; if (!g_psNotifyWorkQueue) { FPSGO_LOGE("[FPSGO_CTRL] NULL WorkQueue\n"); return; } FPSGO_LOGI("[FPSGO_CTRL] switch enable %d\n", enable); if (fpsgo_is_force_enable() != FPSGO_FREE && enable != fpsgo_is_force_enable()) return; vpPush = (struct FPSGO_NOTIFIER_PUSH_TAG *) fpsgo_alloc_atomic(sizeof(struct FPSGO_NOTIFIER_PUSH_TAG)); if (!vpPush) { FPSGO_LOGE("[FPSGO_CTRL] OOM\n"); return; } vpPush->ePushType = FPSGO_NOTIFIER_SWITCH_FPSGO; vpPush->enable = enable; INIT_WORK(&vpPush->sWork, fpsgo_notifier_wq_cb); queue_work(g_psNotifyWorkQueue, &vpPush->sWork); } int fpsgo_is_force_enable(void) { int temp_onoff; mutex_lock(¬ify_lock); temp_onoff = fpsgo_force_onoff; mutex_unlock(¬ify_lock); return temp_onoff; } void fpsgo_force_switch_enable(int enable) { mutex_lock(¬ify_lock); fpsgo_force_onoff = enable; mutex_unlock(¬ify_lock); fpsgo_switch_enable(enable?1:0); } /* FSTB control */ int fpsgo_is_fstb_enable(void) { return is_fstb_enable(); } int fpsgo_switch_fstb(int enable) { return fpsgo_ctrl2fstb_switch_fstb(enable); } int fpsgo_fstb_sample_window(long long time_usec) { return switch_sample_window(time_usec); } int fpsgo_fstb_fps_range(int nr_level, struct fps_level *level) { return switch_fps_range(nr_level, level); } int fpsgo_fstb_process_fps_range(char *proc_name, int nr_level, struct fps_level *level) { return switch_process_fps_range(proc_name, nr_level, level); } int fpsgo_fstb_thread_fps_range(pid_t pid, int nr_level, struct fps_level *level) { return switch_thread_fps_range(pid, nr_level, level); } int fpsgo_fstb_fps_error_threhosld(int threshold) { return switch_fps_error_threhosld(threshold); } int fpsgo_fstb_percentile_frametime(int ratio) { return switch_percentile_frametime(ratio); } static void __exit fpsgo_exit(void) { fpsgo_notifier_wq_cb_enable(0); if (g_psNotifyWorkQueue) { flush_workqueue(g_psNotifyWorkQueue); destroy_workqueue(g_psNotifyWorkQueue); g_psNotifyWorkQueue = NULL; } #if API_READY disp_unregister_fps_chg_callback(dfrc_fps_limit_cb); #endif fbt_cpu_exit(); mtk_fstb_exit(); fpsgo_composer_exit(); fpsgo_sysfs_exit(); } static int __init fpsgo_init(void) { FPSGO_LOGI("[FPSGO_CTRL] init\n"); fpsgo_sysfs_init(); g_psNotifyWorkQueue = create_singlethread_workqueue("fpsgo_notifier_wq"); if (g_psNotifyWorkQueue == NULL) return -EFAULT; mutex_init(¬ify_lock); fpsgo_force_onoff = FPSGO_FREE; gpu_boost_enable_perf = gpu_boost_enable_camera = -1; init_fpsgo_common(); fbt_cpu_init(); mtk_fstb_init(); fpsgo_composer_init(); fpsgo_switch_enable(1); cpufreq_notifier_fp = fpsgo_notify_cpufreq; fpsgo_notify_vsync_fp = fpsgo_notify_vsync; fpsgo_get_fps_fp = fpsgo_get_fps; fpsgo_notify_qudeq_fp = fpsgo_notify_qudeq; fpsgo_notify_connect_fp = fpsgo_notify_connect; fpsgo_notify_bqid_fp = fpsgo_notify_bqid; #ifdef CONFIG_DRM_MEDIATEK drm_register_fps_chg_callback(dfrc_fps_limit_cb); #else #if API_READY disp_register_fps_chg_callback(dfrc_fps_limit_cb); #endif #endif return 0; } module_init(fpsgo_init); module_exit(fpsgo_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("MediaTek FPSGO"); MODULE_AUTHOR("MediaTek Inc.");