1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2024-11-19 13:27:49 +00:00
a155-U-u1/kernel-5.10/drivers/input/touchscreen/GT9916P/goodix_ts_gesture.c
2024-03-11 06:53:12 +11:00

446 lines
12 KiB
C

/*
* 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 <linux/spinlock.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <linux/atomic.h>
#include <linux/input/mt.h>
#include "goodix_ts_core.h"
#define GOODIX_GESTURE_DOUBLE_TAP 0xCC
#define GOODIX_GESTURE_SINGLE_TAP 0x4C
#define GOODIX_GESTURE_FOD_DOWN 0x46
#define GOODIX_GESTURE_FOD_UP 0x55
/*
* 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;
struct goodix_ts_core *ts_core;
struct goodix_ext_module module;
};
static struct gesture_module *gsx_gesture; /*allocated in gesture init module*/
static bool module_initialized;
static ssize_t gsx_double_type_show(struct goodix_ext_module *module, char *buf)
{
struct gesture_module *gsx = module->priv_data;
unsigned char type = gsx->ts_core->gesture_type;
if (!gsx)
return -EIO;
if (atomic_read(&gsx->registered) == 0) {
ts_err("gesture module is not registered");
return 0;
}
return sprintf(buf, "%s\n",
(type & GESTURE_DOUBLE_TAP) ? "enable" : "disable");
}
static ssize_t gsx_double_type_store(struct goodix_ext_module *module,
const char *buf, size_t count)
{
struct gesture_module *gsx = module->priv_data;
if (!gsx)
return -EIO;
if (atomic_read(&gsx->registered) == 0) {
ts_err("gesture module is not registered");
return 0;
}
if (buf[0] == '1') {
ts_info("enable double tap");
gsx->ts_core->gesture_type |= GESTURE_DOUBLE_TAP;
} else if (buf[0] == '0') {
ts_info("disable double tap");
gsx->ts_core->gesture_type &= ~GESTURE_DOUBLE_TAP;
} else
ts_err("invalid cmd[%d]", buf[0]);
return count;
}
static ssize_t gsx_single_type_show(struct goodix_ext_module *module, char *buf)
{
struct gesture_module *gsx = module->priv_data;
unsigned char type = gsx->ts_core->gesture_type;
if (!gsx)
return -EIO;
if (atomic_read(&gsx->registered) == 0) {
ts_err("gesture module is not registered");
return 0;
}
return sprintf(buf, "%s\n",
(type & GESTURE_SINGLE_TAP) ? "enable" : "disable");
}
static ssize_t gsx_single_type_store(struct goodix_ext_module *module,
const char *buf, size_t count)
{
struct gesture_module *gsx = module->priv_data;
if (!gsx)
return -EIO;
if (atomic_read(&gsx->registered) == 0) {
ts_err("gesture module is not registered");
return 0;
}
if (buf[0] == '1') {
ts_info("enable single tap");
gsx->ts_core->gesture_type |= GESTURE_SINGLE_TAP;
} else if (buf[0] == '0') {
ts_info("disable single tap");
gsx->ts_core->gesture_type &= ~GESTURE_SINGLE_TAP;
} else
ts_err("invalid cmd[%d]", buf[0]);
return count;
}
static ssize_t gsx_fod_type_show(struct goodix_ext_module *module, char *buf)
{
struct gesture_module *gsx = module->priv_data;
unsigned char type = gsx->ts_core->gesture_type;
if (!gsx)
return -EIO;
if (atomic_read(&gsx->registered) == 0) {
ts_err("gesture module is not registered");
return 0;
}
return sprintf(buf, "%s\n",
(type & GESTURE_FOD_PRESS) ? "enable" : "disable");
}
static ssize_t gsx_fod_type_store(struct goodix_ext_module *module,
const char *buf, size_t count)
{
struct gesture_module *gsx = module->priv_data;
if (!gsx)
return -EIO;
if (atomic_read(&gsx->registered) == 0) {
ts_err("gesture module is not registered");
return 0;
}
if (buf[0] == '1') {
ts_info("enable fod");
gsx->ts_core->gesture_type |= GESTURE_FOD_PRESS;
} else if (buf[0] == '0') {
ts_info("disable fod");
gsx->ts_core->gesture_type &= ~GESTURE_FOD_PRESS;
} else
ts_err("invalid cmd[%d]", buf[0]);
return count;
}
const struct goodix_ext_attribute gesture_attrs[] = {
__EXTMOD_ATTR(double_en, 0664, gsx_double_type_show,
gsx_double_type_store),
__EXTMOD_ATTR(single_en, 0664, gsx_single_type_show,
gsx_single_type_store),
__EXTMOD_ATTR(fod_en, 0664, gsx_fod_type_show, gsx_fod_type_store),
};
static int gsx_gesture_init(struct goodix_ts_core *cd,
struct goodix_ext_module *module)
{
struct gesture_module *gsx = module->priv_data;
if (!cd || !cd->hw_ops->gesture) {
ts_err("gesture unsupported");
return -EINVAL;
}
gsx->ts_core = cd;
gsx->ts_core->gesture_type = 0;
atomic_set(&gsx->registered, 1);
return 0;
}
static int gsx_gesture_exit(struct goodix_ts_core *cd,
struct goodix_ext_module *module)
{
struct gesture_module *gsx = module->priv_data;
if (!cd || !cd->hw_ops->gesture) {
ts_err("gesture unsupported");
return -EINVAL;
}
atomic_set(&gsx->registered, 0);
return 0;
}
/**
* gsx_gesture_ist - Gesture Irq handle
* This functions is excuted when interrupt happended 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 fodx, fody, overlay_area;
int ret;
if (atomic_read(&cd->suspended) == 0 || cd->gesture_type == 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;
}
switch (gs_event.gesture_type) {
case GOODIX_GESTURE_SINGLE_TAP:
if (cd->gesture_type & GESTURE_SINGLE_TAP) {
ts_info("get SINGLE-TAP gesture");
input_report_key(cd->input_dev, KEY_WAKEUP, 1);
// input_report_key(cd->input_dev, KEY_GOTO, 1);
input_sync(cd->input_dev);
input_report_key(cd->input_dev, KEY_WAKEUP, 0);
// input_report_key(cd->input_dev, KEY_GOTO, 0);
input_sync(cd->input_dev);
} else {
ts_debug("not enable SINGLE-TAP");
}
break;
case GOODIX_GESTURE_DOUBLE_TAP:
if (cd->gesture_type & GESTURE_DOUBLE_TAP) {
ts_info("get DOUBLE-TAP gesture");
input_report_key(cd->input_dev, KEY_WAKEUP, 1);
input_sync(cd->input_dev);
input_report_key(cd->input_dev, KEY_WAKEUP, 0);
input_sync(cd->input_dev);
} else {
ts_debug("not enable DOUBLE-TAP");
}
break;
case GOODIX_GESTURE_FOD_DOWN:
if (cd->gesture_type & GESTURE_FOD_PRESS) {
ts_info("get FOD-DOWN gesture");
fodx = le16_to_cpup((__le16 *)gs_event.gesture_data);
fody = le16_to_cpup(
(__le16 *)(gs_event.gesture_data + 2));
overlay_area = gs_event.gesture_data[4];
ts_debug("fodx:%d fody:%d overlay_area:%d", fodx, fody,
overlay_area);
input_report_key(cd->input_dev, BTN_TOUCH, 1);
input_mt_slot(cd->input_dev, 0);
input_mt_report_slot_state(cd->input_dev,
MT_TOOL_FINGER, 1);
input_report_abs(cd->input_dev, ABS_MT_POSITION_X,
fodx);
input_report_abs(cd->input_dev, ABS_MT_POSITION_Y,
fody);
input_report_abs(cd->input_dev, ABS_MT_WIDTH_MAJOR,
overlay_area);
input_sync(cd->input_dev);
} else {
ts_debug("not enable FOD-DOWN");
}
break;
case GOODIX_GESTURE_FOD_UP:
if (cd->gesture_type & GESTURE_FOD_PRESS) {
ts_info("get FOD-UP gesture");
// fodx = le16_to_cpup((__le16 *)gs_event.gesture_data);
// fody = le16_to_cpup((__le16 *)(gs_event.gesture_data + 2));
// overlay_area = gs_event.gesture_data[4];
input_report_key(cd->input_dev, BTN_TOUCH, 0);
input_mt_slot(cd->input_dev, 0);
input_mt_report_slot_state(cd->input_dev,
MT_TOOL_FINGER, 0);
input_sync(cd->input_dev);
} else {
ts_debug("not enable FOD-UP");
}
break;
default:
ts_err("not support gesture type[%02X]", gs_event.gesture_type);
break;
}
re_send_ges_cmd:
if (hw_ops->gesture(cd, 0))
ts_info("warning: failed re_send gesture cmd");
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;
if (cd->gesture_type == 0)
return EVT_CONTINUE;
ret = hw_ops->gesture(cd, 0);
if (ret)
ts_err("failed enter gesture mode");
else
ts_info("enter gesture mode, type[0x%02X]", cd->gesture_type);
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;
if (cd->gesture_type == 0)
return EVT_CONTINUE;
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);
/* 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;
goodix_register_ext_module_no_wait(&gsx_gesture->module);
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_unregister_ext_module(&gsx_gesture->module);
/* 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;
}