We need support to various accessories on the device, some requiring switch does not exist in switch list. So added switch for the following purpose. SW_EXT_PEN_ATTACHED is for the checking the external pen attached or not on the device. We also added driver that uses such event. Signed-off-by: Hyungjae Im <hj2.im@xxxxxxxxxxx> --- drivers/input/Kconfig | 12 ++ drivers/input/Makefile | 1 + drivers/input/ext_pen_detect.c | 237 +++++++++++++++++++++++++ include/linux/mod_devicetable.h | 2 +- include/uapi/linux/input-event-codes.h | 3 +- 5 files changed, 253 insertions(+), 2 deletions(-) create mode 100644 drivers/input/ext_pen_detect.c diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index ba5e7444c547..5d6d15c8f7e7 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -197,6 +197,18 @@ config INPUT_COVER_DETECT To compile this driver as a module, choose M here: the module will be called cover_detect. +config INPUT_EXT_PEN_DETECT + tristate "Enable external pen attach detection" + help + Say Y here to enable external pen detection + and send a event when external pen is attached/detached. + Active gpio state is low and active event value is 0. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ext_pen_detect. + comment "Input Device Drivers" source "drivers/input/keyboard/Kconfig" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index fc8dd9091821..0ccf02e34557 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o obj-$(CONFIG_RMI4_CORE) += rmi4/ obj-$(CONFIG_INPUT_COVER_DETECT)+= cover_detect.o +obj-$(CONFIG_INPUT_EXT_PEN_DETECT)+= ext_pen_detect.o diff --git a/drivers/input/ext_pen_detect.c b/drivers/input/ext_pen_detect.c new file mode 100644 index 000000000000..9a0d106e49f8 --- /dev/null +++ b/drivers/input/ext_pen_detect.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Support detection pen attachment externally on device + * + * Copyright (C) 2020 Samsung Electronics Co. Ltd. All Rights Reserved. + * + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/wakelock.h> + +struct ext_pen_detect_drvdata { + struct input_dev *input; + struct delayed_work ext_pen_detect_dwork; + struct wakeup_source *ws; + int gpio_ext_pen_detect; + int irq_ext_pen_detect; +}; + +static void ext_pen_detect_work(struct work_struct *work) +{ + struct ext_pen_detect_drvdata *ddata = + container_of(work, struct ext_pen_detect_drvdata, + ext_pen_detect_dwork.work); + bool ext_pen_status; + + ext_pen_status = gpio_get_value(ddata->gpio_ext_pen_detect); + + input_report_switch(ddata->input, + SW_EXT_PEN_ATTACHED, ext_pen_status); + input_sync(ddata->input); +} + +static void __ext_pen_detect(struct ext_pen_detect_drvdata *ddata, + bool ext_pen_status) +{ + cancel_delayed_work_sync(&ddata->ext_pen_detect_dwork); + if (ext_pen_status) { + __pm_wakeup_event(ddata->ws, jiffies_to_msecs(HZ / 20)); + schedule_delayed_work(&ddata->ext_pen_detect_dwork, + HZ * 1 / 100); + } else { + __pm_relax(ddata->ws); + schedule_delayed_work(&ddata->ext_pen_detect_dwork, 0); + } +} + +static irqreturn_t ext_pen_detect_irq(int irq, void *dev_id) +{ + bool ext_pen_status; + struct ext_pen_detect_drvdata *ddata = dev_id; + + ext_pen_status = gpio_get_value(ddata->gpio_ext_pen_detect); + + __ext_pen_detect(ddata, ext_pen_status); + + return IRQ_HANDLED; +} + +static int ext_pen_detect_open(struct input_dev *input) +{ + struct ext_pen_detect_drvdata *ddata = input_get_drvdata(input); + /* update the current status */ + schedule_delayed_work(&ddata->ext_pen_detect_dwork, HZ / 2); + /* Report current state of buttons that are connected to GPIOs */ + input_sync(input); + + return 0; +} + +static void ext_pen_detect_close(struct input_dev *input) +{ +} + +static void init_ext_pen_detect_irq(struct input_dev *input) +{ + struct ext_pen_detect_drvdata *ddata = input_get_drvdata(input); + + int ret = 0; + int irq = ddata->irq_ext_pen_detect; + + ret = request_threaded_irq( + irq, NULL, + ext_pen_detect_irq, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "ext_pen_detect", ddata); + if (ret < 0) + pr_err("keys: failed to request ext_pen detect irq %d gpio %d\n", + irq, ddata->gpio_ext_pen_detect); +} + +#ifdef CONFIG_OF +static int of_ext_pen_detect_data_parsing_dt(struct device *dev, + struct ext_pen_detect_drvdata *ddata) +{ + struct device_node *np = dev->of_node; + int gpio; + enum of_gpio_flags flags; + + gpio = of_get_named_gpio_flags(np, "gpio_ext_pen_detect", 0, &flags); + ddata->gpio_ext_pen_detect = gpio; + + ddata->irq_ext_pen_detect = gpio_to_irq(gpio); + + return 0; +} +#endif + +static int ext_pen_detect_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ext_pen_detect_drvdata *ddata; + struct input_dev *input; + int error; + int wakeup = 0; + + ddata = kzalloc(sizeof(struct ext_pen_detect_drvdata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + +#ifdef CONFIG_OF + if (dev->of_node) { + error = of_ext_pen_detect_data_parsing_dt(dev, ddata); + if (error < 0) { + dev_err(dev, "fail to get the devicetree, error: %d\n", + error); + goto fail1; + } + } +#endif + + input = input_allocate_device(); + if (!input) { + dev_err(dev, "failed to allocate state\n"); + error = -ENOMEM; + goto fail1; + } + + ddata->input = input; + + ddata->ws = wakeup_source_register(NULL, "ext_pen_detect"); + + platform_set_drvdata(pdev, ddata); + input_set_drvdata(input, ddata); + + input->name = "ext_pen_detect"; + input->phys = "ext_pen_detect"; + input->dev.parent = &pdev->dev; + + input->evbit[0] |= BIT_MASK(EV_SW); + input_set_capability(input, EV_SW, SW_EXT_PEN_ATTACHED); + + input->open = ext_pen_detect_open; + input->close = ext_pen_detect_close; + + /* Enable auto repeat feature of Linux input subsystem */ + __set_bit(EV_REP, input->evbit); + + INIT_DELAYED_WORK(&ddata->ext_pen_detect_dwork, ext_pen_detect_work); + + init_ext_pen_detect_irq(input); + + if (ddata->gpio_ext_pen_detect != 0) { + error = input_register_device(input); + if (error) { + dev_err(dev, "Unable to register input device, error: %d\n", + error); + goto fail1; + } + } + + device_init_wakeup(&pdev->dev, wakeup); + + return 0; + + fail1: + kfree(ddata); + + return error; +} + +static int ext_pen_detect_remove(struct platform_device *pdev) +{ + struct ext_pen_detect_drvdata *ddata = platform_get_drvdata(pdev); + struct input_dev *input = ddata->input; + + device_init_wakeup(&pdev->dev, 0); + + input_unregister_device(input); + + wakeup_source_unregister(ddata->ws); + + kfree(ddata); + + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id ext_pen_detect_dt_ids[] = { + { .compatible = "ext_pen_detect" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ext_pen_detect_dt_ids); +#endif /* CONFIG_OF */ + +static struct platform_driver ext_pen_detect_device_driver = { + .probe = ext_pen_detect_probe, + .remove = ext_pen_detect_remove, + .driver = { + .name = "ext_pen_detect", + .owner = THIS_MODULE, +#if defined(CONFIG_OF) + .of_match_table = ext_pen_detect_dt_ids, +#endif /* CONFIG_OF */ + } +}; + +static int __init ext_pen_detect_init(void) +{ + return platform_driver_register(&ext_pen_detect_device_driver); +} + +static void __exit ext_pen_detect_exit(void) +{ + platform_driver_unregister(&ext_pen_detect_device_driver); +} + +module_init(ext_pen_detect_init); +module_exit(ext_pen_detect_exit); diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 2e6ae686555b..897f5a3e7721 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -320,7 +320,7 @@ struct pcmcia_device_id { #define INPUT_DEVICE_ID_LED_MAX 0x0f #define INPUT_DEVICE_ID_SND_MAX 0x07 #define INPUT_DEVICE_ID_FF_MAX 0x7f -#define INPUT_DEVICE_ID_SW_MAX 0x11 +#define INPUT_DEVICE_ID_SW_MAX 0x12 #define INPUT_DEVICE_ID_PROP_MAX 0x1f #define INPUT_DEVICE_ID_MATCH_BUS 1 diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h index 32e27732161c..8ca2acee1f92 100644 --- a/include/uapi/linux/input-event-codes.h +++ b/include/uapi/linux/input-event-codes.h @@ -890,7 +890,8 @@ #define SW_PEN_INSERTED 0x0f /* set = pen inserted */ #define SW_MACHINE_COVER 0x10 /* set = cover closed */ #define SW_COVER_ATTACHED 0x11 /* set = cover attached */ -#define SW_MAX 0x11 +#define SW_EXT_PEN_ATTACHED 0x12 /* set = external pen attached */ +#define SW_MAX 0x12 #define SW_CNT (SW_MAX+1) /* -- 2.17.1