From 13c23cd18bd12ddbf00beddd136e3cd33b4f2dfa Mon Sep 17 00:00:00 2001 From: Franklin S Cooper Jr Date: Fri, 11 Sep 2015 17:30:34 -0700 Subject: Input: edt-ft5x06 - switch to newer gpio framework The current/old gpio framework used doesn't properly listen to ACTIVE_LOW and ACTIVE_HIGH flags. The newer gpio framework takes into account these flags when setting gpio values. Since the values being output were based on voltage and not logic they change to reflect this difference. Also use gpiod_set_value_cansleep since wake and reset pins can be provided by bus based io expanders. Switch from msleep(5) to udelay_range(5000,6000) to avoid check patch warning. Signed-off-by: Franklin S Cooper Jr Signed-off-by: Dmitry Torokhov --- Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt index 76db96704a60..9330d4d34cb9 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt @@ -50,6 +50,6 @@ Example: pinctrl-0 = <&edt_ft5x06_pins>; interrupt-parent = <&gpio2>; interrupts = <5 0>; - reset-gpios = <&gpio2 6 1>; - wake-gpios = <&gpio4 9 0>; + reset-gpios = <&gpio2 6 GPIO_ACTIVE_LOW>; + wake-gpios = <&gpio4 9 GPIO_ACTIVE_HIGH>; }; -- cgit v1.2.3 From f0bef75c593391198f2c9f855aae8e994a0e9293 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 12 Sep 2015 10:11:09 -0700 Subject: Input: edt-ft5x06 - do not hardcode interrupt trigger type Instead of hardcoding IRQ trigger type to IRQF_TRIGGER_FALLING, let's respect settings specified in device tree. To be compatible with older DTSes, if trigger type is not set up in DTS we'll set it to default (falling edge). Tested-by: Franklin S Cooper Jr Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/touchscreen/edt-ft5x06.txt | 2 +- drivers/input/touchscreen/edt-ft5x06.c | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt index 9330d4d34cb9..bedd7ddc56aa 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt @@ -49,7 +49,7 @@ Example: pinctrl-names = "default"; pinctrl-0 = <&edt_ft5x06_pins>; interrupt-parent = <&gpio2>; - interrupts = <5 0>; + interrupts = <5 IRQ_TYPE_EDGE_FALLING>; reset-gpios = <&gpio2 6 GPIO_ACTIVE_LOW>; wake-gpios = <&gpio4 9 GPIO_ACTIVE_HIGH>; }; diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index ef8a7cddb017..7239c314c9e9 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -874,6 +875,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, { struct edt_ft5x06_ts_data *tsdata; struct input_dev *input; + unsigned long irq_flags; int error; char fw_version[EDT_NAME_LEN]; @@ -959,9 +961,13 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, input_set_drvdata(input, tsdata); i2c_set_clientdata(client, tsdata); - error = devm_request_threaded_irq(&client->dev, client->irq, NULL, - edt_ft5x06_ts_isr, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + irq_flags = irq_get_trigger_type(client->irq); + if (irq_flags == IRQF_TRIGGER_NONE) + irq_flags = IRQF_TRIGGER_FALLING; + irq_flags |= IRQF_ONESHOT; + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, edt_ft5x06_ts_isr, irq_flags, client->name, tsdata); if (error) { dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); -- cgit v1.2.3 From a27b5e0a78c31b57fa5cceaec4415caeca2e50dd Mon Sep 17 00:00:00 2001 From: S Twiss Date: Mon, 27 Jul 2015 22:28:40 -0700 Subject: Input: add DA9062 OnKey capability to DA9063 OnKey driver Add DA9062 OnKey support into the existing DA9063 OnKey driver component by using generic access tables for common register and bit mask definitions. The following change will add generic register and bit mask support to the DA9063 OnKey. The following alterations have been made to the DA9063 OnKey: - Addition of a da906x_chip_config structure to hold all generic registers and bitmasks for this type of OnKey component. - Addition of an struct of_device_id table for DA9063 and DA9062 defaults - Refactoring functions to use struct da9063_onkey accesses to generic registers/masks instead of using defines from registers.h - Re-work of da9063_onkey_probe() to use of_match_node() and dev_get_regmap() to provide initialisation of generic registers and masks and access to regmap Signed-off-by: Steve Twiss Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/da9062-onkey.txt | 32 +++++ drivers/input/misc/Kconfig | 8 +- drivers/input/misc/da9063_onkey.c | 129 +++++++++++++++++---- 3 files changed, 140 insertions(+), 29 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/da9062-onkey.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/da9062-onkey.txt b/Documentation/devicetree/bindings/input/da9062-onkey.txt new file mode 100644 index 000000000000..ab0e0488fe92 --- /dev/null +++ b/Documentation/devicetree/bindings/input/da9062-onkey.txt @@ -0,0 +1,32 @@ +* Dialog DA9062/63 OnKey Module + +This module is part of the DA9062/DA9063. For more details about entire +chips see Documentation/devicetree/bindings/mfd/da9062.txt and +Documentation/devicetree/bindings/mfd/da9063.txt + +This module provides KEY_POWER, KEY_SLEEP and events. + +Required properties: + +- compatible: should be one of: + dlg,da9062-onkey + dlg,da9063-onkey + +Optional properties: + + - dlg,disable-key-power : Disable power-down using a long key-press. If this + entry exists the OnKey driver will remove support for the KEY_POWER key + press. If this entry does not exist then by default the key-press + triggered power down is enabled and the OnKey will support both KEY_POWER + and KEY_SLEEP. + +Example: + + pmic0: da9062@58 { + + onkey { + compatible = "dlg,da9063-onkey"; + dlg,disable-key-power; + }; + + }; diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 906dd1b25e41..d1ef61c2decd 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -599,11 +599,11 @@ config INPUT_DA9055_ONKEY will be called da9055_onkey. config INPUT_DA9063_ONKEY - tristate "Dialog DA9063 OnKey" - depends on MFD_DA9063 + tristate "Dialog DA9062/63 OnKey" + depends on MFD_DA9063 || MFD_DA9062 help - Support the ONKEY of Dialog DA9063 Power Management IC as an - input device reporting power button statue. + Support the ONKEY of Dialog DA9063 and DA9062 Power Management ICs + as an input device capable of reporting the power button status. To compile this driver as a module, choose M here: the module will be called da9063_onkey. diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c index f577585ef999..8eb697db82d0 100644 --- a/drivers/input/misc/da9063_onkey.c +++ b/drivers/input/misc/da9063_onkey.c @@ -1,5 +1,5 @@ /* - * OnKey device driver for DA9063 + * OnKey device driver for DA9063 and DA9062 PMICs * Copyright (C) 2015 Dialog Semiconductor Ltd. * * This program is free software; you can redistribute it and/or @@ -24,36 +24,96 @@ #include #include #include +#include +#include + +struct da906x_chip_config { + /* REGS */ + int onkey_status; + int onkey_pwr_signalling; + int onkey_fault_log; + int onkey_shutdown; + /* MASKS */ + int onkey_nonkey_mask; + int onkey_nonkey_lock_mask; + int onkey_key_reset_mask; + int onkey_shutdown_mask; + /* NAMES */ + const char *name; +}; struct da9063_onkey { - struct da9063 *hw; struct delayed_work work; struct input_dev *input; struct device *dev; + struct regmap *regmap; + const struct da906x_chip_config *config; + char phys[32]; bool key_power; }; +static const struct da906x_chip_config da9063_regs = { + /* REGS */ + .onkey_status = DA9063_REG_STATUS_A, + .onkey_pwr_signalling = DA9063_REG_CONTROL_B, + .onkey_fault_log = DA9063_REG_FAULT_LOG, + .onkey_shutdown = DA9063_REG_CONTROL_F, + /* MASKS */ + .onkey_nonkey_mask = DA9063_NONKEY, + .onkey_nonkey_lock_mask = DA9063_NONKEY_LOCK, + .onkey_key_reset_mask = DA9063_KEY_RESET, + .onkey_shutdown_mask = DA9063_SHUTDOWN, + /* NAMES */ + .name = DA9063_DRVNAME_ONKEY, +}; + +static const struct da906x_chip_config da9062_regs = { + /* REGS */ + .onkey_status = DA9062AA_STATUS_A, + .onkey_pwr_signalling = DA9062AA_CONTROL_B, + .onkey_fault_log = DA9062AA_FAULT_LOG, + .onkey_shutdown = DA9062AA_CONTROL_F, + /* MASKS */ + .onkey_nonkey_mask = DA9062AA_NONKEY_MASK, + .onkey_nonkey_lock_mask = DA9062AA_NONKEY_LOCK_MASK, + .onkey_key_reset_mask = DA9062AA_KEY_RESET_MASK, + .onkey_shutdown_mask = DA9062AA_SHUTDOWN_MASK, + /* NAMES */ + .name = "da9062-onkey", +}; + +static const struct of_device_id da9063_compatible_reg_id_table[] = { + { .compatible = "dlg,da9063-onkey", .data = &da9063_regs }, + { .compatible = "dlg,da9062-onkey", .data = &da9062_regs }, + { }, +}; + static void da9063_poll_on(struct work_struct *work) { - struct da9063_onkey *onkey = container_of(work, struct da9063_onkey, - work.work); + struct da9063_onkey *onkey = container_of(work, + struct da9063_onkey, + work.work); + const struct da906x_chip_config *config = onkey->config; unsigned int val; int fault_log = 0; bool poll = true; int error; /* Poll to see when the pin is released */ - error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val); + error = regmap_read(onkey->regmap, + config->onkey_status, + &val); if (error) { dev_err(onkey->dev, "Failed to read ON status: %d\n", error); goto err_poll; } - if (!(val & DA9063_NONKEY)) { - error = regmap_update_bits(onkey->hw->regmap, - DA9063_REG_CONTROL_B, - DA9063_NONKEY_LOCK, 0); + if (!(val & config->onkey_nonkey_mask)) { + error = regmap_update_bits(onkey->regmap, + config->onkey_pwr_signalling, + config->onkey_nonkey_lock_mask, + 0); if (error) { dev_err(onkey->dev, "Failed to reset the Key Delay %d\n", error); @@ -70,15 +130,16 @@ static void da9063_poll_on(struct work_struct *work) * If the fault log KEY_RESET is detected, then clear it * and shut down the system. */ - error = regmap_read(onkey->hw->regmap, - DA9063_REG_FAULT_LOG, &fault_log); + error = regmap_read(onkey->regmap, + config->onkey_fault_log, + &fault_log); if (error) { dev_warn(&onkey->input->dev, "Cannot read FAULT_LOG: %d\n", error); - } else if (fault_log & DA9063_KEY_RESET) { - error = regmap_write(onkey->hw->regmap, - DA9063_REG_FAULT_LOG, - DA9063_KEY_RESET); + } else if (fault_log & config->onkey_key_reset_mask) { + error = regmap_write(onkey->regmap, + config->onkey_fault_log, + config->onkey_key_reset_mask); if (error) { dev_warn(&onkey->input->dev, "Cannot reset KEY_RESET fault log: %d\n", @@ -88,10 +149,10 @@ static void da9063_poll_on(struct work_struct *work) * and then send shutdown command */ dev_dbg(&onkey->input->dev, - "Sending SHUTDOWN to DA9063 ...\n"); - error = regmap_write(onkey->hw->regmap, - DA9063_REG_CONTROL_F, - DA9063_SHUTDOWN); + "Sending SHUTDOWN to DA9063 ...\n"); + error = regmap_write(onkey->regmap, + config->onkey_shutdown, + config->onkey_shutdown_mask); if (error) dev_err(&onkey->input->dev, "Cannot SHUTDOWN DA9063: %d\n", @@ -107,11 +168,14 @@ err_poll: static irqreturn_t da9063_onkey_irq_handler(int irq, void *data) { struct da9063_onkey *onkey = data; + const struct da906x_chip_config *config = onkey->config; unsigned int val; int error; - error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val); - if (onkey->key_power && !error && (val & DA9063_NONKEY)) { + error = regmap_read(onkey->regmap, + config->onkey_status, + &val); + if (onkey->key_power && !error && (val & config->onkey_nonkey_mask)) { input_report_key(onkey->input, KEY_POWER, 1); input_sync(onkey->input); schedule_delayed_work(&onkey->work, 0); @@ -139,9 +203,15 @@ static int da9063_onkey_probe(struct platform_device *pdev) struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent); struct da9063_pdata *pdata = dev_get_platdata(da9063->dev); struct da9063_onkey *onkey; + const struct of_device_id *match; int irq; int error; + match = of_match_node(da9063_compatible_reg_id_table, + pdev->dev.of_node); + if (!match) + return -ENXIO; + onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9063_onkey), GFP_KERNEL); if (!onkey) { @@ -149,8 +219,14 @@ static int da9063_onkey_probe(struct platform_device *pdev) return -ENOMEM; } + onkey->config = match->data; onkey->dev = &pdev->dev; - onkey->hw = da9063; + + onkey->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!onkey->regmap) { + dev_err(&pdev->dev, "Parent regmap unavailable.\n"); + return -ENXIO; + } if (pdata) onkey->key_power = pdata->key_power; @@ -165,8 +241,10 @@ static int da9063_onkey_probe(struct platform_device *pdev) return -ENOMEM; } - onkey->input->name = DA9063_DRVNAME_ONKEY; - onkey->input->phys = DA9063_DRVNAME_ONKEY "/input0"; + onkey->input->name = onkey->config->name; + snprintf(onkey->phys, sizeof(onkey->phys), "%s/input0", + onkey->config->name); + onkey->input->phys = onkey->phys; onkey->input->dev.parent = &pdev->dev; if (onkey->key_power) @@ -216,11 +294,12 @@ static struct platform_driver da9063_onkey_driver = { .probe = da9063_onkey_probe, .driver = { .name = DA9063_DRVNAME_ONKEY, + .of_match_table = da9063_compatible_reg_id_table, }, }; module_platform_driver(da9063_onkey_driver); MODULE_AUTHOR("S Twiss "); -MODULE_DESCRIPTION("Onkey device driver for Dialog DA9063"); +MODULE_DESCRIPTION("Onkey device driver for Dialog DA9063 and DA9062"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DA9063_DRVNAME_ONKEY); -- cgit v1.2.3 From 92deea1350f9fa97a841d45cd1f8228d5e8aa667 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Fri, 2 Oct 2015 11:30:11 -0700 Subject: Input: add support for FocalTech FT6236 touchscreen controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for the FT6x06 and the FT6x36 family of capacitive touch panel controllers, in particular the FT6236. Signed-off-by: Noralf Trønnes Signed-off-by: Dmitry Torokhov --- .../input/touchscreen/focaltech-ft6236.txt | 35 +++ .../devicetree/bindings/vendor-prefixes.txt | 1 + drivers/input/touchscreen/Kconfig | 13 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/ft6236.c | 327 +++++++++++++++++++++ 5 files changed, 377 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/focaltech-ft6236.txt create mode 100644 drivers/input/touchscreen/ft6236.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/touchscreen/focaltech-ft6236.txt b/Documentation/devicetree/bindings/input/touchscreen/focaltech-ft6236.txt new file mode 100644 index 000000000000..777521da3da5 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/focaltech-ft6236.txt @@ -0,0 +1,35 @@ +* FocalTech FT6236 I2C touchscreen controller + +Required properties: + - compatible : "focaltech,ft6236" + - reg : I2C slave address of the chip (0x38) + - interrupt-parent : a phandle pointing to the interrupt controller + serving the interrupt for this chip + - interrupts : interrupt specification for the touch controller + interrupt + - reset-gpios : GPIO specification for the RSTN input + - touchscreen-size-x : horizontal resolution of touchscreen (in pixels) + - touchscreen-size-y : vertical resolution of touchscreen (in pixels) + +Optional properties: + - touchscreen-fuzz-x : horizontal noise value of the absolute input + device (in pixels) + - touchscreen-fuzz-y : vertical noise value of the absolute input + device (in pixels) + - touchscreen-inverted-x : X axis is inverted (boolean) + - touchscreen-inverted-y : Y axis is inverted (boolean) + - touchscreen-swapped-x-y: X and Y axis are swapped (boolean) + Swapping is done after inverting the axis + +Example: + + ft6x06@38 { + compatible = "focaltech,ft6236"; + reg = <0x38>; + interrupt-parent = <&gpio>; + interrupts = <23 2>; + touchscreen-size-x = <320>; + touchscreen-size-y = <480>; + touchscreen-inverted-x; + touchscreen-swapped-x-y; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 82d2ac97af74..3222b2ff475e 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -82,6 +82,7 @@ everspin Everspin Technologies, Inc. excito Excito fcs Fairchild Semiconductor firefly Firefly +focaltech FocalTech Systems Co.,Ltd fsl Freescale Semiconductor GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc. gef GE Fanuc Intelligent Platforms Embedded Systems, Inc. diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 600dcceff542..eda89b6b7e11 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -295,6 +295,19 @@ config TOUCHSCREEN_EGALAX To compile this driver as a module, choose M here: the module will be called egalax_ts. +config TOUCHSCREEN_FT6236 + tristate "FT6236 I2C touchscreen" + depends on I2C + depends on GPIOLIB || COMPILE_TEST + help + Say Y here to enable support for the I2C connected FT6x06 and + FT6x36 family of capacitive touchscreen drivers. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ft6236. + config TOUCHSCREEN_FUJITSU tristate "Fujitsu serial touchscreen" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 1b79cc09744a..baba189e3e16 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o +obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o diff --git a/drivers/input/touchscreen/ft6236.c b/drivers/input/touchscreen/ft6236.c new file mode 100644 index 000000000000..cccae193668e --- /dev/null +++ b/drivers/input/touchscreen/ft6236.c @@ -0,0 +1,327 @@ +/* + * FocalTech FT6236 TouchScreen driver. + * + * Copyright (c) 2010 Focal tech Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define FT6236_MAX_TOUCH_POINTS 2 + +#define FT6236_REG_TH_GROUP 0x80 +#define FT6236_REG_PERIODACTIVE 0x88 +#define FT6236_REG_LIB_VER_H 0xa1 +#define FT6236_REG_LIB_VER_L 0xa2 +#define FT6236_REG_CIPHER 0xa3 +#define FT6236_REG_FIRMID 0xa6 +#define FT6236_REG_FOCALTECH_ID 0xa8 +#define FT6236_REG_RELEASE_CODE_ID 0xaf + +#define FT6236_EVENT_PRESS_DOWN 0 +#define FT6236_EVENT_LIFT_UP 1 +#define FT6236_EVENT_CONTACT 2 +#define FT6236_EVENT_NO_EVENT 3 + +struct ft6236_data { + struct i2c_client *client; + struct input_dev *input; + struct gpio_desc *reset_gpio; + u32 max_x; + u32 max_y; + bool invert_x; + bool invert_y; + bool swap_xy; +}; + +/* + * This struct is a touchpoint as stored in hardware. Note that the id, + * as well as the event, are stored in the upper nybble of the hi byte. + */ +struct ft6236_touchpoint { + union { + u8 xhi; + u8 event; + }; + u8 xlo; + union { + u8 yhi; + u8 id; + }; + u8 ylo; + u8 weight; + u8 misc; +} __packed; + +/* This packet represents the register map as read from offset 0 */ +struct ft6236_packet { + u8 dev_mode; + u8 gest_id; + u8 touches; + struct ft6236_touchpoint points[FT6236_MAX_TOUCH_POINTS]; +} __packed; + +static int ft6236_read(struct i2c_client *client, u8 reg, u8 len, void *data) +{ + int error; + + error = i2c_smbus_read_i2c_block_data(client, reg, len, data); + if (error < 0) + return error; + + if (error != len) + return -EIO; + + return 0; +} + +static irqreturn_t ft6236_interrupt(int irq, void *dev_id) +{ + struct ft6236_data *ft6236 = dev_id; + struct device *dev = &ft6236->client->dev; + struct input_dev *input = ft6236->input; + struct ft6236_packet buf; + u8 touches; + int i, error; + + error = ft6236_read(ft6236->client, 0, sizeof(buf), &buf); + if (error) { + dev_err(dev, "read touchdata failed %d\n", error); + return IRQ_HANDLED; + } + + touches = buf.touches & 0xf; + if (touches > FT6236_MAX_TOUCH_POINTS) { + dev_dbg(dev, + "%d touch points reported, only %d are supported\n", + touches, FT6236_MAX_TOUCH_POINTS); + touches = FT6236_MAX_TOUCH_POINTS; + } + + for (i = 0; i < touches; i++) { + struct ft6236_touchpoint *point = &buf.points[i]; + u16 x = ((point->xhi & 0xf) << 8) | buf.points[i].xlo; + u16 y = ((point->yhi & 0xf) << 8) | buf.points[i].ylo; + u8 event = point->event >> 6; + u8 id = point->id >> 4; + bool act = (event == FT6236_EVENT_PRESS_DOWN || + event == FT6236_EVENT_CONTACT); + + input_mt_slot(input, id); + input_mt_report_slot_state(input, MT_TOOL_FINGER, act); + if (!act) + continue; + + if (ft6236->invert_x) + x = ft6236->max_x - x; + + if (ft6236->invert_y) + y = ft6236->max_y - y; + + if (ft6236->swap_xy) { + input_report_abs(input, ABS_MT_POSITION_X, y); + input_report_abs(input, ABS_MT_POSITION_Y, x); + } else { + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + } + } + + input_mt_sync_frame(input); + input_sync(input); + + return IRQ_HANDLED; +} + +static u8 ft6236_debug_read_byte(struct ft6236_data *ft6236, u8 reg) +{ + struct i2c_client *client = ft6236->client; + u8 val = 0; + int error; + + error = ft6236_read(client, reg, 1, &val); + if (error) + dev_dbg(&client->dev, + "error reading register 0x%02x: %d\n", reg, error); + + return val; +} + +static void ft6236_debug_info(struct ft6236_data *ft6236) +{ + struct device *dev = &ft6236->client->dev; + + dev_dbg(dev, "Touch threshold is %d\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_TH_GROUP) * 4); + dev_dbg(dev, "Report rate is %dHz\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_PERIODACTIVE) * 10); + dev_dbg(dev, "Firmware library version 0x%02x%02x\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_H), + ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_L)); + dev_dbg(dev, "Firmware version 0x%02x\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_FIRMID)); + dev_dbg(dev, "Chip vendor ID 0x%02x\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_CIPHER)); + dev_dbg(dev, "CTPM vendor ID 0x%02x\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_FOCALTECH_ID)); + dev_dbg(dev, "Release code version 0x%02x\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_RELEASE_CODE_ID)); +} + +static void ft6236_reset(struct ft6236_data *ft6236) +{ + if (!ft6236->reset_gpio) + return; + + gpiod_set_value_cansleep(ft6236->reset_gpio, 1); + usleep_range(5000, 20000); + gpiod_set_value_cansleep(ft6236->reset_gpio, 0); + msleep(300); +} + +static int ft6236_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct ft6236_data *ft6236; + struct input_dev *input; + u32 fuzz_x = 0, fuzz_y = 0; + u8 val; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENXIO; + + if (!client->irq) { + dev_err(dev, "irq is missing\n"); + return -EINVAL; + } + + ft6236 = devm_kzalloc(dev, sizeof(*ft6236), GFP_KERNEL); + if (!ft6236) + return -ENOMEM; + + ft6236->client = client; + ft6236->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(ft6236->reset_gpio)) { + error = PTR_ERR(ft6236->reset_gpio); + if (error != -EPROBE_DEFER) + dev_err(dev, "error getting reset gpio: %d\n", error); + return error; + } + + ft6236_reset(ft6236); + + /* verify that the controller is present */ + error = ft6236_read(client, 0x00, 1, &val); + if (error) { + dev_err(dev, "failed to read from controller: %d\n", error); + return error; + } + + ft6236_debug_info(ft6236); + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + ft6236->input = input; + input->name = client->name; + input->id.bustype = BUS_I2C; + + if (device_property_read_u32(dev, "touchscreen-size-x", + &ft6236->max_x) || + device_property_read_u32(dev, "touchscreen-size-y", + &ft6236->max_y)) { + dev_err(dev, "touchscreen-size-x and/or -y missing\n"); + return -EINVAL; + } + + device_property_read_u32(dev, "touchscreen-fuzz-x", &fuzz_x); + device_property_read_u32(dev, "touchscreen-fuzz-y", &fuzz_y); + ft6236->invert_x = device_property_read_bool(dev, + "touchscreen-inverted-x"); + ft6236->invert_y = device_property_read_bool(dev, + "touchscreen-inverted-y"); + ft6236->swap_xy = device_property_read_bool(dev, + "touchscreen-swapped-x-y"); + + if (ft6236->swap_xy) { + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + ft6236->max_y, fuzz_y, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + ft6236->max_x, fuzz_x, 0); + } else { + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + ft6236->max_x, fuzz_x, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + ft6236->max_y, fuzz_y, 0); + } + + error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) + return error; + + error = devm_request_threaded_irq(dev, client->irq, NULL, + ft6236_interrupt, IRQF_ONESHOT, + client->name, ft6236); + if (error) { + dev_err(dev, "request irq %d failed: %d\n", client->irq, error); + return error; + } + + error = input_register_device(input); + if (error) { + dev_err(dev, "failed to register input device: %d\n", error); + return error; + } + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id ft6236_of_match[] = { + { .compatible = "focaltech,ft6236", }, + { } +}; +MODULE_DEVICE_TABLE(of, ft6236_of_match); +#endif + +static const struct i2c_device_id ft6236_id[] = { + { "ft6236", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ft6236_id); + +static struct i2c_driver ft6236_driver = { + .driver = { + .name = "ft6236", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ft6236_of_match), + }, + .probe = ft6236_probe, + .id_table = ft6236_id, +}; +module_i2c_driver(ft6236_driver); + +MODULE_AUTHOR("Sean Cross "); +MODULE_AUTHOR("Noralf Trønnes "); +MODULE_DESCRIPTION("FocalTech FT6236 TouchScreen driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 47ec6e5a5f57f96d7d382e2d9f8dc5a5bdb45259 Mon Sep 17 00:00:00 2001 From: Sylvain Rochet Date: Tue, 13 Oct 2015 23:24:36 -0700 Subject: Input: rotary_encoder - add wake up support This patch adds wake up support to GPIO rotary encoders. Signed-off-by: Sylvain Rochet Reviewed-by: Johan Hovold Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/rotary-encoder.txt | 1 + Documentation/input/rotary-encoder.txt | 1 + drivers/input/misc/rotary_encoder.c | 37 ++++++++++++++++++++++ include/linux/rotary_encoder.h | 1 + 4 files changed, 40 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/rotary-encoder.txt b/Documentation/devicetree/bindings/input/rotary-encoder.txt index 331549593ed5..891ddba2d792 100644 --- a/Documentation/devicetree/bindings/input/rotary-encoder.txt +++ b/Documentation/devicetree/bindings/input/rotary-encoder.txt @@ -15,6 +15,7 @@ Optional properties: - rotary-encoder,rollover: Automatic rollove when the rotary value becomes greater than the specified steps or smaller than 0. For absolute axis only. - rotary-encoder,half-period: Makes the driver work on half-period mode. +- wakeup-source: Boolean, rotary encoder can wake up the system. See Documentation/input/rotary-encoder.txt for more information. diff --git a/Documentation/input/rotary-encoder.txt b/Documentation/input/rotary-encoder.txt index 5737e3590adb..bddbee188624 100644 --- a/Documentation/input/rotary-encoder.txt +++ b/Documentation/input/rotary-encoder.txt @@ -109,6 +109,7 @@ static struct rotary_encoder_platform_data my_rotary_encoder_info = { .inverted_a = 0, .inverted_b = 0, .half_period = false, + .wakeup_source = false, }; static struct platform_device rotary_encoder_device = { diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index f27f81ee84ed..d1665544e109 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -26,6 +26,7 @@ #include #include #include +#include #define DRV_NAME "rotary-encoder" @@ -180,6 +181,8 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic "rotary-encoder,rollover", NULL); pdata->half_period = !!of_get_property(np, "rotary-encoder,half-period", NULL); + pdata->wakeup_source = !!of_get_property(np, + "wakeup-source", NULL); return pdata; } @@ -280,6 +283,8 @@ static int rotary_encoder_probe(struct platform_device *pdev) goto exit_free_irq_b; } + device_init_wakeup(&pdev->dev, pdata->wakeup_source); + platform_set_drvdata(pdev, encoder); return 0; @@ -306,6 +311,8 @@ static int rotary_encoder_remove(struct platform_device *pdev) struct rotary_encoder *encoder = platform_get_drvdata(pdev); const struct rotary_encoder_platform_data *pdata = encoder->pdata; + device_init_wakeup(&pdev->dev, false); + free_irq(encoder->irq_a, encoder); free_irq(encoder->irq_b, encoder); gpio_free(pdata->gpio_a); @@ -320,11 +327,41 @@ static int rotary_encoder_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int rotary_encoder_suspend(struct device *dev) +{ + struct rotary_encoder *encoder = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) { + enable_irq_wake(encoder->irq_a); + enable_irq_wake(encoder->irq_b); + } + + return 0; +} + +static int rotary_encoder_resume(struct device *dev) +{ + struct rotary_encoder *encoder = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) { + disable_irq_wake(encoder->irq_a); + disable_irq_wake(encoder->irq_b); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops, + rotary_encoder_suspend, rotary_encoder_resume); + static struct platform_driver rotary_encoder_driver = { .probe = rotary_encoder_probe, .remove = rotary_encoder_remove, .driver = { .name = DRV_NAME, + .pm = &rotary_encoder_pm_ops, .of_match_table = of_match_ptr(rotary_encoder_of_match), } }; diff --git a/include/linux/rotary_encoder.h b/include/linux/rotary_encoder.h index 3f594dce5716..b33f2d2a708f 100644 --- a/include/linux/rotary_encoder.h +++ b/include/linux/rotary_encoder.h @@ -11,6 +11,7 @@ struct rotary_encoder_platform_data { bool relative_axis; bool rollover; bool half_period; + bool wakeup_source; }; #endif /* __ROTARY_ENCODER_H__ */ -- cgit v1.2.3 From 6f29d3b2777ae32e995d45247c8234d83894b953 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 14 Oct 2015 16:44:35 -0700 Subject: Input: gpio_keys_polled - add support for abs/rel axis This changAdd support for EV_ABS / EV_REL events to the gpio-keys-polled driver. The driver already allows specifying what type of events (key / rel / abs) a button generates when pressed, but for rel / abs axis we also need to specify which value this specific gpio represents. One use case is digital joysticks / direction-pads which are hooked up to gpio, in this case we've left and right buttons which we want to map to EV_ABS, ABS_X and we want generate events for left with a value of -1 and for right with a value of +1 (and similar for up / down and ABS_Y). Signed-off-by: Hans de Goede Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/gpio-keys-polled.txt | 9 ++- drivers/input/keyboard/gpio_keys_polled.c | 88 +++++++++++++++++++--- 2 files changed, 87 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/gpio-keys-polled.txt b/Documentation/devicetree/bindings/input/gpio-keys-polled.txt index 5b91f5a3bd5c..97e2467181e9 100644 --- a/Documentation/devicetree/bindings/input/gpio-keys-polled.txt +++ b/Documentation/devicetree/bindings/input/gpio-keys-polled.txt @@ -13,11 +13,18 @@ Subnode properties: - gpios: OF device-tree gpio specification. - label: Descriptive name of the key. - - linux,code: Keycode to emit. + - linux,code: Key / Axis code to emit. Optional subnode-properties: - linux,input-type: Specify event type this button/key generates. If not specified defaults to <1> == EV_KEY. + - linux,input-value: If linux,input-type is EV_ABS or EV_REL then this + value is sent for events this button generates when pressed. + EV_ABS/EV_REL axis will generate an event with a value of 0 when + all buttons with linux,input-type == type and linux,code == axis + are released. This value is interpreted as a signed 32 bit value, + e.g. to make a button generate a value of -1 use: + linux,input-value = <0xffffffff>; /* -1 */ - debounce-interval: Debouncing interval time in milliseconds. If not specified defaults to 5. - wakeup-source: Boolean, button can wake-up the system. diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 870cfa6e2c44..62bdb1d48c49 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -40,10 +40,36 @@ struct gpio_keys_polled_dev { struct input_polled_dev *poll_dev; struct device *dev; const struct gpio_keys_platform_data *pdata; + unsigned long rel_axis_seen[BITS_TO_LONGS(REL_CNT)]; + unsigned long abs_axis_seen[BITS_TO_LONGS(ABS_CNT)]; struct gpio_keys_button_data data[0]; }; -static void gpio_keys_polled_check_state(struct input_dev *input, +static void gpio_keys_button_event(struct input_polled_dev *dev, + struct gpio_keys_button *button, + int state) +{ + struct gpio_keys_polled_dev *bdev = dev->private; + struct input_dev *input = dev->input; + unsigned int type = button->type ?: EV_KEY; + + if (type == EV_REL) { + if (state) { + input_event(input, type, button->code, button->value); + __set_bit(button->code, bdev->rel_axis_seen); + } + } else if (type == EV_ABS) { + if (state) { + input_event(input, type, button->code, button->value); + __set_bit(button->code, bdev->abs_axis_seen); + } + } else { + input_event(input, type, button->code, state); + input_sync(input); + } +} + +static void gpio_keys_polled_check_state(struct input_polled_dev *dev, struct gpio_keys_button *button, struct gpio_keys_button_data *bdata) { @@ -54,11 +80,9 @@ static void gpio_keys_polled_check_state(struct input_dev *input, else state = !!gpiod_get_value(button->gpiod); - if (state != bdata->last_state) { - unsigned int type = button->type ?: EV_KEY; + gpio_keys_button_event(dev, button, state); - input_event(input, type, button->code, state); - input_sync(input); + if (state != bdata->last_state) { bdata->count = 0; bdata->last_state = state; } @@ -71,15 +95,33 @@ static void gpio_keys_polled_poll(struct input_polled_dev *dev) struct input_dev *input = dev->input; int i; + memset(bdev->rel_axis_seen, 0, sizeof(bdev->rel_axis_seen)); + memset(bdev->abs_axis_seen, 0, sizeof(bdev->abs_axis_seen)); + for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button_data *bdata = &bdev->data[i]; - if (bdata->count < bdata->threshold) + if (bdata->count < bdata->threshold) { bdata->count++; - else - gpio_keys_polled_check_state(input, &pdata->buttons[i], + gpio_keys_button_event(dev, &pdata->buttons[i], + bdata->last_state); + } else { + gpio_keys_polled_check_state(dev, &pdata->buttons[i], bdata); + } + } + + for_each_set_bit(i, input->relbit, REL_CNT) { + if (!test_bit(i, bdev->rel_axis_seen)) + input_event(input, EV_REL, i, 0); + } + + for_each_set_bit(i, input->absbit, ABS_CNT) { + if (!test_bit(i, bdev->abs_axis_seen)) + input_event(input, EV_ABS, i, 0); } + + input_sync(input); } static void gpio_keys_polled_open(struct input_polled_dev *dev) @@ -152,6 +194,10 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct &button->type)) button->type = EV_KEY; + if (fwnode_property_read_u32(child, "linux,input-value", + (u32 *)&button->value)) + button->value = 1; + button->wakeup = fwnode_property_read_bool(child, "wakeup-source") || /* legacy name */ @@ -168,6 +214,25 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct return pdata; } +static void gpio_keys_polled_set_abs_params(struct input_dev *input, + const struct gpio_keys_platform_data *pdata, unsigned int code) +{ + int i, min = 0, max = 0; + + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + + if (button->type != EV_ABS || button->code != code) + continue; + + if (button->value < min) + min = button->value; + if (button->value > max) + max = button->value; + } + input_set_abs_params(input, code, min, max, 0, 0); +} + static const struct of_device_id gpio_keys_polled_of_match[] = { { .compatible = "gpio-keys-polled", }, { }, @@ -274,6 +339,9 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) pdata->poll_interval); input_set_capability(input, type, button->code); + if (type == EV_ABS) + gpio_keys_polled_set_abs_params(input, pdata, + button->code); } bdev->poll_dev = poll_dev; @@ -290,9 +358,11 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) /* report initial state of the buttons */ for (i = 0; i < pdata->nbuttons; i++) - gpio_keys_polled_check_state(input, &pdata->buttons[i], + gpio_keys_polled_check_state(poll_dev, &pdata->buttons[i], &bdev->data[i]); + input_sync(input); + return 0; } -- cgit v1.2.3 From 3a341a4c30d427fd05617087db1564a595f65093 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 13 Oct 2015 23:39:50 -0700 Subject: Input: rotary-encoder - add support for quarter-period mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some encoders have both outputs low in stable states, others also have a stable state with both outputs high (half-period mode) and some have a stable state in all steps (quarter-period mode). The driver used to support the former states and with this change it can also support the later. This commit also deprecates the 'half-period' property and introduces a new property 'steps-per-period'. This property specifies the number of steps (stable states) produced by the rotary encoder for each GPIO period. Signed-off-by: Guido Martínez Signed-off-by: Ezequiel Garcia Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/rotary-encoder.txt | 11 ++- Documentation/input/rotary-encoder.txt | 8 +- drivers/input/misc/rotary_encoder.c | 86 ++++++++++++++++++++-- include/linux/rotary_encoder.h | 2 +- 4 files changed, 98 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/rotary-encoder.txt b/Documentation/devicetree/bindings/input/rotary-encoder.txt index 891ddba2d792..de99cbbbf6da 100644 --- a/Documentation/devicetree/bindings/input/rotary-encoder.txt +++ b/Documentation/devicetree/bindings/input/rotary-encoder.txt @@ -14,9 +14,18 @@ Optional properties: device, hence no steps need to be passed. - rotary-encoder,rollover: Automatic rollove when the rotary value becomes greater than the specified steps or smaller than 0. For absolute axis only. -- rotary-encoder,half-period: Makes the driver work on half-period mode. +- rotary-encoder,steps-per-period: Number of steps (stable states) per period. + The values have the following meaning: + 1: Full-period mode (default) + 2: Half-period mode + 4: Quarter-period mode - wakeup-source: Boolean, rotary encoder can wake up the system. +Deprecated properties: +- rotary-encoder,half-period: Makes the driver work on half-period mode. + This property is deprecated. Instead, a 'steps-per-period ' value should + be used, such as "rotary-encoder,steps-per-period = <2>". + See Documentation/input/rotary-encoder.txt for more information. Example: diff --git a/Documentation/input/rotary-encoder.txt b/Documentation/input/rotary-encoder.txt index bddbee188624..46a74f0c551a 100644 --- a/Documentation/input/rotary-encoder.txt +++ b/Documentation/input/rotary-encoder.txt @@ -9,8 +9,9 @@ peripherals with two wires. The outputs are phase-shifted by 90 degrees and by triggering on falling and rising edges, the turn direction can be determined. -Some encoders have both outputs low in stable states, whereas others also have -a stable state with both outputs high (half-period mode). +Some encoders have both outputs low in stable states, others also have +a stable state with both outputs high (half-period mode) and some have +a stable state in all steps (quarter-period mode). The phase diagram of these two outputs look like this: @@ -32,6 +33,9 @@ The phase diagram of these two outputs look like this: |<-->| one step (half-period mode) + |<>| + one step (quarter-period mode) + For more information, please see https://en.wikipedia.org/wiki/Rotary_encoder diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 962f9e86310b..8aee71986430 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -143,6 +143,55 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id) +{ + struct rotary_encoder *encoder = dev_id; + unsigned char sum; + int state; + + state = rotary_encoder_get_state(encoder->pdata); + + /* + * We encode the previous and the current state using a byte. + * The previous state in the MSB nibble, the current state in the LSB + * nibble. Then use a table to decide the direction of the turn. + */ + sum = (encoder->last_stable << 4) + state; + switch (sum) { + case 0x31: + case 0x10: + case 0x02: + case 0x23: + encoder->dir = 0; /* clockwise */ + break; + + case 0x13: + case 0x01: + case 0x20: + case 0x32: + encoder->dir = 1; /* counter-clockwise */ + break; + + default: + /* + * Ignore all other values. This covers the case when the + * state didn't change (a spurious interrupt) and the + * cases where the state changed by two steps, making it + * impossible to tell the direction. + * + * In either case, don't report any event and save the + * state for later. + */ + goto out; + } + + rotary_encoder_report_event(encoder); + +out: + encoder->last_stable = state; + return IRQ_HANDLED; +} + #ifdef CONFIG_OF static const struct of_device_id rotary_encoder_of_match[] = { { .compatible = "rotary-encoder", }, @@ -157,6 +206,7 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic struct device_node *np = dev->of_node; struct rotary_encoder_platform_data *pdata; enum of_gpio_flags flags; + int error; if (!of_id || !np) return NULL; @@ -178,8 +228,23 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic pdata->relative_axis = of_property_read_bool(np, "rotary-encoder,relative-axis"); pdata->rollover = of_property_read_bool(np, "rotary-encoder,rollover"); - pdata->half_period = - of_property_read_bool(np, "rotary-encoder,half-period"); + + error = of_property_read_u32(np, "rotary-encoder,steps-per-period", + &pdata->steps_per_period); + if (error) { + /* + * The 'half-period' property has been deprecated, you must use + * 'steps-per-period' and set an appropriate value, but we still + * need to parse it to maintain compatibility. + */ + if (of_property_read_bool(np, "rotary-encoder,half-period")) { + pdata->steps_per_period = 2; + } else { + /* Fallback to one step per period behavior */ + pdata->steps_per_period = 1; + } + } + pdata->wakeup_source = of_property_read_bool(np, "wakeup-source"); return pdata; @@ -251,12 +316,23 @@ static int rotary_encoder_probe(struct platform_device *pdev) encoder->irq_a = gpio_to_irq(pdata->gpio_a); encoder->irq_b = gpio_to_irq(pdata->gpio_b); - /* request the IRQs */ - if (pdata->half_period) { + switch (pdata->steps_per_period) { + case 4: + handler = &rotary_encoder_quarter_period_irq; + encoder->last_stable = rotary_encoder_get_state(pdata); + break; + case 2: handler = &rotary_encoder_half_period_irq; encoder->last_stable = rotary_encoder_get_state(pdata); - } else { + break; + case 1: handler = &rotary_encoder_irq; + break; + default: + dev_err(dev, "'%d' is not a valid steps-per-period value\n", + pdata->steps_per_period); + err = -EINVAL; + goto exit_free_gpio_b; } err = request_irq(encoder->irq_a, handler, diff --git a/include/linux/rotary_encoder.h b/include/linux/rotary_encoder.h index b33f2d2a708f..fe3dc64e5aeb 100644 --- a/include/linux/rotary_encoder.h +++ b/include/linux/rotary_encoder.h @@ -8,9 +8,9 @@ struct rotary_encoder_platform_data { unsigned int gpio_b; unsigned int inverted_a; unsigned int inverted_b; + unsigned int steps_per_period; bool relative_axis; bool rollover; - bool half_period; bool wakeup_source; }; -- cgit v1.2.3 From af33e0ad1bf6e065f05a68f177b99202935ed2cf Mon Sep 17 00:00:00 2001 From: Franklin S Cooper Jr Date: Fri, 16 Oct 2015 15:34:26 -0700 Subject: Input: edt-ft5x06 - add support for FT5506 FT5506 is essentially the same as other FT5x06 devices other than supporting 10 support points. Signed-off-by: Franklin S Cooper Jr Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/touchscreen/edt-ft5x06.txt | 2 ++ drivers/input/touchscreen/edt-ft5x06.c | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt index bedd7ddc56aa..f99528da1b1d 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt @@ -5,6 +5,7 @@ There are 3 variants of the chip for various touch panel sizes FT5206GE1 2.8" .. 3.8" FT5306DE4 4.3" .. 7" FT5406EE8 7" .. 8.9" +FT5506EEG 7" .. 8.9" The software interface is identical for all those chips, so that currently there is no need for the driver to distinguish between the @@ -17,6 +18,7 @@ Required properties: - compatible: "edt,edt-ft5206" or: "edt,edt-ft5306" or: "edt,edt-ft5406" + or: "edt,edt-ft5506" - reg: I2C slave address of the chip (0x38) - interrupt-parent: a phandle pointing to the interrupt controller diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 98766f2a8c4f..214d8a08a469 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -172,7 +172,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) struct edt_ft5x06_ts_data *tsdata = dev_id; struct device *dev = &tsdata->client->dev; u8 cmd; - u8 rdbuf[31]; + u8 rdbuf[61]; int i, type, x, y, id; int offset, tplen, datalen, crclen; int error; @@ -1052,8 +1052,13 @@ static const struct edt_i2c_chip_data edt_ft5x06_data = { .max_support_points = 5, }; +static const struct edt_i2c_chip_data edt_ft5506_data = { + .max_support_points = 10, +}; + static const struct i2c_device_id edt_ft5x06_ts_id[] = { { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data }, + { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); @@ -1063,6 +1068,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = { { .compatible = "edt,edt-ft5206", .data = &edt_ft5x06_data }, { .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data }, { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data }, + { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); -- cgit v1.2.3 From 5523662edd4fe937267053c2018b75be2ac17860 Mon Sep 17 00:00:00 2001 From: Stephen Chandler Paul Date: Sat, 24 Oct 2015 13:10:29 -0700 Subject: Input: add userio module Debugging input devices, specifically laptop touchpads, can be tricky without having the physical device handy. Here we try to remedy that with userio. This module allows an application to connect to a character device provided by the kernel, and emulate any serio device. In combination with userspace programs that can record PS/2 devices and replay them through the /dev/userio device, this allows developers to debug driver issues on the PS/2 level with devices simply by requesting a recording from the user experiencing the issue without having to have the physical hardware in front of them. Signed-off-by: Stephen Chandler Paul Reviewed-by: David Herrmann Signed-off-by: Dmitry Torokhov --- Documentation/input/userio.txt | 70 ++++++++++ MAINTAINERS | 6 + drivers/input/serio/Kconfig | 14 ++ drivers/input/serio/Makefile | 1 + drivers/input/serio/userio.c | 285 +++++++++++++++++++++++++++++++++++++++++ include/linux/miscdevice.h | 1 + include/uapi/linux/userio.h | 44 +++++++ 7 files changed, 421 insertions(+) create mode 100644 Documentation/input/userio.txt create mode 100644 drivers/input/serio/userio.c create mode 100644 include/uapi/linux/userio.h (limited to 'Documentation') diff --git a/Documentation/input/userio.txt b/Documentation/input/userio.txt new file mode 100644 index 000000000000..0880c0f447a6 --- /dev/null +++ b/Documentation/input/userio.txt @@ -0,0 +1,70 @@ + The userio Protocol + (c) 2015 Stephen Chandler Paul + Sponsored by Red Hat +-------------------------------------------------------------------------------- + +1. Introduction +~~~~~~~~~~~~~~~ + This module is intended to try to make the lives of input driver developers +easier by allowing them to test various serio devices (mainly the various +touchpads found on laptops) without having to have the physical device in front +of them. userio accomplishes this by allowing any privileged userspace program +to directly interact with the kernel's serio driver and control a virtual serio +port from there. + +2. Usage overview +~~~~~~~~~~~~~~~~~ + In order to interact with the userio kernel module, one simply opens the +/dev/userio character device in their applications. Commands are sent to the +kernel module by writing to the device, and any data received from the serio +driver is read as-is from the /dev/userio device. All of the structures and +macros you need to interact with the device are defined in and +. + +3. Command Structure +~~~~~~~~~~~~~~~~~~~~ + The struct used for sending commands to /dev/userio is as follows: + + struct userio_cmd { + __u8 type; + __u8 data; + }; + + "type" describes the type of command that is being sent. This can be any one +of the USERIO_CMD macros defined in . "data" is the argument +that goes along with the command. In the event that the command doesn't have an +argument, this field can be left untouched and will be ignored by the kernel. +Each command should be sent by writing the struct directly to the character +device. In the event that the command you send is invalid, an error will be +returned by the character device and a more descriptive error will be printed +to the kernel log. Only one command can be sent at a time, any additional data +written to the character device after the initial command will be ignored. + To close the virtual serio port, just close /dev/userio. + +4. Commands +~~~~~~~~~~~ + +4.1 USERIO_CMD_REGISTER +~~~~~~~~~~~~~~~~~~~~~~~ + Registers the port with the serio driver and begins transmitting data back and +forth. Registration can only be performed once a port type is set with +USERIO_CMD_SET_PORT_TYPE. Has no argument. + +4.2 USERIO_CMD_SET_PORT_TYPE +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Sets the type of port we're emulating, where "data" is the port type being +set. Can be any of the macros from . For example: SERIO_8042 +would set the port type to be a normal PS/2 port. + +4.3 USERIO_CMD_SEND_INTERRUPT +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Sends an interrupt through the virtual serio port to the serio driver, where +"data" is the interrupt data being sent. + +5. Userspace tools +~~~~~~~~~~~~~~~~~~ + The userio userspace tools are able to record PS/2 devices using some of the +debugging information from i8042, and play back the devices on /dev/userio. The +latest version of these tools can be found at: + + https://github.com/Lyude/ps2emu diff --git a/MAINTAINERS b/MAINTAINERS index 797236befd27..ba59bd7b3557 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11100,6 +11100,12 @@ S: Maintained F: drivers/media/v4l2-core/videobuf2-* F: include/media/videobuf2-* +VIRTUAL SERIO DEVICE DRIVER +M: Stephen Chandler Paul +S: Maintained +F: drivers/input/serio/userio.c +F: include/uapi/linux/userio.h + VIRTIO CONSOLE DRIVER M: Amit Shah L: virtualization@lists.linux-foundation.org diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 200841b77edb..c3d05b4d3118 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -292,4 +292,18 @@ config SERIO_SUN4I_PS2 To compile this driver as a module, choose M here: the module will be called sun4i-ps2. +config USERIO + tristate "User space serio port driver support" + help + Say Y here if you want to support user level drivers for serio + subsystem accessible under char device 10:240 - /dev/userio. Using + this facility userspace programs can implement serio ports that + will be used by the standard in-kernel serio consumer drivers, + such as psmouse and atkbd. + + To compile this driver as a module, choose M here: the module will be + called userio. + + If you are unsure, say N. + endif diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index c600089b7a34..2374ef9b33d7 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_SERIO_APBPS2) += apbps2.o obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o obj-$(CONFIG_HYPERV_KEYBOARD) += hyperv-keyboard.o obj-$(CONFIG_SERIO_SUN4I_PS2) += sun4i-ps2.o +obj-$(CONFIG_USERIO) += userio.o diff --git a/drivers/input/serio/userio.c b/drivers/input/serio/userio.c new file mode 100644 index 000000000000..df1fd41860ac --- /dev/null +++ b/drivers/input/serio/userio.c @@ -0,0 +1,285 @@ +/* + * userio kernel serio device emulation module + * Copyright (C) 2015 Red Hat + * Copyright (C) 2015 Stephen Chandler Paul + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser 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 useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USERIO_NAME "userio" +#define USERIO_BUFSIZE 16 + +static struct miscdevice userio_misc; + +struct userio_device { + struct serio *serio; + struct mutex mutex; + + bool running; + + u8 head; + u8 tail; + + spinlock_t buf_lock; + unsigned char buf[USERIO_BUFSIZE]; + + wait_queue_head_t waitq; +}; + +/** + * userio_device_write - Write data from serio to a userio device in userspace + * @id: The serio port for the userio device + * @val: The data to write to the device + */ +static int userio_device_write(struct serio *id, unsigned char val) +{ + struct userio_device *userio = id->port_data; + unsigned long flags; + + spin_lock_irqsave(&userio->buf_lock, flags); + + userio->buf[userio->head] = val; + userio->head = (userio->head + 1) % USERIO_BUFSIZE; + + if (userio->head == userio->tail) + dev_warn(userio_misc.this_device, + "Buffer overflowed, userio client isn't keeping up"); + + spin_unlock_irqrestore(&userio->buf_lock, flags); + + wake_up_interruptible(&userio->waitq); + + return 0; +} + +static int userio_char_open(struct inode *inode, struct file *file) +{ + struct userio_device *userio; + + userio = kzalloc(sizeof(struct userio_device), GFP_KERNEL); + if (!userio) + return -ENOMEM; + + mutex_init(&userio->mutex); + spin_lock_init(&userio->buf_lock); + init_waitqueue_head(&userio->waitq); + + userio->serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!userio->serio) { + kfree(userio); + return -ENOMEM; + } + + userio->serio->write = userio_device_write; + userio->serio->port_data = userio; + + file->private_data = userio; + + return 0; +} + +static int userio_char_release(struct inode *inode, struct file *file) +{ + struct userio_device *userio = file->private_data; + + if (userio->running) { + /* + * Don't free the serio port here, serio_unregister_port() + * does it for us. + */ + serio_unregister_port(userio->serio); + } else { + kfree(userio->serio); + } + + kfree(userio); + + return 0; +} + +static ssize_t userio_char_read(struct file *file, char __user *user_buffer, + size_t count, loff_t *ppos) +{ + struct userio_device *userio = file->private_data; + int error; + size_t nonwrap_len, copylen; + unsigned char buf[USERIO_BUFSIZE]; + unsigned long flags; + + /* + * By the time we get here, the data that was waiting might have + * been taken by another thread. Grab the buffer lock and check if + * there's still any data waiting, otherwise repeat this process + * until we have data (unless the file descriptor is non-blocking + * of course). + */ + for (;;) { + spin_lock_irqsave(&userio->buf_lock, flags); + + nonwrap_len = CIRC_CNT_TO_END(userio->head, + userio->tail, + USERIO_BUFSIZE); + copylen = min(nonwrap_len, count); + if (copylen) { + memcpy(buf, &userio->buf[userio->tail], copylen); + userio->tail = (userio->tail + copylen) % + USERIO_BUFSIZE; + } + + spin_unlock_irqrestore(&userio->buf_lock, flags); + + if (nonwrap_len) + break; + + /* buffer was/is empty */ + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + /* + * count == 0 is special - no IO is done but we check + * for error conditions (see above). + */ + if (count == 0) + return 0; + + error = wait_event_interruptible(userio->waitq, + userio->head != userio->tail); + if (error) + return error; + } + + if (copylen) + if (copy_to_user(user_buffer, buf, copylen)) + return -EFAULT; + + return copylen; +} + +static ssize_t userio_char_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct userio_device *userio = file->private_data; + struct userio_cmd cmd; + int error; + + if (count != sizeof(cmd)) { + dev_warn(userio_misc.this_device, "Invalid payload size\n"); + return -EINVAL; + } + + if (copy_from_user(&cmd, buffer, sizeof(cmd))) + return -EFAULT; + + error = mutex_lock_interruptible(&userio->mutex); + if (error) + return error; + + switch (cmd.type) { + case USERIO_CMD_REGISTER: + if (!userio->serio->id.type) { + dev_warn(userio_misc.this_device, + "No port type given on /dev/userio\n"); + + error = -EINVAL; + goto out; + } + + if (userio->running) { + dev_warn(userio_misc.this_device, + "Begin command sent, but we're already running\n"); + error = -EBUSY; + goto out; + } + + userio->running = true; + serio_register_port(userio->serio); + break; + + case USERIO_CMD_SET_PORT_TYPE: + if (userio->running) { + dev_warn(userio_misc.this_device, + "Can't change port type on an already running userio instance\n"); + error = -EBUSY; + goto out; + } + + userio->serio->id.type = cmd.data; + break; + + case USERIO_CMD_SEND_INTERRUPT: + if (!userio->running) { + dev_warn(userio_misc.this_device, + "The device must be registered before sending interrupts\n"); + error = -ENODEV; + goto out; + } + + serio_interrupt(userio->serio, cmd.data, 0); + break; + + default: + error = -EOPNOTSUPP; + goto out; + } + +out: + mutex_unlock(&userio->mutex); + return error ?: count; +} + +static unsigned int userio_char_poll(struct file *file, poll_table *wait) +{ + struct userio_device *userio = file->private_data; + + poll_wait(file, &userio->waitq, wait); + + if (userio->head != userio->tail) + return POLLIN | POLLRDNORM; + + return 0; +} + +static const struct file_operations userio_fops = { + .owner = THIS_MODULE, + .open = userio_char_open, + .release = userio_char_release, + .read = userio_char_read, + .write = userio_char_write, + .poll = userio_char_poll, + .llseek = no_llseek, +}; + +static struct miscdevice userio_misc = { + .fops = &userio_fops, + .minor = USERIO_MINOR, + .name = USERIO_NAME, +}; +module_driver(userio_misc, misc_register, misc_deregister); + +MODULE_ALIAS_MISCDEV(USERIO_MINOR); +MODULE_ALIAS("devname:" USERIO_NAME); + +MODULE_AUTHOR("Stephen Chandler Paul "); +MODULE_DESCRIPTION("Virtual Serio Device Support"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 81f6e427ba6b..543037465973 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -49,6 +49,7 @@ #define LOOP_CTRL_MINOR 237 #define VHOST_NET_MINOR 238 #define UHID_MINOR 239 +#define USERIO_MINOR 240 #define MISC_DYNAMIC_MINOR 255 struct device; diff --git a/include/uapi/linux/userio.h b/include/uapi/linux/userio.h new file mode 100644 index 000000000000..37d147f0a13a --- /dev/null +++ b/include/uapi/linux/userio.h @@ -0,0 +1,44 @@ +/* + * userio: virtual serio device support + * Copyright (C) 2015 Red Hat + * Copyright (C) 2015 Lyude (Stephen Chandler Paul) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser 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 useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * This is the public header used for user-space communication with the userio + * driver. __attribute__((__packed__)) is used for all structs to keep ABI + * compatibility between all architectures. + */ + +#ifndef _USERIO_H +#define _USERIO_H + +#include + +enum userio_cmd_type { + USERIO_CMD_REGISTER = 0, + USERIO_CMD_SET_PORT_TYPE = 1, + USERIO_CMD_SEND_INTERRUPT = 2 +}; + +/* + * userio Commands + * All commands sent to /dev/userio are encoded using this structure. The type + * field should contain a USERIO_CMD* value that indicates what kind of command + * is being sent to userio. The data field should contain the accompanying + * argument for the command, if there is one. + */ +struct userio_cmd { + __u8 type; + __u8 data; +} __attribute__((__packed__)); + +#endif /* !_USERIO_H */ -- cgit v1.2.3