drivers: input: Import Univesal9820 Input_booster

* Import generic and LSI booster only
* This needs adaptation to work on 9810. alot of it
This commit is contained in:
Anan Jaser 2023-03-28 03:12:15 +04:00 committed by xxmustafacooTR
parent f27b9e2a7b
commit f1476272cc
No known key found for this signature in database
GPG key ID: 520B6FE385CBF5C9
6 changed files with 1443 additions and 0 deletions

View file

@ -274,6 +274,15 @@ config SEC_AUTO_INPUT
To compile this driver as a module, choose M here: the
module will be called autoinput.
config SEC_INPUT_BOOSTER
bool "SEC INPUT BOOSTER"
depends on INPUT_EVDEV
default y
help
Say Y here if you need to enable Input Booster.
It's triggered in evdev module.
comment "Input Device Drivers"
source "drivers/input/keyboard/Kconfig"

View file

@ -20,6 +20,8 @@ obj-$(CONFIG_INPUT_LEDS) += input-leds.o
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
obj-$(CONFIG_SEC_INPUT_BOOSTER) += input_booster.o
obj-$(CONFIG_ARCH_EXYNOS) += input_booster_lsi.o
obj-$(CONFIG_INPUT_EVBUG) += evbug.o
obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/

View file

@ -0,0 +1,263 @@
#ifdef CONFIG_SEC_INPUT_BOOSTER
int chk_next_data(struct evdev_client *dev, int idx, int input_type)
{
int ret_val = 0;
int next_type = -1;
int next_code = -1;
int next_idx = (idx+1) & (dev->bufsize - 1);
next_type = dev->buffer[next_idx].type;
next_code = dev->buffer[next_idx].code;
switch (input_type) {
case BTN_TOUCH:
if (next_type == EV_ABS && next_code == ABS_PRESSURE)
ret_val = 1;
break;
case EV_KEY:
ret_val = 1;
break;
default:
break;
}
return ret_val;
}
int chk_boost_on_off(struct evdev_client *dev, int idx, int dev_type)
{
int ret_val = -1;
if (dev_type < 0)
return ret_val;
/* In case of SPEN or HOVER, it must be empty multi event
* Before starting input booster.
*/
if (dev_type == SPEN || dev_type == HOVER) {
if (!evdev_mt_event[dev_type] && dev->buffer[idx].value)
ret_val = 1;
else if (evdev_mt_event[dev_type] && !dev->buffer[idx].value)
ret_val = 0;
} else if (dev_type == TOUCH || dev_type == MULTI_TOUCH) {
if (dev->buffer[idx].value >= 0)
ret_val = 1;
else
ret_val = 0;
} else if (dev->buffer[idx].value > 0)
ret_val = 1;
else if (dev->buffer[idx].value <= 0)
ret_val = 0;
return ret_val;
}
/*
* get_device_type : Define type of device for input_booster.
* dev : Current device that in which input events triggered.
* keyId : Each device get given unique keyid using Type, Code, Slot values
* to identify which booster will be triggered.
* cur_idx : Pointing current handling index from input booster.
* cur_idx will be updated when cur_idx is same as head.
* head : End of events set. Input booster handles from tail event to head events.
* Must check if the event is the last one referring to head.
*/
int get_device_type(struct evdev_client *dev, unsigned int *keyId, int *cur_idx, int head)
{
int i;
int ret_val = -1;
int dev_type = NONE_TYPE_DEVICE;
int uniq_slot = 0;
int next_idx = 0 ;
int target_idx = 0;
if (dev == NULL || dev->ev_cnt > MAX_EVENTS) {
pr_err("evdev client is null and exceed max event number");
return ret_val;
}
/* Initializing device type before finding the proper device type. */
dev->device_type = dev_type;
for (i = *cur_idx; i != head; i = (i+1) & (dev->bufsize - 1)) {
pr_booster("%s Type : %d, Code : %d, Value : %d, Head : %d, idx : %d\n",
"Input Data || ", dev->buffer[i].type,
dev->buffer[i].code, dev->buffer[i].value,
head, i);
if (dev->buffer[i].type == EV_SYN || dev->buffer[i].code == SYN_REPORT) {
break;
}
if (dev->buffer[i].type == EV_KEY) {
target_idx = i;
switch (dev->buffer[i].code) {
case BTN_TOUCH:
if (!chk_next_data(dev, i, BTN_TOUCH))
break;
dev_type = SPEN;
break;
case BTN_TOOL_PEN:
dev_type = HOVER;
break;
case KEY_BACK:
case KEY_HOMEPAGE:
case KEY_RECENT:
dev_type = TOUCH_KEY;
break;
case KEY_VOLUMEUP:
case KEY_VOLUMEDOWN:
case KEY_POWER:
case KEY_WINK:
dev_type = KEY;
break;
default:
break;
}
} else if (dev->buffer[i].type == EV_ABS) {
target_idx = i;
switch (dev->buffer[i].code) {
case ABS_MT_TRACKING_ID:
if (dev->buffer[i].value >= 0) {
evdev_mt_slot++;
} else {
evdev_mt_slot--;
}
if (dev->buffer[i].value >= 0) {
if (evdev_mt_slot == 1) {
dev_type = TOUCH;
uniq_slot = 1;
} else if (evdev_mt_slot == 2) {
dev_type = MULTI_TOUCH;
uniq_slot = 2;
}
} else if (dev->buffer[i].value < 0) {
//ret_val = 0;
if (evdev_mt_slot == 0) {
dev_type = TOUCH;
uniq_slot = 1;
} else if (evdev_mt_slot == 1) {
dev_type = MULTI_TOUCH;
uniq_slot = 2;
}
}
pr_booster("Touch Booster Trigger(%d), Type(%d), Code(%d), Val(%d), head(%d), Tail(%d), uniq_slot(%d), Idx(%d), Cnt(%d)",
evdev_mt_slot, dev->buffer[i].type, dev->buffer[i].code, dev->buffer[i].value, head, dev->tail, uniq_slot, i, dev->ev_cnt);
break;
}
} else if (dev->buffer[i].type == EV_MSC &&
dev->buffer[i].code == MSC_SCAN) {
if (!chk_next_data(dev, i, EV_KEY)) {
break;
}
next_idx = (i+1) & (dev->bufsize - 1);
target_idx = next_idx;
switch (dev->buffer[next_idx].code) {
case BTN_LEFT: /* Checking Touch Button Event */
case BTN_RIGHT:
case BTN_MIDDLE:
dev_type = MOUSE;
//Remain the last of CODE value as a uniq_slot to recognize BTN Type (LEFT, RIGHT, MIDDLE)
uniq_slot = dev->buffer[next_idx].code;
break;
default: /* Checking Keyboard Event */
dev_type = KEYBOARD;
uniq_slot = dev->buffer[next_idx].code;
pr_booster("KBD Booster Trigger(%d), Type(%d), Code(%d), Val(%d), head(%d), Tail(%d), Idx(%d), Cnt(%d)\n",
dev->buffer[next_idx].code, dev->buffer[i].type,
dev->buffer[i].code, dev->buffer[i].value,
head, dev->tail, i, dev->ev_cnt);
break;
}
}
if (dev_type != NONE_TYPE_DEVICE) {
*keyId = create_uniq_id(dev->buffer[i].type, dev->buffer[i].code, uniq_slot);
ret_val = chk_boost_on_off(dev, target_idx, dev_type);
pr_booster("Dev type Find(%d), KeyID(%d), enable(%d), Target(%d)\n",
dev_type, *keyId, ret_val, target_idx);
break;
}
}
// if for loop reach the end, cur_idx is set as head value or pointing next one with plus one.
// Especially, dev->bufsize has to be set as a 2^n.
// In the code that set bufzise, we can see bufsize would be set using roundup_pow_of_two function.
// return roundup_pow_of_two(n_events);
// which means "A power of two is a number of the form 2n where n is an integer"
*cur_idx = (i == head) ? head : ((i+1) & (dev->bufsize - 1));
dev->device_type = dev_type;
return ret_val;
}
// ********** Detect Events ********** //
void input_booster(struct evdev_client *dev, int dev_head)
{
int dev_type = 0;
int keyId = 0;
unsigned int uniqId = 0;
int res_type = 0;
int cnt = 0;
int cur_idx = -1;
int head = 0;
if (dev == NULL) {
pr_err(ITAG"dev is Null");
return;
}
if (!ib_init_succeed || dev->ev_cnt == 0) {
pr_err(ITAG"ev_cnt(%d) dt_infor hasn't mem alloc", dev->ev_cnt);
return;
}
head = dev_head;
cur_idx = (head - dev->ev_cnt) & (dev->bufsize - 1);
while (cur_idx != head) {
keyId = 0;
int enable = get_device_type(dev, &keyId, &cur_idx, head);
if (enable < 0 || keyId == 0) {
continue;
}
dev_type = dev->device_type;
if (dev_type <= NONE_TYPE_DEVICE || dev_type >= MAX_DEVICE_TYPE_NUM) {
continue;
}
if (enable == BOOSTER_ON) {
evdev_mt_event[dev_type]++;
} else {
evdev_mt_event[dev_type]--;
}
if (cnt == 0 && dev->evdev->handle.dev != NULL) {
while (dev->evdev->handle.dev->name[cnt] != '\0') {
ib_trigger[trigger_cnt].dev_name[cnt] = dev->evdev->handle.dev->name[cnt];
cnt++;
}
ib_trigger[trigger_cnt].dev_name[cnt] = '\0';
}
pr_booster("Dev Name : %s(%d), Key Id(%d), IB_Cnt(%d)", ib_trigger[trigger_cnt].dev_name, dev_type, keyId, trigger_cnt);
ib_trigger[trigger_cnt].key_id = keyId;
ib_trigger[trigger_cnt].event_type = enable;
ib_trigger[trigger_cnt].dev_type = dev_type;
queue_work(ev_unbound_wq, &(ib_trigger[trigger_cnt++].ib_trigger_work));
trigger_cnt = (trigger_cnt == MAX_IB_COUNT) ? 0 : trigger_cnt;
}
}
#endif //--CONFIG_SEC_INPUT_BOOSTER

