diff options
author | Wendly Li <wendlyli@google.com> | 2021-12-29 08:25:53 +0000 |
---|---|---|
committer | Wendly Li <wendlyli@google.com> | 2022-01-21 03:51:40 +0000 |
commit | f4cbd1e784f777c544763bb0e2bdb65ad5c685cf (patch) | |
tree | b46d9dba8ae66c85da61754fbdb3b9ce279c3789 /goodix_ts_gesture.c | |
parent | aa0a75aa6af4b4513db1719e841973d3a50f59c3 (diff) | |
download | goodix_touch-f4cbd1e784f777c544763bb0e2bdb65ad5c685cf.tar.gz |
Initial the driver from the original vender code
BYPASS_INCLUSIVE_LANGUAGE_REASON=master and slave are stardand of SPI
Bug: 214018056
Bug: 214118475
Change-Id: Ib1e7bbdca701fe852f665ee2986824d71d5eebe2
Signed-off-by: Wendly Li <wendlyli@google.com>
Diffstat (limited to 'goodix_ts_gesture.c')
-rw-r--r-- | goodix_ts_gesture.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/goodix_ts_gesture.c b/goodix_ts_gesture.c new file mode 100644 index 0000000..43899b4 --- /dev/null +++ b/goodix_ts_gesture.c @@ -0,0 +1,381 @@ +/* + * Goodix Gesture Module + * + * Copyright (C) 2019 - 2020 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include "goodix_ts_core.h" +#include <linux/atomic.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/version.h> + +#define QUERYBIT(longlong, bit) (!!(longlong[bit / 8] & (1 << bit % 8))) + +#define GSX_GESTURE_TYPE_LEN 32 + +/* + * struct gesture_module - gesture module data + * @registered: module register state + * @sysfs_node_created: sysfs node state + * @gesture_type: valid gesture type, each bit represent one gesture type + * @gesture_data: store latest gesture code get from irq event + * @gesture_ts_cmd: gesture command data + */ +struct gesture_module { + atomic_t registered; + rwlock_t rwlock; + u8 gesture_type[GSX_GESTURE_TYPE_LEN]; + u8 gesture_data; + struct goodix_ext_module module; +}; + +static struct gesture_module *gsx_gesture; /*allocated in gesture init module*/ +static bool module_initialized; + +int goodix_gesture_enable(int enable) +{ + int ret = 0; + + if (!module_initialized) + return 0; + + if (enable) { + if (atomic_read(&gsx_gesture->registered)) + ts_info("gesture module has been already registered"); + else + ret = goodix_register_ext_module_no_wait( + &gsx_gesture->module); + } else { + if (!atomic_read(&gsx_gesture->registered)) + ts_info("gesture module has been already unregistered"); + else + ret = goodix_unregister_ext_module( + &gsx_gesture->module); + } + + return ret; +} + +/** + * gsx_gesture_type_show - show valid gesture type + * + * @module: pointer to goodix_ext_module struct + * @buf: pointer to output buffer + * Returns >=0 - succeed,< 0 - failed + */ +static ssize_t gsx_gesture_type_show( + struct goodix_ext_module *module, char *buf) +{ + int count = 0, i, ret = 0; + unsigned char *type; + + type = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!type) + return -ENOMEM; + read_lock(&gsx_gesture->rwlock); + for (i = 0; i < 256; i++) { + if (QUERYBIT(gsx_gesture->gesture_type, i)) { + count += scnprintf(type + count, PAGE_SIZE, "%02x,", i); + } + } + if (count > 0) + ret = scnprintf(buf, PAGE_SIZE, "%s\n", type); + read_unlock(&gsx_gesture->rwlock); + + kfree(type); + return ret; +} + +/** + * gsx_gesture_type_store - set vailed gesture + * + * @module: pointer to goodix_ext_module struct + * @buf: pointer to valid gesture type + * @count: length of buf + * Returns >0 - valid gestures, < 0 - failed + */ +static ssize_t gsx_gesture_type_store( + struct goodix_ext_module *module, const char *buf, size_t count) +{ + int i; + + if (count <= 0 || count > 256 || buf == NULL) { + ts_err("Parameter error"); + return -EINVAL; + } + + write_lock(&gsx_gesture->rwlock); + memset(gsx_gesture->gesture_type, 0, GSX_GESTURE_TYPE_LEN); + for (i = 0; i < count; i++) + gsx_gesture->gesture_type[buf[i] / 8] |= (0x1 << buf[i] % 8); + write_unlock(&gsx_gesture->rwlock); + + return count; +} + +static ssize_t gsx_gesture_enable_show( + struct goodix_ext_module *module, char *buf) +{ + return scnprintf( + buf, PAGE_SIZE, "%d\n", atomic_read(&gsx_gesture->registered)); +} + +static ssize_t gsx_gesture_enable_store( + struct goodix_ext_module *module, const char *buf, size_t count) +{ + bool val; + int ret; + + ret = strtobool(buf, &val); + if (ret < 0) + return ret; + + if (val) { + ret = goodix_gesture_enable(1); + return ret ? ret : count; + } else { + ret = goodix_gesture_enable(0); + return ret ? ret : count; + } +} + +static ssize_t gsx_gesture_data_show( + struct goodix_ext_module *module, char *buf) +{ + ssize_t count; + + read_lock(&gsx_gesture->rwlock); + count = scnprintf(buf, PAGE_SIZE, "gesture type code:0x%x\n", + gsx_gesture->gesture_data); + read_unlock(&gsx_gesture->rwlock); + + return count; +} + +const struct goodix_ext_attribute gesture_attrs[] = { + __EXTMOD_ATTR( + type, 0666, gsx_gesture_type_show, gsx_gesture_type_store), + __EXTMOD_ATTR(enable, 0666, gsx_gesture_enable_show, + gsx_gesture_enable_store), + __EXTMOD_ATTR(data, 0444, gsx_gesture_data_show, NULL) +}; + +static int gsx_gesture_init( + struct goodix_ts_core *cd, struct goodix_ext_module *module) +{ + if (!cd || !cd->hw_ops->gesture) { + ts_err("gesture unsupported"); + return -EINVAL; + } + + ts_info("gesture switch: ON"); + ts_debug("enable all gesture type"); + /* set all bit to 1 to enable all gesture wakeup */ + memset(gsx_gesture->gesture_type, 0xff, GSX_GESTURE_TYPE_LEN); + atomic_set(&gsx_gesture->registered, 1); + + return 0; +} + +static int gsx_gesture_exit( + struct goodix_ts_core *cd, struct goodix_ext_module *module) +{ + if (!cd || !cd->hw_ops->gesture) { + ts_err("gesture unsupported"); + return -EINVAL; + } + + ts_info("gesture switch: OFF"); + ts_debug("disable all gesture type"); + memset(gsx_gesture->gesture_type, 0x00, GSX_GESTURE_TYPE_LEN); + atomic_set(&gsx_gesture->registered, 0); + + return 0; +} + +/** + * gsx_gesture_ist - Gesture Irq handle + * This functions is excuted when interrupt happened and + * ic in doze mode. + * + * @cd: pointer to touch core data + * @module: pointer to goodix_ext_module struct + * return: 0 goon execute, EVT_CANCEL_IRQEVT stop execute + */ +static int gsx_gesture_ist( + struct goodix_ts_core *cd, struct goodix_ext_module *module) +{ + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + struct goodix_ts_event gs_event = { 0 }; + int ret; + + if (atomic_read(&cd->suspended) == 0) + return EVT_CONTINUE; + + ret = hw_ops->event_handler(cd, &gs_event); + if (ret) { + ts_err("failed get gesture data"); + goto re_send_ges_cmd; + } + + if (!(gs_event.event_type & EVENT_GESTURE)) { + ts_err("invalid event type: 0x%x", cd->ts_event.event_type); + goto re_send_ges_cmd; + } + + if (QUERYBIT(gsx_gesture->gesture_type, gs_event.gesture_type)) { + gsx_gesture->gesture_data = gs_event.gesture_type; + /* do resume routine */ + ts_info("got valid gesture type 0x%x", gs_event.gesture_type); + input_report_key(cd->input_dev, KEY_POWER, 1); + input_sync(cd->input_dev); + input_report_key(cd->input_dev, KEY_POWER, 0); + input_sync(cd->input_dev); + goto gesture_ist_exit; + } else { + ts_info("unsupported gesture:%x", gs_event.gesture_type); + } + +re_send_ges_cmd: + if (hw_ops->gesture(cd, 0)) + ts_info("warning: failed re_send gesture cmd"); +gesture_ist_exit: + if (!cd->tools_ctrl_sync) + hw_ops->after_event_handler(cd); + return EVT_CANCEL_IRQEVT; +} + +/** + * gsx_gesture_before_suspend - execute gesture suspend routine + * This functions is excuted to set ic into doze mode + * + * @cd: pointer to touch core data + * @module: pointer to goodix_ext_module struct + * return: 0 goon execute, EVT_IRQCANCLED stop execute + */ +static int gsx_gesture_before_suspend( + struct goodix_ts_core *cd, struct goodix_ext_module *module) +{ + int ret; + const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + ret = hw_ops->gesture(cd, 0); + if (ret) + ts_err("failed enter gesture mode"); + else + ts_info("enter gesture mode"); + + hw_ops->irq_enable(cd, true); + enable_irq_wake(cd->irq); + + return EVT_CANCEL_SUSPEND; +} + +static int gsx_gesture_before_resume( + struct goodix_ts_core *cd, struct goodix_ext_module *module) +{ + const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + hw_ops->irq_enable(cd, false); + disable_irq_wake(cd->irq); + hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); + + return EVT_CANCEL_RESUME; +} + +static struct goodix_ext_module_funcs gsx_gesture_funcs = { + .irq_event = gsx_gesture_ist, + .init = gsx_gesture_init, + .exit = gsx_gesture_exit, + .before_suspend = gsx_gesture_before_suspend, + .before_resume = gsx_gesture_before_resume, +}; + +int gesture_module_init(void) +{ + int ret; + int i; + struct kobject *def_kobj = goodix_get_default_kobj(); + struct kobj_type *def_kobj_type = goodix_get_default_ktype(); + + gsx_gesture = kzalloc(sizeof(struct gesture_module), GFP_KERNEL); + if (!gsx_gesture) + return -ENOMEM; + + gsx_gesture->module.funcs = &gsx_gesture_funcs; + gsx_gesture->module.priority = EXTMOD_PRIO_GESTURE; + gsx_gesture->module.name = "Goodix_gsx_gesture"; + gsx_gesture->module.priv_data = gsx_gesture; + + atomic_set(&gsx_gesture->registered, 0); + rwlock_init(&gsx_gesture->rwlock); + + /* gesture sysfs init */ + ret = kobject_init_and_add( + &gsx_gesture->module.kobj, def_kobj_type, def_kobj, "gesture"); + if (ret) { + ts_err("failed create gesture sysfs node!"); + goto err_out; + } + + for (i = 0; i < ARRAY_SIZE(gesture_attrs) && !ret; i++) + ret = sysfs_create_file( + &gsx_gesture->module.kobj, &gesture_attrs[i].attr); + if (ret) { + ts_err("failed create gst sysfs files"); + while (--i >= 0) + sysfs_remove_file(&gsx_gesture->module.kobj, + &gesture_attrs[i].attr); + + kobject_put(&gsx_gesture->module.kobj); + goto err_out; + } + + module_initialized = true; + ts_info("gesture module init success"); + + return 0; + +err_out: + ts_err("gesture module init failed!"); + kfree(gsx_gesture); + return ret; +} + +void gesture_module_exit(void) +{ + int i; + + ts_info("gesture module exit"); + if (!module_initialized) + return; + + goodix_gesture_enable(0); + + /* deinit sysfs */ + for (i = 0; i < ARRAY_SIZE(gesture_attrs); i++) + sysfs_remove_file( + &gsx_gesture->module.kobj, &gesture_attrs[i].attr); + + kobject_put(&gsx_gesture->module.kobj); + kfree(gsx_gesture); + module_initialized = false; +} |