blob: 4e428ef47269cbfe00ec7c49a8f3aea2f43d37f5 [file] [log] [blame]
#include <linux/input/input_booster.h>
#include <linux/random.h>
#include <linux/spinlock.h>
#include <linux/syscalls.h>
spinlock_t write_ib_lock;
spinlock_t write_qos_lock;
spinlock_t ib_type_lock;
struct mutex trigger_ib_lock;
struct workqueue_struct *ev_unbound_wq;
struct workqueue_struct *ib_unbound_highwq;
int total_ib_cnt = 0;
int ib_init_succeed = 0;
int level_value = IB_MAX;
unsigned int debug_flag = 0;
unsigned int enable_event_booster = INIT_ZERO;
// Input Booster Init Variables
int release_val[MAX_RES_COUNT];
int device_count = 0;
struct t_ib_device_tree *ib_device_trees;
struct t_ib_trigger *ib_trigger;
int max_resource_size;
struct list_head *ib_list;
struct list_head *qos_list;
// @evdev_mt_slot : save the number of inputed touch slot.
int evdev_mt_slot = 0;
// @evdev_mt_event[] : save count of each boooter's events.
int evdev_mt_event[MAX_DEVICE_TYPE_NUM];
int trigger_cnt = 0;
int send_ev_enable = 0;
struct t_ib_info *find_release_ib(int dev_type, int key_id);
struct t_ib_info *create_ib_instance(struct t_ib_trigger *p_IbTrigger, int uniqId);
bool is_validate_uniqid(int dev_type, unsigned int uniq_id);
struct t_ib_target *find_update_target(int uniq_id, int res_id);
unsigned long get_qos_value(int res_id);
void remove_ib_instance(struct t_ib_info *ib);
void trigger_input_booster(struct work_struct *work)
{
unsigned int uniq_id = 0;
int res_type = -1;
struct t_ib_info *ib;
struct t_ib_trigger *p_IbTrigger = container_of(work, struct t_ib_trigger, ib_trigger_work);
if (p_IbTrigger == NULL) {
return;
}
pr_booster("IB Trigger :: %s(%d) %s || key_id : %d\n =========================",
ib_device_trees[p_IbTrigger->dev_type].label, p_IbTrigger->dev_type,
(p_IbTrigger->event_type) ? "PRESS" : "RELEASE", p_IbTrigger->key_id);
mutex_lock(&trigger_ib_lock);
// Input booster On/Off handling
if (p_IbTrigger->event_type == BOOSTER_ON) {
if (find_release_ib(p_IbTrigger->dev_type, p_IbTrigger->key_id) != NULL) {
pr_err(ITAG" IB Trigger :: ib already exist. Key(%d)", p_IbTrigger->key_id);
mutex_unlock(&trigger_ib_lock);
return;
}
// Check if uniqId exits.
do {
uniq_id = total_ib_cnt++;
if (total_ib_cnt == MAX_IB_COUNT)
total_ib_cnt = 0;
} while (!is_validate_uniqid(p_IbTrigger->dev_type, uniq_id));
// Make ib instance with all needed factor.
ib = create_ib_instance(p_IbTrigger, uniq_id);
pr_booster("IB Uniq Id(%d)", uniq_id);
if (ib == NULL) {
mutex_unlock(&trigger_ib_lock);
return;
}
ib->press_flag = FLAG_ON;
// When create ib instance, insert resource info in qos list with value 0.
for (res_type = 0; res_type < max_resource_size; res_type++) {
if (ib != NULL && ib->ib_dt->res[res_type].head_value != 0) {
struct t_ib_target* tv;
tv = kmalloc(sizeof(struct t_ib_target), GFP_KERNEL);
if (tv == NULL)
continue;
tv->uniq_id = ib->uniq_id;
tv->value = 0;
spin_lock(&write_qos_lock);
list_add_tail_rcu(&(tv->list), &qos_list[res_type]);
spin_unlock(&write_qos_lock);
}
}
queue_work(ib_unbound_highwq, &(ib->ib_state_work[IB_HEAD]));
} else {
/* Find ib instance in the list. if not, ignore this event.
* if exists, Release flag on. Call ib's Release func.
*/
ib = find_release_ib(p_IbTrigger->dev_type, p_IbTrigger->key_id);
if (ib == NULL) {
pr_err("IB is null on release");
mutex_unlock(&trigger_ib_lock);
return;
}
pr_booster("IB Trigger Release :: Uniq ID(%d)", ib->uniq_id);
mutex_lock(&ib->lock);
ib->rel_flag = FLAG_ON;
// If head operation is already finished, tail timeout work will be triggered.
if (ib->isHeadFinished) {
if (!delayed_work_pending(&(ib->ib_timeout_work[IB_TAIL]))) {
queue_delayed_work(ib_unbound_highwq,
&(ib->ib_timeout_work[IB_TAIL]),
msecs_to_jiffies(ib->ib_dt->tail_time));
} else {
pr_err(ITAG" IB Trigger Release :: tail timeout start");
}
}
mutex_unlock(&ib->lock);
}
mutex_unlock(&trigger_ib_lock);
}
struct t_ib_info *create_ib_instance(struct t_ib_trigger *p_IbTrigger, int uniqId)
{
struct t_ib_info *ib = kmalloc(sizeof(struct t_ib_info), GFP_KERNEL);
int dev_type = p_IbTrigger->dev_type;
if (ib == NULL)
return NULL;
ib->dev_name = p_IbTrigger->dev_name;
ib->key_id = p_IbTrigger->key_id;
ib->uniq_id = uniqId;
ib->press_flag = FLAG_OFF;
ib->rel_flag = FLAG_OFF;
ib->isHeadFinished = 0;
ib->ib_dt = &ib_device_trees[dev_type];
INIT_WORK(&ib->ib_state_work[IB_HEAD], press_state_func);
INIT_DELAYED_WORK(&ib->ib_timeout_work[IB_HEAD], press_timeout_func);
INIT_WORK(&ib->ib_state_work[IB_TAIL], release_state_func);
INIT_DELAYED_WORK(&ib->ib_timeout_work[IB_TAIL], release_timeout_func);
mutex_init(&ib->lock);
spin_lock(&write_ib_lock);
list_add_tail_rcu(&(ib->list), &ib_list[dev_type]);
spin_unlock(&write_ib_lock);
return ib;
}
bool is_validate_uniqid(int dev_type, unsigned int uniq_id)
{
int cnt = 0;
struct t_ib_info *ib = NULL;
rcu_read_lock();
if (list_empty(&ib_list[dev_type])) {
rcu_read_unlock();
pr_booster("IB list empty");
return true;
}
list_for_each_entry_rcu(ib, &ib_list[dev_type], list) {
cnt++;
if (ib != NULL && ib->uniq_id == uniq_id) {
rcu_read_unlock();
pr_booster("uniq id find :: IB Idx(%d) old(%d) new(%d)", cnt, ib->uniq_id, uniq_id);
return false;
}
}
rcu_read_unlock();
pr_booster("This can be used(IB Total:%d)", cnt);
return true;
}
struct t_ib_info *find_release_ib(int dev_type, int key_id)
{
struct t_ib_info *ib = NULL;
rcu_read_lock();
if (list_empty(&ib_list[dev_type])) {
rcu_read_unlock();
pr_booster("Release IB(%d) Not Exist & List Empty", key_id);
return NULL;
}
list_for_each_entry_rcu(ib, &ib_list[dev_type], list) {
if (ib != NULL && ib->key_id == key_id && ib->rel_flag == FLAG_OFF) {
rcu_read_unlock();
pr_booster("Release IB(%d) Found", key_id);
return ib;
}
}
rcu_read_unlock();
pr_booster("Release IB(%d) Not Exist", key_id);
return NULL;
}
void press_state_func(struct work_struct *work)
{
struct t_ib_res_info res;
struct t_ib_target *tv;
int qos_values[MAX_RES_COUNT] = { 0, };
int res_type = 0;
struct t_ib_info *target_ib = container_of(work, struct t_ib_info, ib_state_work[IB_HEAD]);
pr_booster("Press State Func :::: Unique_Id(%d)", target_ib->uniq_id);
// //To-Do : Get_Res_List(head) and update head value.
for (res_type = 0; res_type < max_resource_size; res_type++) {
res = target_ib->ib_dt->res[res_type];
if (res.head_value == 0)
continue;
//find already added target value instance and update value as a head.
tv = find_update_target(target_ib->uniq_id, res.res_id);
if (tv == NULL) {
pr_booster("Press State Func :::: tv is null T.T");
continue;
}
tv->value = res.head_value;
pr_booster("Press State Func :::: Uniq(%d)'s Update Res(%d) Head Val(%d)",
tv->uniq_id, res.res_id, res.head_value);
qos_values[res.res_id] = get_qos_value(res.res_id);
}
ib_set_booster(qos_values);
pr_booster("Press State Func :::: Press Delay Time(%lu)",
msecs_to_jiffies(target_ib->ib_dt->head_time));
queue_delayed_work(ib_unbound_highwq, &(target_ib->ib_timeout_work[IB_HEAD]),
msecs_to_jiffies(target_ib->ib_dt->head_time));
}
void press_timeout_func(struct work_struct *work)
{
struct t_ib_info *target_ib = container_of(work, struct t_ib_info, ib_timeout_work[IB_HEAD].work);
pr_booster("Press Timeout Func :::: Unique_Id(%d)", target_ib->uniq_id);
int res_type;
struct t_ib_res_info res;
struct t_ib_target *tv;
int qos_values[MAX_RES_COUNT] = { 0, };
if (target_ib->ib_dt->tail_time != 0) {
mutex_lock(&target_ib->lock);
target_ib->isHeadFinished = 1;
queue_work(ib_unbound_highwq, &(target_ib->ib_state_work[IB_TAIL]));
mutex_unlock(&target_ib->lock);
}
else {
//NO TAIL Scenario : Delete Ib instance and free all memory space.
for (res_type = 0; res_type < max_resource_size; res_type++) {
res = target_ib->ib_dt->res[res_type];
tv = find_update_target(target_ib->uniq_id, res.res_id);
if (tv == NULL)
continue;
spin_lock(&write_qos_lock);
list_del_rcu(&(tv->list));
spin_unlock(&write_qos_lock);
synchronize_rcu();
kfree(tv);
rcu_read_lock();
if (!list_empty(&qos_list[res.res_id])) {
rcu_read_unlock();
qos_values[res.res_id] = get_qos_value(res.res_id);
pr_booster("Press Timeout ::: Remove Val Cuz No Tail ::: Res(%d) Qos Val(%d)",
res.res_id, qos_values[res.res_id]);
}
else {
rcu_read_unlock();
pr_booster("Press Timeout ::: Release Booster(%d) ::: No Tail and List Empty",
res.res_id);
ib_release_booster(res.res_id);
}
}
remove_ib_instance(target_ib);
ib_set_booster(qos_values);
}
}
void release_state_func(struct work_struct *work)
{
int qos_values[MAX_RES_COUNT] = { 0, };
int isHeadFinished = -1;
int res_type = 0;
struct t_ib_target *tv;
struct t_ib_res_info res;
struct t_ib_info *target_ib = container_of(work, struct t_ib_info, ib_state_work[IB_TAIL]);
mutex_lock(&target_ib->lock);
isHeadFinished = target_ib->isHeadFinished;
pr_booster("Release State Func :::: Unique_Id(%d) HeadFinish(%d) Rel_Flag(%d)",
target_ib->uniq_id, isHeadFinished, target_ib->rel_flag)
for (res_type = 0; res_type < max_resource_size; res_type++) {
res = target_ib->ib_dt->res[res_type];
if (res.tail_value == 0)
continue;
tv = find_update_target(target_ib->uniq_id, res.res_id);
if (tv == NULL)
continue;
spin_lock(&write_qos_lock);
tv->value = res.tail_value;
spin_unlock(&write_qos_lock);
pr_booster("Release State Func :::: Uniq(%d)'s Update Tail Val (%d), Qos_Val(%d)", tv->uniq_id, tv->value, qos_values[res.res_id]);
qos_values[res.res_id] = get_qos_value(res.res_id);
}
ib_set_booster(qos_values);
// If release event already triggered, tail delay work will be triggered after relese state func.
if (target_ib->rel_flag == FLAG_ON) {
if (!delayed_work_pending(&(target_ib->ib_timeout_work[IB_TAIL]))) {
queue_delayed_work(ib_unbound_highwq,
&(target_ib->ib_timeout_work[IB_TAIL]),
msecs_to_jiffies(target_ib->ib_dt->tail_time));
} else {
pr_err(ITAG" Release State Func :: tail timeout start");
}
}
mutex_unlock(&target_ib->lock);
}
void release_timeout_func(struct work_struct *work)
{
int qos_values[MAX_RES_COUNT] = { 0, };
struct t_ib_target *tv;
struct t_ib_res_info res;
int res_type;
struct t_ib_info *target_ib = container_of(work, struct t_ib_info, ib_timeout_work[IB_TAIL].work);
pr_booster("Release Timeout Func :::: Unique_Id(%d)", target_ib->uniq_id);
// Remove all booster
// delete instance in the ib list and delete instance in the qos list.
for (res_type = 0; res_type < max_resource_size; res_type++) {
res = target_ib->ib_dt->res[res_type];
tv = find_update_target(target_ib->uniq_id, res.res_id);
if (tv == NULL)
continue;
pr_booster("Release Timeout Func :::: Delete Uniq(%d)'s TV Val (%d)",
tv->uniq_id, tv->value);
spin_lock(&write_qos_lock);
list_del_rcu(&(tv->list));
spin_unlock(&write_qos_lock);
synchronize_rcu();
kfree(tv);
rcu_read_lock();
if (!list_empty(&qos_list[res.res_id])) {
rcu_read_unlock();
qos_values[res.res_id] = get_qos_value(res.res_id);
pr_booster("Release Timeout Func ::: Res(%d) Qos Val(%d)",
res.res_id, qos_values[res.res_id]);
}
else {
rcu_read_unlock();
pr_booster("Release Timeout ::: Release Booster(%d) ::: List Empty",
res.res_id);
ib_release_booster(res.res_id);
}
}
ib_set_booster(qos_values);
remove_ib_instance(target_ib);
}
struct t_ib_target *find_update_target(int uniq_id, int res_id)
{
struct t_ib_target *tv;
rcu_read_lock();
list_for_each_entry_rcu(tv, &qos_list[res_id], list) {
if (tv->uniq_id == uniq_id) {
rcu_read_unlock();
return tv;
}
}
rcu_read_unlock();
return NULL;
}
unsigned long get_qos_value(int res_id)
{
//Find tv instance that has max value in the qos_list that has the passed res_id.
struct t_ib_target *tv;
int ret_val = 0;
rcu_read_lock();
if (list_empty(&qos_list[res_id])) {
rcu_read_unlock();
return 0;
}
list_for_each_entry_rcu(tv, &qos_list[res_id], list) {
if (tv->value > ret_val)
ret_val = tv->value;
}
rcu_read_unlock();
return ret_val;
}
void remove_ib_instance(struct t_ib_info *ib)
{
pr_booster("Del Ib Instance's Id : %d", ib->uniq_id);
//Delete Kmalloc instances
spin_lock(&write_ib_lock);
list_del_rcu(&(ib->list));
spin_unlock(&write_ib_lock);
synchronize_rcu();
kfree(ib);
}
unsigned int create_uniq_id(int type, int code, int slot)
{
//id1 | (id2 << num_bits_id1) | (id3 << (num_bits_id2 + num_bits_id1))
pr_booster("Create Key Id -> type(%d), code(%d), slot(%d)", type, code, slot);
return (type << (TYPE_BITS + CODE_BITS)) | (code << CODE_BITS) | slot;
}
void ib_auto_test(int type, int code, int val)
{
send_ev_enable = 1;
}
//+++++++++++++++++++++++++++++++++++++++++++++++ STRUCT & VARIABLE FOR SYSFS +++++++++++++++++++++++++++++++++++++++++++++++//
SYSFS_CLASS(enable_event, (buf, "%u\n", enable_event), 1)
SYSFS_CLASS(debug_level, (buf, "%u\n", debug_level), 1)
SYSFS_CLASS(sendevent, (buf, "%d\n", sendevent), 3)
HEAD_TAIL_SYSFS_DEVICE(head)
HEAD_TAIL_SYSFS_DEVICE(tail)
LEVEL_SYSFS_DEVICE(level)
struct attribute *dvfs_attributes[] = {
&dev_attr_head.attr,
&dev_attr_tail.attr,
&dev_attr_level.attr,
NULL,
};
struct attribute_group dvfs_attr_group = {
.attrs = dvfs_attributes,
};
void init_sysfs_device(struct class *sysfs_class, struct t_ib_device_tree *ib_dt)
{
struct device *sysfs_dev;
int ret = 0;
sysfs_dev = device_create(sysfs_class, NULL, 0, ib_dt, "%s", ib_dt->label);
if (IS_ERR(sysfs_dev)) {
ret = IS_ERR(sysfs_dev);
pr_booster("[Input Booster] Failed to create %s sysfs device[%d]n", ib_dt->label, ret);
return;
}
ret = sysfs_create_group(&sysfs_dev->kobj, &dvfs_attr_group);
if (ret) {
pr_booster("[Input Booster] Failed to create %s sysfs groupn", ib_dt->label);
return;
}
}
int is_ib_init_succeed(void)
{
return (ib_trigger != NULL && ib_device_trees != NULL &&
ib_list != NULL && qos_list != NULL) ? 1 : 0;
}
void input_booster_exit(void)
{
int i = 0;
kfree(ib_trigger);
kfree(ib_device_trees);
kfree(ib_list);
kfree(qos_list);
input_booster_exit_vendor();
}
// ********** Init Booster ********** //
void input_booster_init(void)
{
// ********** Load Frequency data from DTSI **********
struct device_node *np;
int i;
int ib_dt_size = sizeof(struct t_ib_device_tree);
int ib_res_size = sizeof(struct t_ib_res_info);
int list_head_size = sizeof(struct list_head);
int ddr_info_size = sizeof(struct t_ddr_info);
int res_type = 0;
int ndevice_in_dt = 0;
char rel_val_str[100];
size_t rel_val_size = 0;
char *rel_val_pointer = NULL;
const char *token = NULL;
spin_lock_init(&write_ib_lock);
spin_lock_init(&write_qos_lock);
spin_lock_init(&ib_type_lock);
mutex_init(&trigger_ib_lock);
ev_unbound_wq =
alloc_ordered_workqueue("ev_unbound_wq", WQ_HIGHPRI);
ib_unbound_highwq =
alloc_workqueue("ib_unbound_high_wq", WQ_UNBOUND | WQ_HIGHPRI,
MAX_IB_COUNT);
if (ev_unbound_wq == NULL || ib_unbound_highwq == NULL)
goto out;
//Input Booster Trigger Strcut Init
ib_trigger = kcalloc(ABS_CNT, sizeof(struct t_ib_trigger) * MAX_IB_COUNT, GFP_KERNEL);
if (ib_trigger == NULL) {
pr_err(ITAG" ib_trigger mem alloc fail");
goto out;
}
for (i = 0; i < MAX_IB_COUNT; i++) {
INIT_WORK(&(ib_trigger[i].ib_trigger_work), trigger_input_booster);
}
np = of_find_compatible_node(NULL, NULL, "input_booster");
if (np == NULL) {
goto out;
}
// Geting the count of devices.
ndevice_in_dt = of_get_child_count(np);
pr_info(ITAG" %s ndevice_in_dt : %d\n", __func__, ndevice_in_dt);
ib_device_trees = kcalloc(ABS_CNT, ib_dt_size * ndevice_in_dt, GFP_KERNEL);
if (ib_device_trees == NULL) {
pr_err(ITAG" dt_infor mem alloc fail");
goto out;
}
// ib list mem alloc
ib_list = kcalloc(ABS_CNT, list_head_size * ndevice_in_dt, GFP_KERNEL);
if (ib_list == NULL) {
pr_err(ITAG" ib list mem alloc fail");
goto out;
}
sscanf((of_get_property(np, "max_resource_count", NULL)), "%d", &max_resource_size);
pr_info(ITAG" resource size : %d", max_resource_size);
//qos list mem alloc
qos_list = kcalloc(ABS_CNT, list_head_size * max_resource_size, GFP_KERNEL);
if (qos_list == NULL) {
pr_err(ITAG" ib list mem alloc fail");
goto out;
}
//Init Resource Release Values
const char *rel_vals = of_get_property(np, "ib_release_values", NULL);
rel_val_size = strlcpy(rel_val_str, rel_vals, sizeof(char) * 100);
rel_val_pointer = rel_val_str;
token = strsep(&rel_val_pointer, ",");
res_type = 0;
while (token != NULL) {
pr_booster("Rel %d's Type Value(%s)", res_type, token);
//Release Values inserted inside array
sscanf(token, "%d", &release_val[res_type]);
//Init Qos List
INIT_LIST_HEAD(&qos_list[res_type]);
token = strsep(&rel_val_pointer, ",");
res_type++;
}
if (res_type < max_resource_size) {
pr_err(ITAG" release value parse fail");
goto out;
}
struct device_node *cnp;
for_each_child_of_node(np, cnp) {
/************************************************/
// fill all needed data into res_info instance that is in dt instance.
struct t_ib_device_tree *ib_dt = (ib_device_trees + device_count);
struct device_node *child_resource_node;
struct device_node *resource_node = of_find_compatible_node(cnp, NULL, "resource");
ib_dt->res = kcalloc(ABS_CNT, ib_res_size * max_resource_size, GFP_KERNEL);
int resource_node_index = 0;
int res_type = 0;
for_each_child_of_node(resource_node, child_resource_node) {
// resource_node_index is same as Resource's ID.
ib_dt->res[resource_node_index].res_id = resource_node_index;
ib_dt->res[resource_node_index].label = of_get_property(child_resource_node, "resource,label", NULL);
int inputbooster_size = 0;
const u32 *is_exist_inputbooster_size = of_get_property(child_resource_node, "resource,value", &inputbooster_size);
if (is_exist_inputbooster_size && inputbooster_size) {
inputbooster_size = inputbooster_size / sizeof(u32);
}
pr_info(ITAG" inputbooster_size : %d", inputbooster_size);
if (inputbooster_size != 2)
return; // error
for (res_type = 0; res_type < inputbooster_size; ++res_type) {
if (res_type == IB_HEAD) {
of_property_read_u32_index(child_resource_node, "resource,value",
res_type, &ib_dt->res[resource_node_index].head_value);
pr_info(ITAG"res[%d]->values[%d] : %d", resource_node_index,
res_type, ib_dt->res[resource_node_index].head_value);
}
else if (res_type == IB_TAIL) {
of_property_read_u32_index(child_resource_node, "resource,value",
res_type, &ib_dt->res[resource_node_index].tail_value);
pr_info(ITAG"res[%d]->values[%d] : %d", resource_node_index,
res_type, ib_dt->res[resource_node_index].tail_value);
}
}
resource_node_index++;
}
ib_dt->label = of_get_property(cnp, "input_booster,label", NULL);
pr_info(ITAG" %s ib_dt->label : %s\n", __func__, ib_dt->label);
if (of_property_read_u32(cnp, "input_booster,type", &ib_dt->type)) {
pr_err(ITAG" Failed to get type property\n");
break;
}
if (of_property_read_u32(cnp, "input_booster,head_time", &ib_dt->head_time)) {
pr_err(ITAG" Fail Get Head Time\n");
break;
}
if (of_property_read_u32(cnp, "input_booster,tail_time", &ib_dt->tail_time)) {
pr_err(ITAG" Fail Get Tail Time\n");
break;
}
//Init all type of ib list.
INIT_LIST_HEAD(&ib_list[device_count]);
device_count++;
}
ib_init_succeed = is_ib_init_succeed();
out:
// ********** Initialize Sysfs **********
{
struct class *sysfs_class;
int ret;
int ib_type;
sysfs_class = class_create(THIS_MODULE, "input_booster");
if (IS_ERR(sysfs_class)) {
pr_err(" Failed to create class\n");
return;
}
if (ib_init_succeed) {
INIT_SYSFS_CLASS(enable_event)
INIT_SYSFS_CLASS(debug_level)
INIT_SYSFS_CLASS(sendevent)
for (ib_type = 0; ib_type < MAX_DEVICE_TYPE_NUM; ib_type++) {
init_sysfs_device(sysfs_class, &ib_device_trees[ib_type]);
}
}
}
#if !defined(CONFIG_ARCH_QCOM) && !defined (CONFIG_ARCH_EXYNOS)
pr_err(ITAG" At least, one vendor feature needed\n");
#else
input_booster_init_vendor(release_val);
#endif
}