View file

@ -0,0 +1,745 @@
#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
}

View file

@ -0,0 +1,124 @@
#include <linux/input/input_booster.h>
#include <linux/exynos-ucc.h>
#include <linux/ems_service.h>
static struct pm_qos_request cluster2_qos;
static struct pm_qos_request cluster1_qos;
static struct pm_qos_request cluster0_qos;
static struct pm_qos_request mif_qos;
static struct pm_qos_request int_qos;
static struct kpp kpp_ta;
static struct kpp kpp_fg;
static struct ucc_req ucc_req = {
.name = "input",
};
static DEFINE_MUTEX(input_lock);
bool current_hmp_boost = INIT_ZERO;
bool current_ucc_boost = INIT_ZERO;
struct inform {
void *qos;
void (*set_func)(int);
int release_value;
};
struct inform informations[MAX_RES_COUNT];
void set_ucc(int enable)
{
mutex_lock(&input_lock);
if (enable != current_ucc_boost) {
pr_booster("[Input Booster2] ****** set_ucc : %d ( %s )\n", enable, __FUNCTION__);
if (enable) {
ucc_add_request(&ucc_req, enable);
} else {
ucc_remove_request(&ucc_req);
}
current_ucc_boost = enable;
}
mutex_unlock(&input_lock);
}
void set_hmp(int enable)
{
mutex_lock(&input_lock);
if (enable != current_hmp_boost) {
pr_booster("[Input Booster2] ****** set_ehmp : %d ( %s )\n", enable, __FUNCTION__);
if (enable) {
kpp_request(STUNE_TOPAPP, &kpp_ta, enable);
kpp_request(STUNE_FOREGROUND, &kpp_fg, enable);
} else {
kpp_request(STUNE_TOPAPP, &kpp_ta, 0);
kpp_request(STUNE_FOREGROUND, &kpp_fg, 0);
}
current_hmp_boost = enable;
}
mutex_unlock(&input_lock);
}
void ib_set_booster(int *qos_values)
{
int value = -1;
int res_type = 0;
for (res_type = 0; res_type < MAX_RES_COUNT; res_type++) {
pr_info(" ib_set_booster qos_values[%d] : %d", res_type, qos_values[res_type]);
value = qos_values[res_type];
if (value <= 0)
continue;
if (informations[res_type].qos) {
pm_qos_update_request(informations[res_type].qos, qos_values[res_type]);
} else {
informations[res_type].set_func(qos_values[res_type]);
}
}
}
void ib_release_booster(int res_id)
{
if (informations[res_id].qos) {
pm_qos_update_request(informations[res_id].qos, informations[res_id].release_value);
} else {
informations[res_id].set_func(informations[res_id].release_value);
}
pr_info("ib_release_booster %d value : %d", res_id, informations[res_id].release_value);
}
void input_booster_init_vendor(int *release_val)
{
int i = 0;
pm_qos_add_request(&cluster2_qos, PM_QOS_CLUSTER2_FREQ_MIN, PM_QOS_CLUSTER2_FREQ_MIN_DEFAULT_VALUE);
pm_qos_add_request(&cluster1_qos, PM_QOS_CLUSTER1_FREQ_MIN, PM_QOS_CLUSTER1_FREQ_MIN_DEFAULT_VALUE);
pm_qos_add_request(&cluster0_qos, PM_QOS_CLUSTER0_FREQ_MIN, PM_QOS_CLUSTER0_FREQ_MIN_DEFAULT_VALUE);
pm_qos_add_request(&mif_qos, PM_QOS_BUS_THROUGHPUT, PM_QOS_BUS_THROUGHPUT_DEFAULT_VALUE);
pm_qos_add_request(&int_qos, PM_QOS_DEVICE_THROUGHPUT, PM_QOS_DEVICE_THROUGHPUT_DEFAULT_VALUE);
informations[i++].qos = &cluster2_qos;
informations[i++].qos = &cluster1_qos;
informations[i++].qos = &cluster0_qos;
informations[i++].qos = &mif_qos;
informations[i++].qos = &int_qos;
informations[i++].set_func = set_hmp;
informations[i++].set_func = set_ucc;
for (i = 0; i < MAX_RES_COUNT; i++) {
informations[i].release_value = release_val[i];
}
}
void input_booster_exit_vendor(void)
{
pm_qos_remove_request(&cluster2_qos);
pm_qos_remove_request(&cluster1_qos);
pm_qos_remove_request(&cluster0_qos);
pm_qos_remove_request(&mif_qos);
pm_qos_remove_request(&int_qos);
}

