593 lines
14 KiB
C
593 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
#include "perf_ioctl.h"
|
|
|
|
|
|
#define TAG "PERF_IOCTL"
|
|
|
|
void (*fpsgo_notify_qudeq_fp)(int qudeq,
|
|
unsigned int startend,
|
|
int pid, unsigned long long identifier);
|
|
EXPORT_SYMBOL_GPL(fpsgo_notify_qudeq_fp);
|
|
void (*fpsgo_notify_connect_fp)(int pid,
|
|
int connectedAPI, unsigned long long identifier);
|
|
EXPORT_SYMBOL_GPL(fpsgo_notify_connect_fp);
|
|
void (*fpsgo_notify_bqid_fp)(int pid, unsigned long long bufID,
|
|
int queue_SF, unsigned long long identifier, int create);
|
|
EXPORT_SYMBOL_GPL(fpsgo_notify_bqid_fp);
|
|
void (*fpsgo_notify_vsync_fp)(void);
|
|
EXPORT_SYMBOL_GPL(fpsgo_notify_vsync_fp);
|
|
void (*fpsgo_get_fps_fp)(int *pid, int *fps);
|
|
EXPORT_SYMBOL_GPL(fpsgo_get_fps_fp);
|
|
void (*fpsgo_get_cmd_fp)(int *cmd, int *value1, int *value2);
|
|
EXPORT_SYMBOL_GPL(fpsgo_get_cmd_fp);
|
|
void (*gbe_get_cmd_fp)(int *cmd, int *value1, int *value2);
|
|
EXPORT_SYMBOL_GPL(gbe_get_cmd_fp);
|
|
int (*fpsgo_get_fstb_active_fp)(long long time_diff);
|
|
EXPORT_SYMBOL_GPL(fpsgo_get_fstb_active_fp);
|
|
int (*fpsgo_wait_fstb_active_fp)(void);
|
|
EXPORT_SYMBOL_GPL(fpsgo_wait_fstb_active_fp);
|
|
void (*fpsgo_notify_swap_buffer_fp)(int pid);
|
|
EXPORT_SYMBOL_GPL(fpsgo_notify_swap_buffer_fp);
|
|
|
|
void (*fpsgo_notify_sbe_rescue_fp)(int pid, int start, int enhance, unsigned long long frameID);
|
|
EXPORT_SYMBOL_GPL(fpsgo_notify_sbe_rescue_fp);
|
|
|
|
struct proc_dir_entry *perfmgr_root;
|
|
EXPORT_SYMBOL(perfmgr_root);
|
|
|
|
static unsigned long perfctl_copy_from_user(void *pvTo,
|
|
const void __user *pvFrom, unsigned long ulBytes)
|
|
{
|
|
if (access_ok(pvFrom, ulBytes))
|
|
return __copy_from_user(pvTo, pvFrom, ulBytes);
|
|
|
|
return ulBytes;
|
|
}
|
|
|
|
static unsigned long perfctl_copy_to_user(void __user *pvTo,
|
|
const void *pvFrom, unsigned long ulBytes)
|
|
{
|
|
if (access_ok(pvTo, ulBytes))
|
|
return __copy_to_user(pvTo, pvFrom, ulBytes);
|
|
|
|
return ulBytes;
|
|
}
|
|
|
|
/*--------------------SYNC------------------------*/
|
|
/* XGFF BOOST*/
|
|
int (*xgff_boost_startend_fp)(unsigned int startend,
|
|
int group_id,
|
|
int *dep_list,
|
|
int dep_list_num,
|
|
int prefer_cluster,
|
|
unsigned long long target_time,
|
|
int *param);
|
|
EXPORT_SYMBOL(xgff_boost_startend_fp);
|
|
static int xgff_boost_show(struct seq_file *m, void *v)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int xgff_boost_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, xgff_boost_show, inode->i_private);
|
|
}
|
|
|
|
static long xgff_boost_ioctl(struct file *filp,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
ssize_t ret = 0;
|
|
int *dep_list;
|
|
int dep_list_num;
|
|
int *param;
|
|
struct _XGFFRAME_BOOST_PACKAGE *msgKM = NULL,
|
|
*msgUM = (struct _XGFFRAME_BOOST_PACKAGE *)arg;
|
|
struct _XGFFRAME_BOOST_PACKAGE smsgKM;
|
|
|
|
|
|
msgKM = &smsgKM;
|
|
|
|
if (perfctl_copy_from_user(msgKM, msgUM,
|
|
sizeof(struct _XGFFRAME_BOOST_PACKAGE))) {
|
|
ret = -EFAULT;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case XGFFRAME_BOOST_START:
|
|
if (!xgff_boost_startend_fp) {
|
|
ret = -EAGAIN;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
if (msgKM->dep_list_num <= 0 || !msgKM->dep_list) {
|
|
ret = -EINVAL;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
dep_list_num = msgKM->dep_list_num;
|
|
|
|
dep_list = kcalloc(dep_list_num, sizeof(int), GFP_KERNEL);
|
|
if (!dep_list) {
|
|
ret = -ENOMEM;
|
|
goto ret_ioctl;
|
|
}
|
|
param = kzalloc(12 * sizeof(int), GFP_KERNEL);
|
|
if (!param) {
|
|
kfree(dep_list);
|
|
ret = -ENOMEM;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
if (perfctl_copy_from_user(dep_list, msgKM->dep_list,
|
|
dep_list_num * sizeof(__u32))) {
|
|
kfree(param);
|
|
kfree(dep_list);
|
|
ret = -EFAULT;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
param[0] = msgKM->param.rescue_pct_1;
|
|
param[1] = msgKM->param.rescue_f_1;
|
|
param[2] = msgKM->param.rescue_pct_2;
|
|
param[3] = msgKM->param.rescue_f_2;
|
|
param[4] = msgKM->param.gcc_std_filter;
|
|
param[5] = msgKM->param.gcc_history_window;
|
|
param[6] = msgKM->param.gcc_up_check;
|
|
param[7] = msgKM->param.gcc_up_thrs;
|
|
param[8] = msgKM->param.gcc_up_step;
|
|
param[9] = msgKM->param.gcc_down_check;
|
|
param[10] = msgKM->param.gcc_down_thrs;
|
|
param[11] = msgKM->param.gcc_down_step;
|
|
|
|
ret = xgff_boost_startend_fp(1, msgKM->group_id,
|
|
dep_list, dep_list_num, msgKM->prefer_cluster, msgKM->target_time, param);
|
|
|
|
kfree(dep_list);
|
|
kfree(param);
|
|
|
|
break;
|
|
case XGFFRAME_BOOST_END:
|
|
if (!xgff_boost_startend_fp) {
|
|
ret = -EAGAIN;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
ret = xgff_boost_startend_fp(0, msgKM->group_id, NULL, 0, msgKM->prefer_cluster,
|
|
msgKM->target_time, NULL);
|
|
|
|
break;
|
|
|
|
default:
|
|
pr_debug(TAG "%s %d: unknown cmd %x\n",
|
|
__FILE__, __LINE__, cmd);
|
|
ret = -1;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
ret_ioctl:
|
|
return ret;
|
|
}
|
|
|
|
static long xgff_boost_compat_ioctl(struct file *filp,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static const struct proc_ops xgff_boost_Fops = {
|
|
.proc_ioctl = xgff_boost_ioctl,
|
|
.proc_compat_ioctl = xgff_boost_compat_ioctl,
|
|
.proc_open = xgff_boost_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
};
|
|
|
|
|
|
/*--------------------XGFFRAME------------------------*/
|
|
int (*xgff_frame_startend_fp)(unsigned int startend,
|
|
unsigned int tid,
|
|
unsigned long long queueid,
|
|
unsigned long long frameid,
|
|
unsigned long long *cputime,
|
|
unsigned int *area,
|
|
unsigned int *pdeplistsize,
|
|
unsigned int *pdeplist);
|
|
EXPORT_SYMBOL(xgff_frame_startend_fp);
|
|
void (*xgff_frame_getdeplist_maxsize_fp)(
|
|
unsigned int *pdeplistsize);
|
|
EXPORT_SYMBOL(xgff_frame_getdeplist_maxsize_fp);
|
|
void (*xgff_frame_min_cap_fp)(unsigned int min_cap);
|
|
EXPORT_SYMBOL(xgff_frame_min_cap_fp);
|
|
|
|
static int xgff_show(struct seq_file *m, void *v)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int xgff_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, xgff_show, inode->i_private);
|
|
}
|
|
|
|
static long xgff_ioctl_impl(struct file *filp,
|
|
unsigned int cmd, unsigned long arg, void *pKM)
|
|
{
|
|
ssize_t ret = 0;
|
|
struct _XGFFRAME_PACKAGE *msgKM = NULL,
|
|
*msgUM = (struct _XGFFRAME_PACKAGE *)arg;
|
|
struct _XGFFRAME_PACKAGE smsgKM;
|
|
|
|
__u32 *vpdeplist = NULL;
|
|
unsigned int maxsize_deplist = 0;
|
|
|
|
msgKM = (struct _XGFFRAME_PACKAGE *)pKM;
|
|
if (!msgKM) {
|
|
msgKM = &smsgKM;
|
|
if (perfctl_copy_from_user(msgKM, msgUM,
|
|
sizeof(struct _XGFFRAME_PACKAGE))) {
|
|
ret = -EFAULT;
|
|
goto ret_ioctl;
|
|
}
|
|
}
|
|
|
|
switch (cmd) {
|
|
case XGFFRAME_START:
|
|
if (!xgff_frame_startend_fp || !xgff_frame_getdeplist_maxsize_fp) {
|
|
ret = -EAGAIN;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
xgff_frame_getdeplist_maxsize_fp(&maxsize_deplist);
|
|
vpdeplist = kcalloc(msgKM->deplist_size, sizeof(__u32), GFP_KERNEL);
|
|
if (!vpdeplist) {
|
|
ret = -ENOMEM;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
if (perfctl_copy_from_user(vpdeplist,
|
|
msgKM->deplist, msgKM->deplist_size * sizeof(__s32))) {
|
|
kfree(vpdeplist);
|
|
ret = -EFAULT;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
if (msgKM->deplist_size > maxsize_deplist)
|
|
msgKM->deplist_size = maxsize_deplist;
|
|
ret = xgff_frame_startend_fp(1, msgKM->tid, msgKM->queueid,
|
|
msgKM->frameid, NULL, NULL, &msgKM->deplist_size, vpdeplist);
|
|
|
|
perfctl_copy_to_user(msgUM, msgKM, sizeof(struct _XGFFRAME_PACKAGE));
|
|
|
|
kfree(vpdeplist);
|
|
break;
|
|
case XGFFRAME_END:
|
|
if (!xgff_frame_startend_fp || !xgff_frame_getdeplist_maxsize_fp) {
|
|
ret = -EAGAIN;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
xgff_frame_getdeplist_maxsize_fp(&maxsize_deplist);
|
|
vpdeplist = kcalloc(msgKM->deplist_size, sizeof(__u32), GFP_KERNEL);
|
|
|
|
if (!vpdeplist) {
|
|
ret = -ENOMEM;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
if (perfctl_copy_from_user(vpdeplist,
|
|
msgKM->deplist, msgKM->deplist_size * sizeof(__s32))) {
|
|
kfree(vpdeplist);
|
|
ret = -EFAULT;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
if (msgKM->deplist_size > maxsize_deplist)
|
|
msgKM->deplist_size = maxsize_deplist;
|
|
|
|
ret = xgff_frame_startend_fp(0, msgKM->tid, msgKM->queueid,
|
|
msgKM->frameid, &msgKM->cputime, &msgKM->area,
|
|
&msgKM->deplist_size, vpdeplist);
|
|
|
|
perfctl_copy_to_user(msgUM, msgKM, sizeof(struct _XGFFRAME_PACKAGE));
|
|
|
|
|
|
kfree(vpdeplist);
|
|
|
|
break;
|
|
|
|
case XGFFRAME_MIN_CAP:
|
|
if (!xgff_frame_min_cap_fp) {
|
|
ret = -EAGAIN;
|
|
goto ret_ioctl;
|
|
}
|
|
xgff_frame_min_cap_fp((unsigned int)msgKM->min_cap);
|
|
break;
|
|
|
|
default:
|
|
pr_debug(TAG "%s %d: unknown cmd %x\n",
|
|
__FILE__, __LINE__, cmd);
|
|
ret = -1;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
ret_ioctl:
|
|
return ret;
|
|
}
|
|
|
|
static long xgff_ioctl(struct file *filp,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
return xgff_ioctl_impl(filp, cmd, arg, NULL);
|
|
}
|
|
|
|
static long xgff_compat_ioctl(struct file *filp,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
int ret = -EFAULT;
|
|
struct _XGFFRAME_PACKAGE_32 {
|
|
__u32 tid;
|
|
__u64 queueid;
|
|
__u64 frameid;
|
|
|
|
__u64 cputime;
|
|
__u32 area;
|
|
__u32 deplist_size;
|
|
|
|
union {
|
|
__u32 *deplist;
|
|
__u64 p_dummy_deplist;
|
|
};
|
|
};
|
|
|
|
struct _XGFFRAME_PACKAGE sEaraPackageKM64;
|
|
struct _XGFFRAME_PACKAGE_32 sEaraPackageKM32;
|
|
struct _XGFFRAME_PACKAGE_32 *psEaraPackageKM32 = &sEaraPackageKM32;
|
|
struct _XGFFRAME_PACKAGE_32 *psEaraPackageUM32 =
|
|
(struct _XGFFRAME_PACKAGE_32 *)arg;
|
|
|
|
if (perfctl_copy_from_user(psEaraPackageKM32,
|
|
psEaraPackageUM32, sizeof(struct _XGFFRAME_PACKAGE_32)))
|
|
goto unlock_and_return;
|
|
|
|
sEaraPackageKM64 = *((struct _XGFFRAME_PACKAGE *)psEaraPackageKM32);
|
|
sEaraPackageKM64.deplist =
|
|
(void *)((size_t) psEaraPackageKM32->deplist);
|
|
|
|
ret = xgff_ioctl_impl(filp, cmd, arg, &sEaraPackageKM64);
|
|
|
|
unlock_and_return:
|
|
return ret;
|
|
}
|
|
|
|
static const struct proc_ops xgff_Fops = {
|
|
.proc_ioctl = xgff_ioctl,
|
|
.proc_compat_ioctl = xgff_compat_ioctl,
|
|
.proc_open = xgff_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
};
|
|
|
|
/*--------------------INIT------------------------*/
|
|
|
|
static int device_show(struct seq_file *m, void *v)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int device_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, device_show, inode->i_private);
|
|
}
|
|
|
|
static long device_ioctl(struct file *filp,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
ssize_t ret = 0;
|
|
int pwr_cmd = -1, value1 = -1, value2 = -1, pwr_pid = -1, pwr_fps = -1;
|
|
struct _FPSGO_PACKAGE *msgKM = NULL,
|
|
*msgUM = (struct _FPSGO_PACKAGE *)arg;
|
|
struct _FPSGO_PACKAGE smsgKM;
|
|
|
|
msgKM = &smsgKM;
|
|
|
|
if (perfctl_copy_from_user(msgKM, msgUM,
|
|
sizeof(struct _FPSGO_PACKAGE))) {
|
|
ret = -EFAULT;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
switch (cmd) {
|
|
#if defined(CONFIG_MTK_FPSGO_V3)
|
|
case FPSGO_QUEUE:
|
|
if (fpsgo_notify_qudeq_fp)
|
|
fpsgo_notify_qudeq_fp(1,
|
|
msgKM->start, msgKM->tid,
|
|
msgKM->identifier);
|
|
break;
|
|
case FPSGO_DEQUEUE:
|
|
if (fpsgo_notify_qudeq_fp)
|
|
fpsgo_notify_qudeq_fp(0,
|
|
msgKM->start, msgKM->tid,
|
|
msgKM->identifier);
|
|
break;
|
|
case FPSGO_QUEUE_CONNECT:
|
|
if (fpsgo_notify_connect_fp)
|
|
fpsgo_notify_connect_fp(msgKM->tid,
|
|
msgKM->connectedAPI, msgKM->identifier);
|
|
break;
|
|
case FPSGO_BQID:
|
|
if (fpsgo_notify_bqid_fp)
|
|
fpsgo_notify_bqid_fp(msgKM->tid, msgKM->bufID,
|
|
msgKM->queue_SF, msgKM->identifier,
|
|
msgKM->start);
|
|
break;
|
|
case FPSGO_TOUCH:
|
|
break;
|
|
case FPSGO_VSYNC:
|
|
if (fpsgo_notify_vsync_fp)
|
|
fpsgo_notify_vsync_fp();
|
|
break;
|
|
case FPSGO_SWAP_BUFFER:
|
|
if (fpsgo_notify_swap_buffer_fp)
|
|
fpsgo_notify_swap_buffer_fp(msgKM->tid);
|
|
break;
|
|
case FPSGO_GET_FPS:
|
|
if (fpsgo_get_fps_fp) {
|
|
fpsgo_get_fps_fp(&pwr_pid, &pwr_fps);
|
|
msgKM->tid = pwr_pid;
|
|
msgKM->value1 = pwr_fps;
|
|
} else
|
|
ret = -1;
|
|
perfctl_copy_to_user(msgUM, msgKM,
|
|
sizeof(struct _FPSGO_PACKAGE));
|
|
break;
|
|
case FPSGO_GET_CMD:
|
|
if (fpsgo_get_cmd_fp) {
|
|
fpsgo_get_cmd_fp(&pwr_cmd, &value1, &value2);
|
|
msgKM->cmd = pwr_cmd;
|
|
msgKM->value1 = value1;
|
|
msgKM->value2 = value2;
|
|
} else
|
|
ret = -1;
|
|
perfctl_copy_to_user(msgUM, msgKM,
|
|
sizeof(struct _FPSGO_PACKAGE));
|
|
break;
|
|
case FPSGO_GBE_GET_CMD:
|
|
if (gbe_get_cmd_fp) {
|
|
gbe_get_cmd_fp(&pwr_cmd, &value1, &value2);
|
|
msgKM->cmd = pwr_cmd;
|
|
msgKM->value1 = value1;
|
|
msgKM->value2 = value2;
|
|
} else
|
|
ret = -1;
|
|
perfctl_copy_to_user(msgUM, msgKM,
|
|
sizeof(struct _FPSGO_PACKAGE));
|
|
break;
|
|
case FPSGO_GET_FSTB_ACTIVE:
|
|
if (fpsgo_get_fstb_active_fp)
|
|
msgKM->active = fpsgo_get_fstb_active_fp(msgKM->time_diff);
|
|
else
|
|
ret = 0;
|
|
perfctl_copy_to_user(msgUM, msgKM,
|
|
sizeof(struct _FPSGO_PACKAGE));
|
|
break;
|
|
case FPSGO_WAIT_FSTB_ACTIVE:
|
|
if (fpsgo_wait_fstb_active_fp)
|
|
fpsgo_wait_fstb_active_fp();
|
|
break;
|
|
case FPSGO_SBE_RESCUE:
|
|
if (fpsgo_notify_sbe_rescue_fp)
|
|
fpsgo_notify_sbe_rescue_fp(msgKM->tid, msgKM->start, msgKM->value2,
|
|
msgKM->frame_id);
|
|
break;
|
|
|
|
#else
|
|
case FPSGO_TOUCH:
|
|
[[fallthrough]];
|
|
case FPSGO_QUEUE:
|
|
[[fallthrough]];
|
|
case FPSGO_DEQUEUE:
|
|
[[fallthrough]];
|
|
case FPSGO_QUEUE_CONNECT:
|
|
[[fallthrough]];
|
|
case FPSGO_VSYNC:
|
|
[[fallthrough]];
|
|
case FPSGO_BQID:
|
|
[[fallthrough]];
|
|
case FPSGO_SWAP_BUFFER:
|
|
[[fallthrough]];
|
|
case FPSGO_GET_FPS:
|
|
[[fallthrough]];
|
|
case FPSGO_GET_CMD:
|
|
[[fallthrough]];
|
|
case FPSGO_GBE_GET_CMD:
|
|
[[fallthrough]];
|
|
case FPSGO_GET_FSTB_ACTIVE:
|
|
[[fallthrough]];
|
|
case FPSGO_WAIT_FSTB_ACTIVE:
|
|
[[fallthrough]];
|
|
case FPSGO_SBE_RESCUE:
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
pr_debug(TAG "%s %d: unknown cmd %x\n",
|
|
__FILE__, __LINE__, cmd);
|
|
ret = -1;
|
|
goto ret_ioctl;
|
|
}
|
|
|
|
ret_ioctl:
|
|
return ret;
|
|
}
|
|
|
|
static const struct proc_ops Fops = {
|
|
.proc_compat_ioctl = device_ioctl,
|
|
.proc_ioctl = device_ioctl,
|
|
.proc_open = device_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
};
|
|
|
|
/*--------------------INIT------------------------*/
|
|
static void __exit exit_perfctl(void) {}
|
|
//static int __init init_perfctl(struct proc_dir_entry *parent)
|
|
static int __init init_perfctl(void)
|
|
{
|
|
struct proc_dir_entry *pe, *parent;
|
|
int ret_val = 0;
|
|
|
|
|
|
pr_debug(TAG"Start to init perf_ioctl driver\n");
|
|
|
|
parent = proc_mkdir("perfmgr", NULL);
|
|
perfmgr_root = parent;
|
|
|
|
pe = proc_create("perf_ioctl", 0664, parent, &Fops);
|
|
if (!pe) {
|
|
pr_debug(TAG"%s failed with %d\n",
|
|
"Creating file node ",
|
|
ret_val);
|
|
ret_val = -ENOMEM;
|
|
goto out_wq;
|
|
}
|
|
|
|
pe = proc_create("xgff_ioctl", 0664, parent, &xgff_Fops);
|
|
if (!pe) {
|
|
pr_debug(TAG"%s failed with %d\n",
|
|
"Creating file node ",
|
|
ret_val);
|
|
ret_val = -ENOMEM;
|
|
goto out_wq;
|
|
}
|
|
|
|
pe = proc_create("xgff_boost_ioctl", 0664, parent, &xgff_boost_Fops);
|
|
if (!pe) {
|
|
pr_debug(TAG"%s failed with %d\n",
|
|
"Creating file node ",
|
|
ret_val);
|
|
ret_val = -ENOMEM;
|
|
goto out_wq;
|
|
}
|
|
|
|
pr_debug(TAG"init perf_ioctl driver done\n");
|
|
|
|
return 0;
|
|
|
|
out_wq:
|
|
return ret_val;
|
|
}
|
|
|
|
module_init(init_perfctl);
|
|
module_exit(exit_perfctl);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("MediaTek FPSGO perf_ioctl");
|
|
MODULE_AUTHOR("MediaTek Inc.");
|