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:
parent
f27b9e2a7b
commit
f1476272cc
6 changed files with 1443 additions and 0 deletions
|
@ -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"
|
||||
|
|
|
@ -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/
|
||||
|
|
263
drivers/input/evdev_booster.c
Normal file
263
drivers/input/evdev_booster.c
Normal 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
|
745
drivers/input/input_booster.c
Normal file
745
drivers/input/input_booster.c
Normal 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
|
||||
}
|
124
drivers/input/input_booster_lsi.c
Normal file
124
drivers/input/input_booster_lsi.c
Normal 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);
|
||||
}
|
300
include/linux/input/input_booster.h
Normal file
300
include/linux/input/input_booster.h
Normal 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 -
|
Loading…
Add table
Add a link
Reference in a new issue