View file

@ -0,0 +1,300 @@
#if !defined(CONFIG_INPUT_BOOSTER) // Input Booster +
#ifndef _INPUT_BOOSTER_H_
#define _INPUT_BOOSTER_H_
#include <linux/pm_qos.h>
#include <linux/of.h>
#include <linux/cpufreq.h>
#include <linux/kernel.h>
#include <linux/workqueue.h>
#include <linux/input.h>
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
#ifdef CONFIG_SCHED_HMP
#define USE_HMP_BOOST
#endif
#define ITAG " [Input Booster] "
#define pr_booster(format, ...) { \
if (debug_flag) \
printk(ITAG format, ## __VA_ARGS__); \
}
#define MAX_MULTI_TOUCH_EVENTS 10
#define MAX_IB_COUNT 100
#define MAX_EVENTS (MAX_MULTI_TOUCH_EVENTS * 10)
#define INPUT_BOOSTER_NULL -1
#define INIT_ZERO 0
#define DEFAULT_LEVEL 0
#define INPUT_LEVEL 2
//+++++++++++++++++++++++++++++++++++++++++++++++ STRUCT & VARIABLE FOR SYSFS +++++++++++++++++++++++++++++++++++++++++++++++//
#define SYSFS_CLASS(_ATTR_, _ARGU_, _COUNT_) \
ssize_t input_booster_sysfs_class_show_##_ATTR_(struct class *dev, struct class_attribute *attr, char *buf) \
{ \
ssize_t ret; \
unsigned int enable_event; \
unsigned int debug_level; \
unsigned int sendevent; \
enable_event = enable_event_booster; \
debug_level = debug_flag; \
sendevent = send_ev_enable; \
ret = sprintf _ARGU_; \
pr_booster("[Input Booster8] %s buf : %s\n", __func__, buf); \
return ret; \
} \
ssize_t input_booster_sysfs_class_store_##_ATTR_(struct class *dev, struct class_attribute *attr, const char *buf, size_t count) \
{ \
unsigned int enable_event[1] = {-1}; \
unsigned int debug_level[1] = {-1}; \
unsigned int sendevent[1] = {-1}; \
sscanf _ARGU_; \
send_ev_enable = sendevent[0]; \
debug_flag = debug_level[0]; \
enable_event_booster = enable_event[0]; \
pr_booster("[Input Booster8] %s buf : %s\n", __func__, buf); \
if (sscanf _ARGU_ != _COUNT_) { \
return count; \
} \
return count; \
} \
static struct class_attribute class_attr_##_ATTR_ = __ATTR(_ATTR_, S_IRUGO | S_IWUSR, input_booster_sysfs_class_show_##_ATTR_, input_booster_sysfs_class_store_##_ATTR_);
#define HEAD_TAIL_SYSFS_DEVICE(_ATTR_) \
ssize_t input_booster_sysfs_device_show_##_ATTR_(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
int i = 0; \
ssize_t ret = 0; \
ssize_t return_value = 0; \
struct t_ib_device_tree *ib_dt = dev_get_drvdata(dev); \
if (ib_dt == NULL) { \
return ret; \
} \
ret = sprintf(buf, "%d", ib_dt->_ATTR_##_time); \
return_value += ret; \
buf = buf + ret; \
for (i = 0; i < MAX_RES_COUNT; ++i) { \
pr_booster("[Input Booster8] show i : %d, %s\n", i, #_ATTR_); \
ret = sprintf(buf, " %d", ib_dt->res[i]._ATTR_##_value); \
buf = buf + ret; \
return_value += ret; \
} \
ret = sprintf(buf, "\n"); \
buf = buf + ret; \
return_value += ret; \
return return_value; \
} \
ssize_t input_booster_sysfs_device_store_##_ATTR_(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
{ \
struct t_ib_device_tree *ib_dt = dev_get_drvdata(dev); \
int time = 0; \
int values[MAX_RES_COUNT + 1] = {0,}; \
int i = 0; \
int offset = 0; \
int dataCnt = 0; \
pr_booster("[Input Booster8] %s buf : %s\n", __func__, buf); \
if (ib_dt == NULL) \
return count; \
if (!sscanf(buf, "%d%n", &time, &offset)) { \
pr_booster("### Keep this format : [time cpu_freq hmp_boost ddr_freq lpm_bias] (Ex: 200 1171200 2 1017 5###\n"); \
return count; \
} \
buf += offset; \
for (i = 0; i < MAX_RES_COUNT; ++i) { \
if (!sscanf(buf, "%d%n", &values[i], &offset)) { \
pr_booster("### Keep this format : [time cpu_freq hmp_boost ddr_freq lpm_bias] (Ex: 200 1171200 2 1017 5###\n"); \
return count; \
} \
dataCnt++; \
buf += offset; \
} \
if (sscanf(buf, "%d", &values[i])) { \
pr_booster("### Keep this format : [time cpu_freq hmp_boost ddr_freq lpm_bias] (Ex: 200 1171200 2 1017 5###\n"); \
return count; \
} \
ib_dt->_ATTR_##_time = time; \
for (i = 0; i < MAX_RES_COUNT; ++i) { \
ib_dt->res[i]._ATTR_##_value = values[i]; \
} \
return count; \
} \
DEVICE_ATTR(_ATTR_, S_IRUGO | S_IWUSR, input_booster_sysfs_device_show_##_ATTR_, input_booster_sysfs_device_store_##_ATTR_);
#define LEVEL_SYSFS_DEVICE(_ATTR_) \
ssize_t input_booster_sysfs_device_show_##_ATTR_(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
ssize_t ret = 0; \
ret += sprintf(buf, "%d\n", level_value); \
return ret; \
} \
ssize_t input_booster_sysfs_device_store_##_ATTR_(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
{ \
int level; \
sscanf(buf, "%d", &level); \
if (level < 0 || level > 2) { \
pr_booster("### Keep this format : greater than 0, and less than 3\n"); \
return count; \
} \
pr_booster("[Input Booster8] %s buf : %s\n", __func__, buf); \
level_value = level; \
return count; \
} \
DEVICE_ATTR(_ATTR_, S_IRUGO | S_IWUSR, input_booster_sysfs_device_show_##_ATTR_, input_booster_sysfs_device_store_##_ATTR_);
#define INIT_SYSFS_CLASS(_CLASS_) { \
ret = class_create_file(sysfs_class, &class_attr_##_CLASS_); \
if (ret) { \
pr_booster("[Input Booster] Failed to create class\n"); \
class_destroy(sysfs_class); \
return; \
} \
}
//---------------------------------------------- STRUCT & VARIABLE FOR SYSFS ----------------------------------------------//
#define TYPE_BITS 8
#define CODE_BITS 12
enum ib_flag_on_off {
FLAG_OFF = 0,
FLAG_ON
};
enum booster_head_or_tail {
IB_HEAD = 0,
IB_TAIL,
IB_MAX
};
enum booster_mode_on_off {
BOOSTER_OFF = 0,
BOOSTER_ON,
};
enum {
NONE_TYPE_DEVICE = -1,
KEY = 0,
TOUCH_KEY,
TOUCH,
MULTI_TOUCH,
KEYBOARD,
MOUSE,
MOUSH_WHEEL,
HOVER,
SPEN,
MAX_DEVICE_TYPE_NUM
};
struct t_ib_trigger {
char dev_name[100];
int key_id;
int event_type;
int dev_type;
struct work_struct ib_trigger_work;
};
struct t_ib_info {
char *dev_name;
int key_id;
int uniq_id;
int press_flag;
int rel_flag;
int isHeadFinished;
struct t_ib_device_tree *ib_dt;
struct list_head list;
struct work_struct ib_state_work[IB_MAX];
struct delayed_work ib_timeout_work[IB_MAX];
struct mutex lock;
};
struct t_ib_target {
int uniq_id;
int value;
struct list_head list;
};
struct t_ib_res_info {
int res_id;
const char *label;
int head_value;
int tail_value;
};
struct t_ib_device_tree {
const char *label;
int type;
int head_time;
int tail_time;
struct t_ib_res_info *res;
};
struct t_ddr_info {
long mHz;
long bps;
};
void trigger_input_booster(struct work_struct *work);
void press_state_func(struct work_struct *work);
void press_timeout_func(struct work_struct *work);
void release_state_func(struct work_struct *work);
void release_timeout_func(struct work_struct *work);
void ib_release_booster(int res_id);
void ib_set_booster(int *qos_values);
unsigned int create_uniq_id(int type, int code, int slot);
void input_booster_init(void);
void init_sysfs_device(struct class *sysfs_class, struct t_ib_device_tree *ib_dt);
void input_booster_init_vendor(int *release_val);
void input_booster_exit_vendor(void);
int set_freq_limit(unsigned long id, unsigned int freq);
#if defined(CONFIG_ARCH_QCOM)
enum booster_res_type {
CPUFREQ = 0,
DDRFREQ,
HMPBOOST,
LPMBIAS,
MAX_RES_COUNT
};
#elif defined(CONFIG_ARCH_EXYNOS)
enum booster_res_type {
CLUSTER2 = 0,
CLUSTER1,
CLUSTER0,
MIF,
INT,
HMPBOOST,
UCC,
MAX_RES_COUNT
};
#endif
extern int ib_init_succeed;
extern unsigned int debug_flag;
extern unsigned int enable_event_booster;
extern spinlock_t ib_type_lock;
extern struct workqueue_struct *ev_unbound_wq;
extern struct workqueue_struct *ib_unbound_highwq;
extern int trigger_cnt;
// @ ib_trigger : input trigger starts input booster in evdev.c.
extern struct t_ib_trigger *ib_trigger;
// @evdev_mt_slot : save the number of inputed touch slot.
extern int evdev_mt_slot;
// @evdev_mt_event[] : save count of each boooter's events.
extern int evdev_mt_event[MAX_DEVICE_TYPE_NUM];
#endif // _INPUT_BOOSTER_H_
#endif // Input Booster -