summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-03-11 20:57:11 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2019-03-11 20:57:11 +0300
commit4f0237062ca70c8e34e16e518aee4b84c30d1832 (patch)
tree117272a42446c2ebd12d9786f52adbeb059cae95
parent6bc3fe8e7e172d5584e529a04cf9eec946428768 (diff)
parent44fc95e218a09d7966a9d448941fdb003f6bb69f (diff)
downloadlinux-4f0237062ca70c8e34e16e518aee4b84c30d1832.tar.xz
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov: - update the ili210x touchscreen driver, refreshing the code and adding support for ILI251X line - add support for st1633 to the st1232 touchscreen driver - add support for sx8650 to the the sx8654 touchscreen driver - add support for Evervision FT5726 to the edt-ft5x06 touchscreen driver - add support for gt5688 to the Goodix touchscreen driver - new vibrator driver for MSM SOCs - miscellaneous fixes for the rest of input drivers * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (53 commits) Input: wacom_serial4 - add support for Wacom ArtPad II tablet Input: elan_i2c - add id for touchpad found in Lenovo s21e-20 Input: raspberrypi-ts - select CONFIG_INPUT_POLLDEV Input: msm-vibrator - use correct gpio header Input: ti_am335x_tsc - remove set but not used variable 'tscadc_dev' Input: i8042 - rework DT node name comparisons Input: goodix - print values in case of inconsistencies Input: goodix - refer to touchscreen.txt in device tree bindings Input: goodix - support Goodix gt5688 Input: synaptics_i2c - remove redundant spinlock Input: db9 - mark expected switch fall-through Input: qt2160 - remove redundant spinlock Input: st1232 - handle common DT bindings Input: ims-pcu - switch to using brightness_set_blocking() Input: st1232 - switch to gpiod API Input: ili210x - fetch touchscreen geometry from DT Input: msm-vibrator - tweak an error message Input: tm2-touchkey - acknowledge that setting brightness is a blocking call Input: stmfts - acknowledge that setting brightness is a blocking call Input: ili210x - switch to using devm_device_add_group() ...
-rw-r--r--Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt9
-rw-r--r--Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt25
-rw-r--r--Documentation/devicetree/bindings/input/msm-vibrator.txt36
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt13
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/goodix.txt14
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt8
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/sx8654.txt10
-rw-r--r--drivers/input/joystick/db9.c2
-rw-r--r--drivers/input/keyboard/gpio_keys.c10
-rw-r--r--drivers/input/keyboard/mcs_touchkey.c5
-rw-r--r--drivers/input/keyboard/mtk-pmic-keys.c13
-rw-r--r--drivers/input/keyboard/qt2160.c9
-rw-r--r--drivers/input/keyboard/tca6416-keypad.c4
-rw-r--r--drivers/input/keyboard/tm2-touchkey.c136
-rw-r--r--drivers/input/misc/Kconfig10
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/ims-pcu.c27
-rw-r--r--drivers/input/misc/msm-vibrator.c281
-rw-r--r--drivers/input/misc/soc_button_array.c6
-rw-r--r--drivers/input/mouse/elan_i2c_core.c1
-rw-r--r--drivers/input/mouse/synaptics_i2c.c22
-rw-r--r--drivers/input/serio/i8042-sparcio.h21
-rw-r--r--drivers/input/tablet/wacom_serial4.c2
-rw-r--r--drivers/input/touchscreen/Kconfig7
-rw-r--r--drivers/input/touchscreen/ad7879.c11
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c110
-rw-r--r--drivers/input/touchscreen/goodix.c6
-rw-r--r--drivers/input/touchscreen/ili210x.c321
-rw-r--r--drivers/input/touchscreen/st1232.c154
-rw-r--r--drivers/input/touchscreen/stmfts.c30
-rw-r--r--drivers/input/touchscreen/sx8654.c255
-rw-r--r--drivers/input/touchscreen/ti_am335x_tsc.c4
-rw-r--r--include/linux/input/ili210x.h11
33 files changed, 1207 insertions, 367 deletions
diff --git a/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt b/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt
index 0c252d9306da..ef2ae729718f 100644
--- a/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt
+++ b/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt
@@ -1,13 +1,19 @@
Samsung tm2-touchkey
Required properties:
-- compatible: must be "cypress,tm2-touchkey"
+- compatible:
+ * "cypress,tm2-touchkey" - for the touchkey found on the tm2 board
+ * "cypress,midas-touchkey" - for the touchkey found on midas boards
+ * "cypress,aries-touchkey" - for the touchkey found on aries boards
- reg: I2C address of the chip.
- interrupts: interrupt to which the chip is connected (see interrupt
binding[0]).
- vcc-supply : internal regulator output. 1.8V
- vdd-supply : power supply for IC 3.3V
+Optional properties:
+- linux,keycodes: array of keycodes (max 4), default KEY_PHONE and KEY_BACK
+
[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
Example:
@@ -21,5 +27,6 @@ Example:
interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
vcc-supply=<&ldo32_reg>;
vdd-supply=<&ldo33_reg>;
+ linux,keycodes = <KEY_PHONE KEY_BACK>;
};
};
diff --git a/Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt b/Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt
new file mode 100644
index 000000000000..b2a76301e632
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt
@@ -0,0 +1,25 @@
+Ilitek ILI210x/ILI251x touchscreen controller
+
+Required properties:
+- compatible:
+ ilitek,ili210x for ILI210x
+ ilitek,ili251x for ILI251x
+
+- reg: The I2C address of the device
+
+- interrupts: The sink for the touchscreen's IRQ output
+ See ../interrupt-controller/interrupts.txt
+
+Optional properties for main touchpad device:
+
+- reset-gpios: GPIO specifier for the touchscreen's reset pin (active low)
+
+Example:
+
+ touchscreen@41 {
+ compatible = "ilitek,ili251x";
+ reg = <0x41>;
+ interrupt-parent = <&gpio4>;
+ interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
+ reset-gpios = <&gpio5 21 GPIO_ACTIVE_LOW>;
+ };
diff --git a/Documentation/devicetree/bindings/input/msm-vibrator.txt b/Documentation/devicetree/bindings/input/msm-vibrator.txt
new file mode 100644
index 000000000000..8dcf014ef2e5
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/msm-vibrator.txt
@@ -0,0 +1,36 @@
+* Device tree bindings for the Qualcomm MSM vibrator
+
+Required properties:
+
+ - compatible: Should be one of
+ "qcom,msm8226-vibrator"
+ "qcom,msm8974-vibrator"
+ - reg: the base address and length of the IO memory for the registers.
+ - pinctrl-names: set to default.
+ - pinctrl-0: phandles pointing to pin configuration nodes. See
+ Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
+ - clock-names: set to pwm
+ - clocks: phandle of the clock. See
+ Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - enable-gpios: GPIO that enables the vibrator.
+
+Optional properties:
+
+ - vcc-supply: phandle to the regulator that provides power to the sensor.
+
+Example from a LG Nexus 5 (hammerhead) phone:
+
+vibrator@fd8c3450 {
+ reg = <0xfd8c3450 0x400>;
+ compatible = "qcom,msm8974-vibrator";
+
+ vcc-supply = <&pm8941_l19>;
+
+ clocks = <&mmcc CAMSS_GP1_CLK>;
+ clock-names = "pwm";
+
+ enable-gpios = <&msmgpio 60 GPIO_ACTIVE_HIGH>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&vibrator_pin>;
+};
diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
index da2dc5d6c98b..870b8c5cce9b 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
@@ -1,11 +1,12 @@
FocalTech EDT-FT5x06 Polytouch driver
=====================================
-There are 3 variants of the chip for various touch panel sizes
+There are 5 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"
+FT5726NEI 5.7” .. 11.6"
The software interface is identical for all those chips, so that
currently there is no need for the driver to distinguish between the
@@ -19,6 +20,7 @@ Required properties:
or: "edt,edt-ft5306"
or: "edt,edt-ft5406"
or: "edt,edt-ft5506"
+ or: "evervision,ev-ft5726"
or: "focaltech,ft6236"
- reg: I2C slave address of the chip (0x38)
@@ -42,6 +44,15 @@ Optional properties:
- offset: allows setting the edge compensation in the range from
0 to 31.
+
+ - offset-x: Same as offset, but applies only to the horizontal position.
+ Range from 0 to 80, only supported by evervision,ev-ft5726
+ devices.
+
+ - offset-y: Same as offset, but applies only to the vertical position.
+ Range from 0 to 80, only supported by evervision,ev-ft5726
+ devices.
+
- touchscreen-size-x : See touchscreen.txt
- touchscreen-size-y : See touchscreen.txt
- touchscreen-fuzz-x : See touchscreen.txt
diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
index f7e95c52f3c7..8cf0b4d38a7e 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
@@ -3,6 +3,7 @@ Device tree bindings for Goodix GT9xx series touchscreen controller
Required properties:
- compatible : Should be "goodix,gt1151"
+ or "goodix,gt5688"
or "goodix,gt911"
or "goodix,gt9110"
or "goodix,gt912"
@@ -18,11 +19,14 @@ Optional properties:
- irq-gpios : GPIO pin used for IRQ. The driver uses the
interrupt gpio pin as output to reset the device.
- reset-gpios : GPIO pin used for reset
-
- - 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)
+ - touchscreen-inverted-x
+ - touchscreen-inverted-y
+ - touchscreen-size-x
+ - touchscreen-size-y
+ - touchscreen-swapped-x-y
+
+The touchscreen-* properties are documented in touchscreen.txt in this
+directory.
Example:
diff --git a/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt b/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt
index 64ad48b824a2..019373253b28 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt
@@ -1,13 +1,17 @@
-* Sitronix st1232 touchscreen controller
+* Sitronix st1232 or st1633 touchscreen controller
Required properties:
-- compatible: must be "sitronix,st1232"
+- compatible: must contain one of
+ * "sitronix,st1232"
+ * "sitronix,st1633"
- reg: I2C address of the chip
- interrupts: interrupt to which the chip is connected
Optional properties:
- gpios: a phandle to the reset GPIO
+For additional optional properties see: touchscreen.txt
+
Example:
i2c@00000000 {
diff --git a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt
index 4886c4aa2906..0ebe6dd043c7 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt
@@ -1,10 +1,17 @@
* Semtech SX8654 I2C Touchscreen Controller
Required properties:
-- compatible: must be "semtech,sx8654"
+- compatible: must be one of the following, depending on the model:
+ "semtech,sx8650"
+ "semtech,sx8654"
+ "semtech,sx8655"
+ "semtech,sx8656"
- reg: i2c slave address
- interrupts: touch controller interrupt
+Optional properties:
+ - reset-gpios: GPIO specification for the NRST input
+
Example:
sx8654@48 {
@@ -12,4 +19,5 @@ Example:
reg = <0x48>;
interrupt-parent = <&gpio6>;
interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
+ reset-gpios = <&gpio4 2 GPIO_ACTIVE_LOW>;
};
diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c
index 804b1b80a8be..5a52b65bef9a 100644
--- a/drivers/input/joystick/db9.c
+++ b/drivers/input/joystick/db9.c
@@ -259,7 +259,7 @@ static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char
db9_saturn_write_sub(port, type, 3, powered, 0);
return data[0] = 0xe3;
}
- /* else: fall through */
+ /* fall through */
default:
return data[0];
}
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 492a971b95b5..6cd199e8a370 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -1015,8 +1015,18 @@ static int __maybe_unused gpio_keys_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
+static void gpio_keys_shutdown(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = gpio_keys_suspend(&pdev->dev);
+ if (ret)
+ dev_err(&pdev->dev, "failed to shutdown\n");
+}
+
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
+ .shutdown = gpio_keys_shutdown,
.driver = {
.name = "gpio-keys",
.pm = &gpio_keys_pm_ops,
diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c
index be56d4f262a7..b132662201a4 100644
--- a/drivers/input/keyboard/mcs_touchkey.c
+++ b/drivers/input/keyboard/mcs_touchkey.c
@@ -113,9 +113,8 @@ static int mcs_touchkey_probe(struct i2c_client *client,
return -EINVAL;
}
- data = kzalloc(sizeof(struct mcs_touchkey_data) +
- sizeof(data->keycodes[0]) * (pdata->key_maxval + 1),
- GFP_KERNEL);
+ data = kzalloc(struct_size(data, keycodes, pdata->key_maxval + 1),
+ GFP_KERNEL);
input_dev = input_allocate_device();
if (!data || !input_dev) {
dev_err(&client->dev, "Failed to allocate memory\n");
diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c
index 02c67a1749fc..8e6ebab05ab4 100644
--- a/drivers/input/keyboard/mtk-pmic-keys.c
+++ b/drivers/input/keyboard/mtk-pmic-keys.c
@@ -14,18 +14,17 @@
*
*/
-#include <linux/module.h>
-#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/interrupt.h>
-#include <linux/platform_device.h>
#include <linux/kernel.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/regmap.h>
#include <linux/mfd/mt6323/registers.h>
-#include <linux/mfd/mt6397/registers.h>
#include <linux/mfd/mt6397/core.h>
+#include <linux/mfd/mt6397/registers.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
#define MTK_PMIC_PWRKEY_RST_EN_MASK 0x1
#define MTK_PMIC_PWRKEY_RST_EN_SHIFT 6
diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c
index d466bc07aebb..6a43895b28e7 100644
--- a/drivers/input/keyboard/qt2160.c
+++ b/drivers/input/keyboard/qt2160.c
@@ -68,7 +68,6 @@ struct qt2160_data {
struct i2c_client *client;
struct input_dev *input;
struct delayed_work dwork;
- spinlock_t lock; /* Protects canceling/rescheduling of dwork */
unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
u16 key_matrix;
#ifdef CONFIG_LEDS_CLASS
@@ -212,22 +211,15 @@ static int qt2160_get_key_matrix(struct qt2160_data *qt2160)
static irqreturn_t qt2160_irq(int irq, void *_qt2160)
{
struct qt2160_data *qt2160 = _qt2160;
- unsigned long flags;
-
- spin_lock_irqsave(&qt2160->lock, flags);
mod_delayed_work(system_wq, &qt2160->dwork, 0);
- spin_unlock_irqrestore(&qt2160->lock, flags);
-
return IRQ_HANDLED;
}
static void qt2160_schedule_read(struct qt2160_data *qt2160)
{
- spin_lock_irq(&qt2160->lock);
schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL);
- spin_unlock_irq(&qt2160->lock);
}
static void qt2160_worker(struct work_struct *work)
@@ -391,7 +383,6 @@ static int qt2160_probe(struct i2c_client *client,
qt2160->client = client;
qt2160->input = input;
INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker);
- spin_lock_init(&qt2160->lock);
input->name = "AT42QT2160 Touch Sense Keyboard";
input->id.bustype = BUS_I2C;
diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c
index dc983ab6c0ad..cdeef180aead 100644
--- a/drivers/input/keyboard/tca6416-keypad.c
+++ b/drivers/input/keyboard/tca6416-keypad.c
@@ -219,9 +219,7 @@ static int tca6416_keypad_probe(struct i2c_client *client,
return -EINVAL;
}
- chip = kzalloc(sizeof(struct tca6416_keypad_chip) +
- pdata->nbuttons * sizeof(struct tca6416_button),
- GFP_KERNEL);
+ chip = kzalloc(struct_size(chip, buttons, pdata->nbuttons), GFP_KERNEL);
input = input_allocate_device();
if (!chip || !input) {
error = -ENOMEM;
diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c
index abc266e40e17..d4455f3a5cf1 100644
--- a/drivers/input/keyboard/tm2-touchkey.c
+++ b/drivers/input/keyboard/tm2-touchkey.c
@@ -22,12 +22,14 @@
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/pm.h>
#include <linux/regulator/consumer.h>
#define TM2_TOUCHKEY_DEV_NAME "tm2-touchkey"
-#define TM2_TOUCHKEY_KEYCODE_REG 0x03
-#define TM2_TOUCHKEY_BASE_REG 0x00
+
+#define ARIES_TOUCHKEY_CMD_LED_ON 0x1
+#define ARIES_TOUCHKEY_CMD_LED_OFF 0x2
#define TM2_TOUCHKEY_CMD_LED_ON 0x10
#define TM2_TOUCHKEY_CMD_LED_OFF 0x20
#define TM2_TOUCHKEY_BIT_PRESS_EV BIT(3)
@@ -35,9 +37,13 @@
#define TM2_TOUCHKEY_LED_VOLTAGE_MIN 2500000
#define TM2_TOUCHKEY_LED_VOLTAGE_MAX 3300000
-enum {
- TM2_TOUCHKEY_KEY_MENU = 0x1,
- TM2_TOUCHKEY_KEY_BACK,
+struct touchkey_variant {
+ u8 keycode_reg;
+ u8 base_reg;
+ u8 cmd_led_on;
+ u8 cmd_led_off;
+ bool no_reg;
+ bool fixed_regulator;
};
struct tm2_touchkey_data {
@@ -46,9 +52,33 @@ struct tm2_touchkey_data {
struct led_classdev led_dev;
struct regulator *vdd;
struct regulator_bulk_data regulators[2];
+ const struct touchkey_variant *variant;
+ u32 keycodes[4];
+ int num_keycodes;
+};
+
+static const struct touchkey_variant tm2_touchkey_variant = {
+ .keycode_reg = 0x03,
+ .base_reg = 0x00,
+ .cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON,
+ .cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF,
+};
+
+static const struct touchkey_variant midas_touchkey_variant = {
+ .keycode_reg = 0x00,
+ .base_reg = 0x00,
+ .cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON,
+ .cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF,
};
-static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
+static struct touchkey_variant aries_touchkey_variant = {
+ .no_reg = true,
+ .fixed_regulator = true,
+ .cmd_led_on = ARIES_TOUCHKEY_CMD_LED_ON,
+ .cmd_led_off = ARIES_TOUCHKEY_CMD_LED_OFF,
+};
+
+static int tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
enum led_brightness brightness)
{
struct tm2_touchkey_data *touchkey =
@@ -58,15 +88,19 @@ static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
if (brightness == LED_OFF) {
volt = TM2_TOUCHKEY_LED_VOLTAGE_MIN;
- data = TM2_TOUCHKEY_CMD_LED_OFF;
+ data = touchkey->variant->cmd_led_off;
} else {
volt = TM2_TOUCHKEY_LED_VOLTAGE_MAX;
- data = TM2_TOUCHKEY_CMD_LED_ON;
+ data = touchkey->variant->cmd_led_on;
}
- regulator_set_voltage(touchkey->vdd, volt, volt);
- i2c_smbus_write_byte_data(touchkey->client,
- TM2_TOUCHKEY_BASE_REG, data);
+ if (!touchkey->variant->fixed_regulator)
+ regulator_set_voltage(touchkey->vdd, volt, volt);
+
+ return touchkey->variant->no_reg ?
+ i2c_smbus_write_byte(touchkey->client, data) :
+ i2c_smbus_write_byte_data(touchkey->client,
+ touchkey->variant->base_reg, data);
}
static int tm2_touchkey_power_enable(struct tm2_touchkey_data *touchkey)
@@ -96,49 +130,57 @@ static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid)
{
struct tm2_touchkey_data *touchkey = devid;
int data;
- int key;
-
- data = i2c_smbus_read_byte_data(touchkey->client,
- TM2_TOUCHKEY_KEYCODE_REG);
+ int index;
+ int i;
+
+ if (touchkey->variant->no_reg)
+ data = i2c_smbus_read_byte(touchkey->client);
+ else
+ data = i2c_smbus_read_byte_data(touchkey->client,
+ touchkey->variant->keycode_reg);
if (data < 0) {
dev_err(&touchkey->client->dev,
"failed to read i2c data: %d\n", data);
goto out;
}
- switch (data & TM2_TOUCHKEY_BIT_KEYCODE) {
- case TM2_TOUCHKEY_KEY_MENU:
- key = KEY_PHONE;
- break;
-
- case TM2_TOUCHKEY_KEY_BACK:
- key = KEY_BACK;
- break;
-
- default:
+ index = (data & TM2_TOUCHKEY_BIT_KEYCODE) - 1;
+ if (index < 0 || index >= touchkey->num_keycodes) {
dev_warn(&touchkey->client->dev,
- "unhandled keycode, data %#02x\n", data);
+ "invalid keycode index %d\n", index);
goto out;
}
if (data & TM2_TOUCHKEY_BIT_PRESS_EV) {
- input_report_key(touchkey->input_dev, KEY_PHONE, 0);
- input_report_key(touchkey->input_dev, KEY_BACK, 0);
+ for (i = 0; i < touchkey->num_keycodes; i++)
+ input_report_key(touchkey->input_dev,
+ touchkey->keycodes[i], 0);
} else {
- input_report_key(touchkey->input_dev, key, 1);
+ input_report_key(touchkey->input_dev,
+ touchkey->keycodes[index], 1);
}
input_sync(touchkey->input_dev);
out:
+ if (touchkey->variant->fixed_regulator &&
+ data & TM2_TOUCHKEY_BIT_PRESS_EV) {
+ /* touch turns backlight on, so make sure we're in sync */
+ if (touchkey->led_dev.brightness == LED_OFF)
+ tm2_touchkey_led_brightness_set(&touchkey->led_dev,
+ LED_OFF);
+ }
+
return IRQ_HANDLED;
}
static int tm2_touchkey_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct device_node *np = client->dev.of_node;
struct tm2_touchkey_data *touchkey;
int error;
+ int i;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -153,6 +195,8 @@ static int tm2_touchkey_probe(struct i2c_client *client,
touchkey->client = client;
i2c_set_clientdata(client, touchkey);
+ touchkey->variant = of_device_get_match_data(&client->dev);
+
touchkey->regulators[0].supply = "vcc";
touchkey->regulators[1].supply = "vdd";
error = devm_regulator_bulk_get(&client->dev,
@@ -166,6 +210,16 @@ static int tm2_touchkey_probe(struct i2c_client *client,
/* Save VDD for easy access */
touchkey->vdd = touchkey->regulators[1].consumer;
+ touchkey->num_keycodes = of_property_read_variable_u32_array(np,
+ "linux,keycodes", touchkey->keycodes, 0,
+ ARRAY_SIZE(touchkey->keycodes));
+ if (touchkey->num_keycodes <= 0) {
+ /* default keycodes */
+ touchkey->keycodes[0] = KEY_PHONE;
+ touchkey->keycodes[1] = KEY_BACK;
+ touchkey->num_keycodes = 2;
+ }
+
error = tm2_touchkey_power_enable(touchkey);
if (error) {
dev_err(&client->dev, "failed to power up device: %d\n", error);
@@ -190,8 +244,9 @@ static int tm2_touchkey_probe(struct i2c_client *client,
touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME;
touchkey->input_dev->id.bustype = BUS_I2C;
- input_set_capability(touchkey->input_dev, EV_KEY, KEY_PHONE);
- input_set_capability(touchkey->input_dev, EV_KEY, KEY_BACK);
+ for (i = 0; i < touchkey->num_keycodes; i++)
+ input_set_capability(touchkey->input_dev, EV_KEY,
+ touchkey->keycodes[i]);
error = input_register_device(touchkey->input_dev);
if (error) {
@@ -212,9 +267,10 @@ static int tm2_touchkey_probe(struct i2c_client *client,
/* led device */
touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME;
- touchkey->led_dev.brightness = LED_FULL;
+ touchkey->led_dev.brightness = LED_ON;
touchkey->led_dev.max_brightness = LED_ON;
- touchkey->led_dev.brightness_set = tm2_touchkey_led_brightness_set;
+ touchkey->led_dev.brightness_set_blocking =
+ tm2_touchkey_led_brightness_set;
error = devm_led_classdev_register(&client->dev, &touchkey->led_dev);
if (error) {
@@ -223,6 +279,9 @@ static int tm2_touchkey_probe(struct i2c_client *client,
return error;
}
+ if (touchkey->variant->fixed_regulator)
+ tm2_touchkey_led_brightness_set(&touchkey->led_dev, LED_ON);
+
return 0;
}
@@ -262,7 +321,16 @@ static const struct i2c_device_id tm2_touchkey_id_table[] = {
MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table);
static const struct of_device_id tm2_touchkey_of_match[] = {
- { .compatible = "cypress,tm2-touchkey", },
+ {
+ .compatible = "cypress,tm2-touchkey",
+ .data = &tm2_touchkey_variant,
+ }, {
+ .compatible = "cypress,midas-touchkey",
+ .data = &midas_touchkey_variant,
+ }, {
+ .compatible = "cypress,aries-touchkey",
+ .data = &aries_touchkey_variant,
+ },
{ },
};
MODULE_DEVICE_TABLE(of, tm2_touchkey_of_match);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 279fb02a0f14..e15ed1bb8558 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -117,6 +117,16 @@ config INPUT_E3X0_BUTTON
To compile this driver as a module, choose M here: the
module will be called e3x0_button.
+config INPUT_MSM_VIBRATOR
+ tristate "Qualcomm MSM vibrator driver"
+ select INPUT_FF_MEMLESS
+ help
+ Support for the vibrator that is found on various Qualcomm MSM
+ SOCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called msm_vibrator.
+
config INPUT_PCSPKR
tristate "PC Speaker support"
depends on PCSPKR_PLATFORM
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 1b44202ad8f7..b936c5b1d4ac 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o
obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
obj-$(CONFIG_INPUT_MMA8450) += mma8450.o
+obj-$(CONFIG_INPUT_MSM_VIBRATOR) += msm-vibrator.o
obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index 3d51175c4d72..74cf3b612f05 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -39,8 +39,6 @@ struct ims_pcu_gamepad {
struct ims_pcu_backlight {
struct led_classdev cdev;
- struct work_struct work;
- enum led_brightness desired_brightness;
char name[32];
};
@@ -949,14 +947,14 @@ out:
#define IMS_PCU_MAX_BRIGHTNESS 31998
-static void ims_pcu_backlight_work(struct work_struct *work)
+static int ims_pcu_backlight_set_brightness(struct led_classdev *cdev,
+ enum led_brightness value)
{
struct ims_pcu_backlight *backlight =
- container_of(work, struct ims_pcu_backlight, work);
+ container_of(cdev, struct ims_pcu_backlight, cdev);
struct ims_pcu *pcu =
container_of(backlight, struct ims_pcu, backlight);
- int desired_brightness = backlight->desired_brightness;
- __le16 br_val = cpu_to_le16(desired_brightness);
+ __le16 br_val = cpu_to_le16(value);
int error;
mutex_lock(&pcu->cmd_mutex);
@@ -966,19 +964,11 @@ static void ims_pcu_backlight_work(struct work_struct *work)
if (error && error != -ENODEV)
dev_warn(pcu->dev,
"Failed to set desired brightness %u, error: %d\n",
- desired_brightness, error);
+ value, error);
mutex_unlock(&pcu->cmd_mutex);
-}
-static void ims_pcu_backlight_set_brightness(struct led_classdev *cdev,
- enum led_brightness value)
-{
- struct ims_pcu_backlight *backlight =
- container_of(cdev, struct ims_pcu_backlight, cdev);
-
- backlight->desired_brightness = value;
- schedule_work(&backlight->work);
+ return error;
}
static enum led_brightness
@@ -1015,14 +1005,14 @@ static int ims_pcu_setup_backlight(struct ims_pcu *pcu)
struct ims_pcu_backlight *backlight = &pcu->backlight;
int error;
- INIT_WORK(&backlight->work, ims_pcu_backlight_work);
snprintf(backlight->name, sizeof(backlight->name),
"pcu%d::kbd_backlight", pcu->device_no);
backlight->cdev.name = backlight->name;
backlight->cdev.max_brightness = IMS_PCU_MAX_BRIGHTNESS;
backlight->cdev.brightness_get = ims_pcu_backlight_get_brightness;
- backlight->cdev.brightness_set = ims_pcu_backlight_set_brightness;
+ backlight->cdev.brightness_set_blocking =
+ ims_pcu_backlight_set_brightness;
error = led_classdev_register(pcu->dev, &backlight->cdev);
if (error) {
@@ -1040,7 +1030,6 @@ static void ims_pcu_destroy_backlight(struct ims_pcu *pcu)
struct ims_pcu_backlight *backlight = &pcu->backlight;
led_classdev_unregister(&backlight->cdev);
- cancel_work_sync(&backlight->work);
}
diff --git a/drivers/input/misc/msm-vibrator.c b/drivers/input/misc/msm-vibrator.c
new file mode 100644
index 000000000000..b60f1aaee705
--- /dev/null
+++ b/drivers/input/misc/msm-vibrator.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Qualcomm MSM vibrator driver
+ *
+ * Copyright (c) 2018 Brian Masney <masneyb@onstation.org>
+ *
+ * Based on qcom,pwm-vibrator.c from:
+ * Copyright (c) 2018 Jonathan Marek <jonathan@marek.ca>
+ *
+ * Based on msm_pwm_vibrator.c from downstream Android sources:
+ * Copyright (C) 2009-2014 LGE, Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#define REG_CMD_RCGR 0x00
+#define REG_CFG_RCGR 0x04
+#define REG_M 0x08
+#define REG_N 0x0C
+#define REG_D 0x10
+#define REG_CBCR 0x24
+#define MMSS_CC_M_DEFAULT 1
+
+struct msm_vibrator {
+ struct input_dev *input;
+ struct mutex mutex;
+ struct work_struct worker;
+ void __iomem *base;
+ struct regulator *vcc;
+ struct clk *clk;
+ struct gpio_desc *enable_gpio;
+ u16 magnitude;
+ bool enabled;
+};
+
+static void msm_vibrator_write(struct msm_vibrator *vibrator, int offset,
+ u32 value)
+{
+ writel(value, vibrator->base + offset);
+}
+
+static int msm_vibrator_start(struct msm_vibrator *vibrator)
+{
+ int d_reg_val, ret = 0;
+
+ mutex_lock(&vibrator->mutex);
+
+ if (!vibrator->enabled) {
+ ret = clk_set_rate(vibrator->clk, 24000);
+ if (ret) {
+ dev_err(&vibrator->input->dev,
+ "Failed to set clock rate: %d\n", ret);
+ goto unlock;
+ }
+
+ ret = clk_prepare_enable(vibrator->clk);
+ if (ret) {
+ dev_err(&vibrator->input->dev,
+ "Failed to enable clock: %d\n", ret);
+ goto unlock;
+ }
+
+ ret = regulator_enable(vibrator->vcc);
+ if (ret) {
+ dev_err(&vibrator->input->dev,
+ "Failed to enable regulator: %d\n", ret);
+ clk_disable(vibrator->clk);
+ goto unlock;
+ }
+
+ gpiod_set_value_cansleep(vibrator->enable_gpio, 1);
+
+ vibrator->enabled = true;
+ }
+
+ d_reg_val = 127 - ((126 * vibrator->magnitude) / 0xffff);
+ msm_vibrator_write(vibrator, REG_CFG_RCGR,
+ (2 << 12) | /* dual edge mode */
+ (0 << 8) | /* cxo */
+ (7 << 0));
+ msm_vibrator_write(vibrator, REG_M, 1);
+ msm_vibrator_write(vibrator, REG_N, 128);
+ msm_vibrator_write(vibrator, REG_D, d_reg_val);
+ msm_vibrator_write(vibrator, REG_CMD_RCGR, 1);
+ msm_vibrator_write(vibrator, REG_CBCR, 1);
+
+unlock:
+ mutex_unlock(&vibrator->mutex);
+
+ return ret;
+}
+
+static void msm_vibrator_stop(struct msm_vibrator *vibrator)
+{
+ mutex_lock(&vibrator->mutex);
+
+ if (vibrator->enabled) {
+ gpiod_set_value_cansleep(vibrator->enable_gpio, 0);
+ regulator_disable(vibrator->vcc);
+ clk_disable(vibrator->clk);
+ vibrator->enabled = false;
+ }
+
+ mutex_unlock(&vibrator->mutex);
+}
+
+static void msm_vibrator_worker(struct work_struct *work)
+{
+ struct msm_vibrator *vibrator = container_of(work,
+ struct msm_vibrator,
+ worker);
+
+ if (vibrator->magnitude)
+ msm_vibrator_start(vibrator);
+ else
+ msm_vibrator_stop(vibrator);
+}
+
+static int msm_vibrator_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct msm_vibrator *vibrator = input_get_drvdata(dev);
+
+ mutex_lock(&vibrator->mutex);
+
+ if (effect->u.rumble.strong_magnitude > 0)
+ vibrator->magnitude = effect->u.rumble.strong_magnitude;
+ else
+ vibrator->magnitude = effect->u.rumble.weak_magnitude;
+
+ mutex_unlock(&vibrator->mutex);
+
+ schedule_work(&vibrator->worker);
+
+ return 0;
+}
+
+static void msm_vibrator_close(struct input_dev *input)
+{
+ struct msm_vibrator *vibrator = input_get_drvdata(input);
+
+ cancel_work_sync(&vibrator->worker);
+ msm_vibrator_stop(vibrator);
+}
+
+static int msm_vibrator_probe(struct platform_device *pdev)
+{
+ struct msm_vibrator *vibrator;
+ struct resource *res;
+ int ret;
+
+ vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL);
+ if (!vibrator)
+ return -ENOMEM;
+
+ vibrator->input = devm_input_allocate_device(&pdev->dev);
+ if (!vibrator->input)
+ return -ENOMEM;
+
+ vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
+ if (IS_ERR(vibrator->vcc)) {
+ if (PTR_ERR(vibrator->vcc) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get regulator: %ld\n",
+ PTR_ERR(vibrator->vcc));
+ return PTR_ERR(vibrator->vcc);
+ }
+
+ vibrator->enable_gpio = devm_gpiod_get(&pdev->dev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(vibrator->enable_gpio)) {
+ if (PTR_ERR(vibrator->enable_gpio) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get enable gpio: %ld\n",
+ PTR_ERR(vibrator->enable_gpio));
+ return PTR_ERR(vibrator->enable_gpio);
+ }
+
+ vibrator->clk = devm_clk_get(&pdev->dev, "pwm");
+ if (IS_ERR(vibrator->clk)) {
+ if (PTR_ERR(vibrator->clk) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to lookup pwm clock: %ld\n",
+ PTR_ERR(vibrator->clk));
+ return PTR_ERR(vibrator->clk);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get platform resource\n");
+ return -ENODEV;
+ }
+
+ vibrator->base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!vibrator->base) {
+ dev_err(&pdev->dev, "Failed to iomap resource.\n");
+ return -ENOMEM;
+ }
+
+ vibrator->enabled = false;
+ mutex_init(&vibrator->mutex);
+ INIT_WORK(&vibrator->worker, msm_vibrator_worker);
+
+ vibrator->input->name = "msm-vibrator";
+ vibrator->input->id.bustype = BUS_HOST;
+ vibrator->input->close = msm_vibrator_close;
+
+ input_set_drvdata(vibrator->input, vibrator);
+ input_set_capability(vibrator->input, EV_FF, FF_RUMBLE);
+
+ ret = input_ff_create_memless(vibrator->input, NULL,
+ msm_vibrator_play_effect);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create ff memless: %d", ret);
+ return ret;
+ }
+
+ ret = input_register_device(vibrator->input);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register input device: %d", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, vibrator);
+
+ return 0;
+}
+
+static int __maybe_unused msm_vibrator_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct msm_vibrator *vibrator = platform_get_drvdata(pdev);
+
+ cancel_work_sync(&vibrator->worker);
+
+ if (vibrator->enabled)
+ msm_vibrator_stop(vibrator);
+
+ return 0;
+}
+
+static int __maybe_unused msm_vibrator_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct msm_vibrator *vibrator = platform_get_drvdata(pdev);
+
+ if (vibrator->enabled)
+ msm_vibrator_start(vibrator);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(msm_vibrator_pm_ops, msm_vibrator_suspend,
+ msm_vibrator_resume);
+
+static const struct of_device_id msm_vibrator_of_match[] = {
+ { .compatible = "qcom,msm8226-vibrator" },
+ { .compatible = "qcom,msm8974-vibrator" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, msm_vibrator_of_match);
+
+static struct platform_driver msm_vibrator_driver = {
+ .probe = msm_vibrator_probe,
+ .driver = {
+ .name = "msm-vibrator",
+ .pm = &msm_vibrator_pm_ops,
+ .of_match_table = of_match_ptr(msm_vibrator_of_match),
+ },
+};
+module_platform_driver(msm_vibrator_driver);
+
+MODULE_AUTHOR("Brian Masney <masneyb@onstation.org>");
+MODULE_DESCRIPTION("Qualcomm MSM vibrator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c
index 23520df7650f..bb458beecb43 100644
--- a/drivers/input/misc/soc_button_array.c
+++ b/drivers/input/misc/soc_button_array.c
@@ -185,6 +185,10 @@ static int soc_button_parse_btn_desc(struct device *dev,
info->name = "power";
info->event_code = KEY_POWER;
info->wakeup = true;
+ } else if (upage == 0x01 && usage == 0xca) {
+ info->name = "rotation lock switch";
+ info->event_type = EV_SW;
+ info->event_code = SW_ROTATE_LOCK;
} else if (upage == 0x07 && usage == 0xe3) {
info->name = "home";
info->event_code = KEY_LEFTMETA;
@@ -373,7 +377,7 @@ static struct soc_button_info soc_button_PNP0C40[] = {
{ "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
{ "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false },
- { "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false },
+ { "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false },
{ }
};
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index 225ae6980182..628ef617bb2f 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -1337,6 +1337,7 @@ static const struct acpi_device_id elan_acpi_id[] = {
{ "ELAN0000", 0 },
{ "ELAN0100", 0 },
{ "ELAN0600", 0 },
+ { "ELAN0601", 0 },
{ "ELAN0602", 0 },
{ "ELAN0605", 0 },
{ "ELAN0608", 0 },
diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c
index 8538318d332c..fa304648d611 100644
--- a/drivers/input/mouse/synaptics_i2c.c
+++ b/drivers/input/mouse/synaptics_i2c.c
@@ -219,7 +219,6 @@ struct synaptics_i2c {
struct i2c_client *client;
struct input_dev *input;
struct delayed_work dwork;
- spinlock_t lock;
int no_data_count;
int no_decel_param;
int reduce_report_param;
@@ -369,23 +368,11 @@ static bool synaptics_i2c_get_input(struct synaptics_i2c *touch)
return xy_delta || gesture;
}
-static void synaptics_i2c_reschedule_work(struct synaptics_i2c *touch,
- unsigned long delay)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&touch->lock, flags);
-
- mod_delayed_work(system_wq, &touch->dwork, delay);
-
- spin_unlock_irqrestore(&touch->lock, flags);
-}
-
static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id)
{
struct synaptics_i2c *touch = dev_id;
- synaptics_i2c_reschedule_work(touch, 0);
+ mod_delayed_work(system_wq, &touch->dwork, 0);
return IRQ_HANDLED;
}
@@ -461,7 +448,7 @@ static void synaptics_i2c_work_handler(struct work_struct *work)
* We poll the device once in THREAD_IRQ_SLEEP_SECS and
* if error is detected, we try to reset and reconfigure the touchpad.
*/
- synaptics_i2c_reschedule_work(touch, delay);
+ mod_delayed_work(system_wq, &touch->dwork, delay);
}
static int synaptics_i2c_open(struct input_dev *input)
@@ -474,7 +461,7 @@ static int synaptics_i2c_open(struct input_dev *input)
return ret;
if (polling_req)
- synaptics_i2c_reschedule_work(touch,
+ mod_delayed_work(system_wq, &touch->dwork,
msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
return 0;
@@ -530,7 +517,6 @@ static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *clien
touch->scan_rate_param = scan_rate;
set_scan_rate(touch, scan_rate);
INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler);
- spin_lock_init(&touch->lock);
return touch;
}
@@ -637,7 +623,7 @@ static int __maybe_unused synaptics_i2c_resume(struct device *dev)
if (ret)
return ret;
- synaptics_i2c_reschedule_work(touch,
+ mod_delayed_work(system_wq, &touch->dwork,
msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
return 0;
diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h
index 796289846204..fce76812843b 100644
--- a/drivers/input/serio/i8042-sparcio.h
+++ b/drivers/input/serio/i8042-sparcio.h
@@ -53,12 +53,11 @@ static struct resource *kbd_res;
static int sparc_i8042_probe(struct platform_device *op)
{
- struct device_node *dp = op->dev.of_node;
+ struct device_node *dp;
- dp = dp->child;
- while (dp) {
- if (!strcmp(dp->name, OBP_PS2KBD_NAME1) ||
- !strcmp(dp->name, OBP_PS2KBD_NAME2)) {
+ for_each_child_of_node(op->dev.of_node, dp) {
+ if (of_node_name_eq(dp, OBP_PS2KBD_NAME1) ||
+ of_node_name_eq(dp, OBP_PS2KBD_NAME2)) {
struct platform_device *kbd = of_find_device_by_node(dp);
unsigned int irq = kbd->archdata.irqs[0];
if (irq == 0xffffffff)
@@ -67,16 +66,14 @@ static int sparc_i8042_probe(struct platform_device *op)
kbd_iobase = of_ioremap(&kbd->resource[0],
0, 8, "kbd");
kbd_res = &kbd->resource[0];
- } else if (!strcmp(dp->name, OBP_PS2MS_NAME1) ||
- !strcmp(dp->name, OBP_PS2MS_NAME2)) {
+ } else if (of_node_name_eq(dp, OBP_PS2MS_NAME1) ||
+ of_node_name_eq(dp, OBP_PS2MS_NAME2)) {
struct platform_device *ms = of_find_device_by_node(dp);
unsigned int irq = ms->archdata.irqs[0];
if (irq == 0xffffffff)
irq = op->archdata.irqs[0];
i8042_aux_irq = irq;
}
-
- dp = dp->sibling;
}
return 0;
@@ -109,8 +106,9 @@ static struct platform_driver sparc_i8042_driver = {
static int __init i8042_platform_init(void)
{
struct device_node *root = of_find_node_by_path("/");
+ const char *name = of_get_property(root, "name", NULL);
- if (!strcmp(root->name, "SUNW,JavaStation-1")) {
+ if (name && !strcmp(name, "SUNW,JavaStation-1")) {
/* Hardcoded values for MrCoffee. */
i8042_kbd_irq = i8042_aux_irq = 13 | 0x20;
kbd_iobase = ioremap(0x71300060, 8);
@@ -139,8 +137,9 @@ static int __init i8042_platform_init(void)
static inline void i8042_platform_exit(void)
{
struct device_node *root = of_find_node_by_path("/");
+ const char *name = of_get_property(root, "name", NULL);
- if (strcmp(root->name, "SUNW,JavaStation-1"))
+ if (!name || strcmp(name, "SUNW,JavaStation-1"))
platform_driver_unregister(&sparc_i8042_driver);
}
diff --git a/drivers/input/tablet/wacom_serial4.c b/drivers/input/tablet/wacom_serial4.c
index 38bfaca48eab..150f9eecaca7 100644
--- a/drivers/input/tablet/wacom_serial4.c
+++ b/drivers/input/tablet/wacom_serial4.c
@@ -187,6 +187,7 @@ enum {
MODEL_DIGITIZER_II = 0x5544, /* UD */
MODEL_GRAPHIRE = 0x4554, /* ET */
MODEL_PENPARTNER = 0x4354, /* CT */
+ MODEL_ARTPAD_II = 0x4B54, /* KT */
};
static void wacom_handle_model_response(struct wacom *wacom)
@@ -245,6 +246,7 @@ static void wacom_handle_model_response(struct wacom *wacom)
wacom->flags = F_HAS_STYLUS2 | F_HAS_SCROLLWHEEL;
break;
+ case MODEL_ARTPAD_II:
case MODEL_DIGITIZER_II:
wacom->dev->name = "Wacom Digitizer II";
wacom->dev->id.version = MODEL_DIGITIZER_II;
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 068dbbc610fc..7a4884ad198b 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -699,6 +699,7 @@ config TOUCHSCREEN_EDT_FT5X06
config TOUCHSCREEN_RASPBERRYPI_FW
tristate "Raspberry Pi's firmware base touch screen support"
depends on RASPBERRYPI_FIRMWARE || (RASPBERRYPI_FIRMWARE=n && COMPILE_TEST)
+ select INPUT_POLLDEV
help
Say Y here if you have the official Raspberry Pi 7 inch screen on
your system.
@@ -1168,11 +1169,11 @@ config TOUCHSCREEN_SIS_I2C
module will be called sis_i2c.
config TOUCHSCREEN_ST1232
- tristate "Sitronix ST1232 touchscreen controllers"
+ tristate "Sitronix ST1232 or ST1633 touchscreen controllers"
depends on I2C
help
- Say Y here if you want to support Sitronix ST1232
- touchscreen controller.
+ Say Y here if you want to support the Sitronix ST1232
+ or ST1633 touchscreen controller.
If unsure, say N.
diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c
index 6fa714c587b4..3a016f43fb85 100644
--- a/drivers/input/touchscreen/ad7879.c
+++ b/drivers/input/touchscreen/ad7879.c
@@ -246,11 +246,14 @@ static void ad7879_timer(struct timer_list *t)
static irqreturn_t ad7879_irq(int irq, void *handle)
{
struct ad7879 *ts = handle;
+ int error;
- regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS,
- ts->conversion_data, AD7879_NR_SENSE);
-
- if (!ad7879_report(ts))
+ error = regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS,
+ ts->conversion_data, AD7879_NR_SENSE);
+ if (error)
+ dev_err_ratelimited(ts->dev, "failed to read %#02x: %d\n",
+ AD7879_REG_XPLUS, error);
+ else if (!ad7879_report(ts))
mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
return IRQ_HANDLED;
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index 1e18ca0d1b4e..702bfda7ee77 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -31,6 +31,7 @@
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/i2c.h>
+#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/debugfs.h>
@@ -53,6 +54,11 @@
#define M09_REGISTER_NUM_X 0x94
#define M09_REGISTER_NUM_Y 0x95
+#define EV_REGISTER_THRESHOLD 0x40
+#define EV_REGISTER_GAIN 0x41
+#define EV_REGISTER_OFFSET_Y 0x45
+#define EV_REGISTER_OFFSET_X 0x46
+
#define NO_REGISTER 0xff
#define WORK_REGISTER_OPMODE 0x3c
@@ -73,6 +79,7 @@ enum edt_ver {
EDT_M06,
EDT_M09,
EDT_M12,
+ EV_FT,
GENERIC_FT,
};
@@ -81,6 +88,8 @@ struct edt_reg_addr {
int reg_report_rate;
int reg_gain;
int reg_offset;
+ int reg_offset_x;
+ int reg_offset_y;
int reg_num_x;
int reg_num_y;
};
@@ -106,6 +115,8 @@ struct edt_ft5x06_ts_data {
int threshold;
int gain;
int offset;
+ int offset_x;
+ int offset_y;
int report_rate;
int max_support_points;
@@ -190,6 +201,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
case EDT_M09:
case EDT_M12:
+ case EV_FT:
case GENERIC_FT:
cmd = 0x0;
offset = 3;
@@ -242,6 +254,10 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
x = ((buf[0] << 8) | buf[1]) & 0x0fff;
y = ((buf[2] << 8) | buf[3]) & 0x0fff;
+ /* The FT5x26 send the y coordinate first */
+ if (tsdata->version == EV_FT)
+ swap(x, y);
+
id = (buf[2] >> 4) & 0x0f;
down = type != TOUCH_EVENT_UP;
@@ -275,8 +291,10 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
return edt_ft5x06_ts_readwrite(tsdata->client, 4,
wrbuf, 0, NULL);
+ /* fallthrough */
case EDT_M09:
case EDT_M12:
+ case EV_FT:
case GENERIC_FT:
wrbuf[0] = addr;
wrbuf[1] = value;
@@ -315,8 +333,10 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
}
break;
+ /* fallthrough */
case EDT_M09:
case EDT_M12:
+ case EV_FT:
case GENERIC_FT:
wrbuf[0] = addr;
error = edt_ft5x06_ts_readwrite(tsdata->client, 1,
@@ -339,9 +359,10 @@ struct edt_ft5x06_attribute {
u8 limit_high;
u8 addr_m06;
u8 addr_m09;
+ u8 addr_ev;
};
-#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, \
+#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, _addr_ev, \
_limit_low, _limit_high) \
struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \
.dattr = __ATTR(_field, _mode, \
@@ -350,6 +371,7 @@ struct edt_ft5x06_attribute {
.field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \
.addr_m06 = _addr_m06, \
.addr_m09 = _addr_m09, \
+ .addr_ev = _addr_ev, \
.limit_low = _limit_low, \
.limit_high = _limit_high, \
}
@@ -386,6 +408,10 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
addr = attr->addr_m09;
break;
+ case EV_FT:
+ addr = attr->addr_ev;
+ break;
+
default:
error = -ENODEV;
goto out;
@@ -457,6 +483,10 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
addr = attr->addr_m09;
break;
+ case EV_FT:
+ addr = attr->addr_ev;
+ break;
+
default:
error = -ENODEV;
goto out;
@@ -480,20 +510,28 @@ out:
/* m06, m09: range 0-31, m12: range 0-5 */
static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN,
- M09_REGISTER_GAIN, 0, 31);
+ M09_REGISTER_GAIN, EV_REGISTER_GAIN, 0, 31);
/* m06, m09: range 0-31, m12: range 0-16 */
static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET,
- M09_REGISTER_OFFSET, 0, 31);
+ M09_REGISTER_OFFSET, NO_REGISTER, 0, 31);
+/* m06, m09, m12: no supported, ev_ft: range 0-80 */
+static EDT_ATTR(offset_x, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER,
+ EV_REGISTER_OFFSET_X, 0, 80);
+/* m06, m09, m12: no supported, ev_ft: range 0-80 */
+static EDT_ATTR(offset_y, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER,
+ EV_REGISTER_OFFSET_Y, 0, 80);
/* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */
static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD,
- M09_REGISTER_THRESHOLD, 0, 255);
+ M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255);
/* m06: range 3 to 14, m12: (0x64: 100Hz) */
static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE,
- NO_REGISTER, 0, 255);
+ NO_REGISTER, NO_REGISTER, 0, 255);
static struct attribute *edt_ft5x06_attrs[] = {
&edt_ft5x06_attr_gain.dattr.attr,
&edt_ft5x06_attr_offset.dattr.attr,
+ &edt_ft5x06_attr_offset_x.dattr.attr,
+ &edt_ft5x06_attr_offset_y.dattr.attr,
&edt_ft5x06_attr_threshold.dattr.attr,
&edt_ft5x06_attr_report_rate.dattr.attr,
NULL
@@ -605,8 +643,15 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
tsdata->threshold);
edt_ft5x06_register_write(tsdata, reg_addr->reg_gain,
tsdata->gain);
- edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
- tsdata->offset);
+ if (reg_addr->reg_offset != NO_REGISTER)
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
+ tsdata->offset);
+ if (reg_addr->reg_offset_x != NO_REGISTER)
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x,
+ tsdata->offset_x);
+ if (reg_addr->reg_offset_y != NO_REGISTER)
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y,
+ tsdata->offset_y);
if (reg_addr->reg_report_rate != NO_REGISTER)
edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
tsdata->report_rate);
@@ -867,6 +912,16 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
case 0x5a: /* Solomon Goldentek Display */
snprintf(model_name, EDT_NAME_LEN, "GKTW50SCED1R0");
break;
+ case 0x59: /* Evervision Display with FT5xx6 TS */
+ tsdata->version = EV_FT;
+ error = edt_ft5x06_ts_readwrite(client, 1, "\x53",
+ 1, rdbuf);
+ if (error)
+ return error;
+ strlcpy(fw_version, rdbuf, 1);
+ snprintf(model_name, EDT_NAME_LEN,
+ "EVERVISION-FT5726NEi");
+ break;
default:
snprintf(model_name, EDT_NAME_LEN,
"generic ft5x06 (%02x)",
@@ -902,6 +957,18 @@ static void edt_ft5x06_ts_get_defaults(struct device *dev,
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, val);
tsdata->offset = val;
}
+
+ error = device_property_read_u32(dev, "offset-x", &val);
+ if (!error) {
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x, val);
+ tsdata->offset_x = val;
+ }
+
+ error = device_property_read_u32(dev, "offset-y", &val);
+ if (!error) {
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y, val);
+ tsdata->offset_y = val;
+ }
}
static void
@@ -912,7 +979,15 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
tsdata->threshold = edt_ft5x06_register_read(tsdata,
reg_addr->reg_threshold);
tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain);
- tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset);
+ if (reg_addr->reg_offset != NO_REGISTER)
+ tsdata->offset =
+ edt_ft5x06_register_read(tsdata, reg_addr->reg_offset);
+ if (reg_addr->reg_offset_x != NO_REGISTER)
+ tsdata->offset_x = edt_ft5x06_register_read(tsdata,
+ reg_addr->reg_offset_x);
+ if (reg_addr->reg_offset_y != NO_REGISTER)
+ tsdata->offset_y = edt_ft5x06_register_read(tsdata,
+ reg_addr->reg_offset_y);
if (reg_addr->reg_report_rate != NO_REGISTER)
tsdata->report_rate = edt_ft5x06_register_read(tsdata,
reg_addr->reg_report_rate);
@@ -940,6 +1015,8 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE;
reg_addr->reg_gain = WORK_REGISTER_GAIN;
reg_addr->reg_offset = WORK_REGISTER_OFFSET;
+ reg_addr->reg_offset_x = NO_REGISTER;
+ reg_addr->reg_offset_y = NO_REGISTER;
reg_addr->reg_num_x = WORK_REGISTER_NUM_X;
reg_addr->reg_num_y = WORK_REGISTER_NUM_Y;
break;
@@ -950,15 +1027,30 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
reg_addr->reg_report_rate = NO_REGISTER;
reg_addr->reg_gain = M09_REGISTER_GAIN;
reg_addr->reg_offset = M09_REGISTER_OFFSET;
+ reg_addr->reg_offset_x = NO_REGISTER;
+ reg_addr->reg_offset_y = NO_REGISTER;
reg_addr->reg_num_x = M09_REGISTER_NUM_X;
reg_addr->reg_num_y = M09_REGISTER_NUM_Y;
break;
+ case EV_FT:
+ reg_addr->reg_threshold = EV_REGISTER_THRESHOLD;
+ reg_addr->reg_gain = EV_REGISTER_GAIN;
+ reg_addr->reg_offset = NO_REGISTER;
+ reg_addr->reg_offset_x = EV_REGISTER_OFFSET_X;
+ reg_addr->reg_offset_y = EV_REGISTER_OFFSET_Y;
+ reg_addr->reg_num_x = NO_REGISTER;
+ reg_addr->reg_num_y = NO_REGISTER;
+ reg_addr->reg_report_rate = NO_REGISTER;
+ break;
+
case GENERIC_FT:
/* this is a guesswork */
reg_addr->reg_threshold = M09_REGISTER_THRESHOLD;
reg_addr->reg_gain = M09_REGISTER_GAIN;
reg_addr->reg_offset = M09_REGISTER_OFFSET;
+ reg_addr->reg_offset_x = NO_REGISTER;
+ reg_addr->reg_offset_y = NO_REGISTER;
break;
}
}
@@ -1155,6 +1247,7 @@ static const struct edt_i2c_chip_data edt_ft6236_data = {
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 },
+ { .name = "ev-ft5726", .driver_data = (long)&edt_ft5506_data },
/* Note no edt- prefix for compatibility with the ft6236.c driver */
{ .name = "ft6236", .driver_data = (long)&edt_ft6236_data },
{ /* sentinel */ }
@@ -1167,6 +1260,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = {
{ .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data },
{ .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data },
{ .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data },
+ { .compatible = "evervision,ev-ft5726", .data = &edt_ft5506_data },
/* Note focaltech vendor prefix for compatibility with ft6236.c */
{ .compatible = "focaltech,ft6236", .data = &edt_ft6236_data },
{ /* sentinel */ }
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index f2d9c2c41885..f57d82220a88 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -216,6 +216,7 @@ static const struct goodix_chip_data *goodix_get_chip_data(u16 id)
{
switch (id) {
case 1151:
+ case 5688:
return &gt1x_chip_data;
case 911:
@@ -692,7 +693,9 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
if (!ts->prop.max_x || !ts->prop.max_y || !ts->max_touch_num) {
- dev_err(&ts->client->dev, "Invalid config, using defaults\n");
+ dev_err(&ts->client->dev,
+ "Invalid config (%d, %d, %d), using defaults\n",
+ ts->prop.max_x, ts->prop.max_y, ts->max_touch_num);
ts->prop.max_x = GOODIX_MAX_WIDTH - 1;
ts->prop.max_y = GOODIX_MAX_HEIGHT - 1;
ts->max_touch_num = GOODIX_MAX_CONTACTS;
@@ -942,6 +945,7 @@ MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
#ifdef CONFIG_OF
static const struct of_device_id goodix_of_match[] = {
{ .compatible = "goodix,gt1151" },
+ { .compatible = "goodix,gt5688" },
{ .compatible = "goodix,gt911" },
{ .compatible = "goodix,gt9110" },
{ .compatible = "goodix,gt912" },
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c
index 6f76eeedf465..9169aa03958a 100644
--- a/drivers/input/touchscreen/ili210x.c
+++ b/drivers/input/touchscreen/ili210x.c
@@ -4,11 +4,15 @@
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
-#include <linux/input/ili210x.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <asm/unaligned.h>
-#define MAX_TOUCHES 2
+#define ILI210X_TOUCHES 2
+#define ILI251X_TOUCHES 10
#define DEFAULT_POLL_PERIOD 20
/* Touchscreen commands */
@@ -17,41 +21,32 @@
#define REG_FIRMWARE_VERSION 0x40
#define REG_CALIBRATE 0xcc
-struct finger {
- u8 x_low;
- u8 x_high;
- u8 y_low;
- u8 y_high;
-} __packed;
-
-struct touchdata {
- u8 status;
- struct finger finger[MAX_TOUCHES];
-} __packed;
-
-struct panel_info {
- struct finger finger_max;
- u8 xchannel_num;
- u8 ychannel_num;
-} __packed;
-
struct firmware_version {
u8 id;
u8 major;
u8 minor;
} __packed;
+enum ili2xxx_model {
+ MODEL_ILI210X,
+ MODEL_ILI251X,
+};
+
struct ili210x {
struct i2c_client *client;
struct input_dev *input;
- bool (*get_pendown_state)(void);
unsigned int poll_period;
struct delayed_work dwork;
+ struct gpio_desc *reset_gpio;
+ struct touchscreen_properties prop;
+ enum ili2xxx_model model;
+ unsigned int max_touches;
};
static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
size_t len)
{
+ struct ili210x *priv = i2c_get_clientdata(client);
struct i2c_msg msg[2] = {
{
.addr = client->addr,
@@ -67,7 +62,38 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
}
};
- if (i2c_transfer(client->adapter, msg, 2) != 2) {
+ if (priv->model == MODEL_ILI251X) {
+ if (i2c_transfer(client->adapter, msg, 1) != 1) {
+ dev_err(&client->dev, "i2c transfer failed\n");
+ return -EIO;
+ }
+
+ usleep_range(5000, 5500);
+
+ if (i2c_transfer(client->adapter, msg + 1, 1) != 1) {
+ dev_err(&client->dev, "i2c transfer failed\n");
+ return -EIO;
+ }
+ } else {
+ if (i2c_transfer(client->adapter, msg, 2) != 2) {
+ dev_err(&client->dev, "i2c transfer failed\n");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int ili210x_read(struct i2c_client *client, void *buf, size_t len)
+{
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ };
+
+ if (i2c_transfer(client->adapter, &msg, 1) != 1) {
dev_err(&client->dev, "i2c transfer failed\n");
return -EIO;
}
@@ -75,42 +101,72 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
return 0;
}
-static void ili210x_report_events(struct input_dev *input,
- const struct touchdata *touchdata)
+static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
+ unsigned int finger,
+ unsigned int *x, unsigned int *y)
{
- int i;
- bool touch;
- unsigned int x, y;
- const struct finger *finger;
+ if (finger >= ILI210X_TOUCHES)
+ return false;
- for (i = 0; i < MAX_TOUCHES; i++) {
- input_mt_slot(input, i);
+ if (touchdata[0] & BIT(finger))
+ return false;
- finger = &touchdata->finger[i];
+ *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0);
+ *y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2);
- touch = touchdata->status & (1 << i);
- input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
- if (touch) {
- x = finger->x_low | (finger->x_high << 8);
- y = finger->y_low | (finger->y_high << 8);
+ return true;
+}
+
+static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
+ unsigned int finger,
+ unsigned int *x, unsigned int *y)
+{
+ if (finger >= ILI251X_TOUCHES)
+ return false;
+
+ *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0);
+ if (!(*x & BIT(15))) /* Touch indication */
+ return false;
- input_report_abs(input, ABS_MT_POSITION_X, x);
- input_report_abs(input, ABS_MT_POSITION_Y, y);
+ *x &= 0x3fff;
+ *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2);
+
+ return true;
+}
+
+static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata)
+{
+ struct input_dev *input = priv->input;
+ int i;
+ bool contact = false, touch = false;
+ unsigned int x = 0, y = 0;
+
+ for (i = 0; i < priv->max_touches; i++) {
+ if (priv->model == MODEL_ILI210X) {
+ touch = ili210x_touchdata_to_coords(priv, touchdata,
+ i, &x, &y);
+ } else if (priv->model == MODEL_ILI251X) {
+ touch = ili251x_touchdata_to_coords(priv, touchdata,
+ i, &x, &y);
+ if (touch)
+ contact = true;
}
+
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+ if (!touch)
+ continue;
+ touchscreen_report_pos(input, &priv->prop, x, y,
+ true);
}
input_mt_report_pointer_emulation(input, false);
input_sync(input);
-}
-static bool get_pendown_state(const struct ili210x *priv)
-{
- bool state = false;
-
- if (priv->get_pendown_state)
- state = priv->get_pendown_state();
+ if (priv->model == MODEL_ILI210X)
+ contact = touchdata[0] & 0xf3;
- return state;
+ return contact;
}
static void ili210x_work(struct work_struct *work)
@@ -118,20 +174,29 @@ static void ili210x_work(struct work_struct *work)
struct ili210x *priv = container_of(work, struct ili210x,
dwork.work);
struct i2c_client *client = priv->client;
- struct touchdata touchdata;
- int error;
+ u8 touchdata[64] = { 0 };
+ bool touch;
+ int error = -EINVAL;
+
+ if (priv->model == MODEL_ILI210X) {
+ error = ili210x_read_reg(client, REG_TOUCHDATA,
+ touchdata, sizeof(touchdata));
+ } else if (priv->model == MODEL_ILI251X) {
+ error = ili210x_read_reg(client, REG_TOUCHDATA,
+ touchdata, 31);
+ if (!error && touchdata[0] == 2)
+ error = ili210x_read(client, &touchdata[31], 20);
+ }
- error = ili210x_read_reg(client, REG_TOUCHDATA,
- &touchdata, sizeof(touchdata));
if (error) {
dev_err(&client->dev,
"Unable to get touchdata, err = %d\n", error);
return;
}
- ili210x_report_events(priv->input, &touchdata);
+ touch = ili210x_report_events(priv, touchdata);
- if ((touchdata.status & 0xf3) || get_pendown_state(priv))
+ if (touch)
schedule_delayed_work(&priv->dwork,
msecs_to_jiffies(priv->poll_period));
}
@@ -180,103 +245,119 @@ static const struct attribute_group ili210x_attr_group = {
.attrs = ili210x_attributes,
};
+static void ili210x_power_down(void *data)
+{
+ struct gpio_desc *reset_gpio = data;
+
+ gpiod_set_value_cansleep(reset_gpio, 1);
+}
+
+static void ili210x_cancel_work(void *data)
+{
+ struct ili210x *priv = data;
+
+ cancel_delayed_work_sync(&priv->dwork);
+}
+
static int ili210x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
- const struct ili210x_platform_data *pdata = dev_get_platdata(dev);
struct ili210x *priv;
+ struct gpio_desc *reset_gpio;
struct input_dev *input;
- struct panel_info panel;
struct firmware_version firmware;
- int xmax, ymax;
+ enum ili2xxx_model model;
int error;
- dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
+ model = (enum ili2xxx_model)id->driver_data;
- if (!pdata) {
- dev_err(dev, "No platform data!\n");
- return -EINVAL;
- }
+ dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
if (client->irq <= 0) {
dev_err(dev, "No IRQ!\n");
return -EINVAL;
}
- /* Get firmware version */
- error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
- &firmware, sizeof(firmware));
- if (error) {
- dev_err(dev, "Failed to get firmware version, err: %d\n",
- error);
- return error;
- }
+ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(reset_gpio))
+ return PTR_ERR(reset_gpio);
- /* get panel info */
- error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel));
- if (error) {
- dev_err(dev, "Failed to get panel information, err: %d\n",
- error);
- return error;
+ if (reset_gpio) {
+ error = devm_add_action_or_reset(dev, ili210x_power_down,
+ reset_gpio);
+ if (error)
+ return error;
+
+ usleep_range(50, 100);
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ msleep(100);
}
- xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8);
- ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8);
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- input = input_allocate_device();
- if (!priv || !input) {
- error = -ENOMEM;
- goto err_free_mem;
- }
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return -ENOMEM;
priv->client = client;
priv->input = input;
- priv->get_pendown_state = pdata->get_pendown_state;
- priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD;
+ priv->poll_period = DEFAULT_POLL_PERIOD;
INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
+ priv->reset_gpio = reset_gpio;
+ priv->model = model;
+ if (model == MODEL_ILI210X)
+ priv->max_touches = ILI210X_TOUCHES;
+ if (model == MODEL_ILI251X)
+ priv->max_touches = ILI251X_TOUCHES;
+
+ i2c_set_clientdata(client, priv);
+
+ /* Get firmware version */
+ error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
+ &firmware, sizeof(firmware));
+ if (error) {
+ dev_err(dev, "Failed to get firmware version, err: %d\n",
+ error);
+ return error;
+ }
/* Setup input device */
input->name = "ILI210x Touchscreen";
input->id.bustype = BUS_I2C;
input->dev.parent = dev;
- __set_bit(EV_SYN, input->evbit);
- __set_bit(EV_KEY, input->evbit);
- __set_bit(EV_ABS, input->evbit);
- __set_bit(BTN_TOUCH, input->keybit);
-
- /* Single touch */
- input_set_abs_params(input, ABS_X, 0, xmax, 0, 0);
- input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
-
/* Multi touch */
- input_mt_init_slots(input, MAX_TOUCHES, 0);
- input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
- input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, 0xffff, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0);
+ touchscreen_parse_properties(input, true, &priv->prop);
+ input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT);
- i2c_set_clientdata(client, priv);
+ error = devm_add_action(dev, ili210x_cancel_work, priv);
+ if (error)
+ return error;
- error = request_irq(client->irq, ili210x_irq, pdata->irq_flags,
- client->name, priv);
+ error = devm_request_irq(dev, client->irq, ili210x_irq, 0,
+ client->name, priv);
if (error) {
dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
error);
- goto err_free_mem;
+ return error;
}
- error = sysfs_create_group(&dev->kobj, &ili210x_attr_group);
+ error = devm_device_add_group(dev, &ili210x_attr_group);
if (error) {
dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
error);
- goto err_free_irq;
+ return error;
}
error = input_register_device(priv->input);
if (error) {
dev_err(dev, "Cannot register input device, err: %d\n", error);
- goto err_remove_sysfs;
+ return error;
}
device_init_wakeup(dev, 1);
@@ -286,28 +367,6 @@ static int ili210x_i2c_probe(struct i2c_client *client,
client->irq, firmware.id, firmware.major, firmware.minor);
return 0;
-
-err_remove_sysfs:
- sysfs_remove_group(&dev->kobj, &ili210x_attr_group);
-err_free_irq:
- free_irq(client->irq, priv);
-err_free_mem:
- input_free_device(input);
- kfree(priv);
- return error;
-}
-
-static int ili210x_i2c_remove(struct i2c_client *client)
-{
- struct ili210x *priv = i2c_get_clientdata(client);
-
- sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group);
- free_irq(priv->client->irq, priv);
- cancel_delayed_work_sync(&priv->dwork);
- input_unregister_device(priv->input);
- kfree(priv);
-
- return 0;
}
static int __maybe_unused ili210x_i2c_suspend(struct device *dev)
@@ -334,19 +393,27 @@ static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm,
ili210x_i2c_suspend, ili210x_i2c_resume);
static const struct i2c_device_id ili210x_i2c_id[] = {
- { "ili210x", 0 },
+ { "ili210x", MODEL_ILI210X },
+ { "ili251x", MODEL_ILI251X },
{ }
};
MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
+static const struct of_device_id ili210x_dt_ids[] = {
+ { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X },
+ { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ili210x_dt_ids);
+
static struct i2c_driver ili210x_ts_driver = {
.driver = {
.name = "ili210x_i2c",
.pm = &ili210x_i2c_pm,
+ .of_match_table = ili210x_dt_ids,
},
.id_table = ili210x_i2c_id,
.probe = ili210x_i2c_probe,
- .remove = ili210x_i2c_remove,
};
module_i2c_driver(ili210x_ts_driver);
diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c
index 11ff32c68025..34923399ece4 100644
--- a/drivers/input/touchscreen/st1232.c
+++ b/drivers/input/touchscreen/st1232.c
@@ -11,25 +11,19 @@
*/
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/input/touchscreen.h>
#define ST1232_TS_NAME "st1232-ts"
-
-#define MIN_X 0x00
-#define MIN_Y 0x00
-#define MAX_X 0x31f /* (800 - 1) */
-#define MAX_Y 0x1df /* (480 - 1) */
-#define MAX_AREA 0xff
-#define MAX_FINGERS 2
+#define ST1633_TS_NAME "st1633-ts"
struct st1232_ts_finger {
u16 x;
@@ -38,12 +32,25 @@ struct st1232_ts_finger {
bool is_valid;
};
+struct st_chip_info {
+ bool have_z;
+ u16 max_x;
+ u16 max_y;
+ u16 max_area;
+ u16 max_fingers;
+ u8 start_reg;
+};
+
struct st1232_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
- struct st1232_ts_finger finger[MAX_FINGERS];
+ struct touchscreen_properties prop;
struct dev_pm_qos_request low_latency_req;
- int reset_gpio;
+ struct gpio_desc *reset_gpio;
+ const struct st_chip_info *chip_info;
+ int read_buf_len;
+ u8 *read_buf;
+ struct st1232_ts_finger *finger;
};
static int st1232_ts_read_data(struct st1232_ts_data *ts)
@@ -52,40 +59,35 @@ static int st1232_ts_read_data(struct st1232_ts_data *ts)
struct i2c_client *client = ts->client;
struct i2c_msg msg[2];
int error;
- u8 start_reg;
- u8 buf[10];
+ int i, y;
+ u8 start_reg = ts->chip_info->start_reg;
+ u8 *buf = ts->read_buf;
- /* read touchscreen data from ST1232 */
+ /* read touchscreen data */
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = &start_reg;
- start_reg = 0x10;
msg[1].addr = ts->client->addr;
msg[1].flags = I2C_M_RD;
- msg[1].len = sizeof(buf);
+ msg[1].len = ts->read_buf_len;
msg[1].buf = buf;
error = i2c_transfer(client->adapter, msg, 2);
if (error < 0)
return error;
- /* get "valid" bits */
- finger[0].is_valid = buf[2] >> 7;
- finger[1].is_valid = buf[5] >> 7;
+ for (i = 0, y = 0; i < ts->chip_info->max_fingers; i++, y += 3) {
+ finger[i].is_valid = buf[i + y] >> 7;
+ if (finger[i].is_valid) {
+ finger[i].x = ((buf[i + y] & 0x0070) << 4) | buf[i + 1];
+ finger[i].y = ((buf[i + y] & 0x0007) << 8) | buf[i + 2];
- /* get xy coordinate */
- if (finger[0].is_valid) {
- finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3];
- finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4];
- finger[0].t = buf[8];
- }
-
- if (finger[1].is_valid) {
- finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6];
- finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7];
- finger[1].t = buf[9];
+ /* st1232 includes a z-axis / touch strength */
+ if (ts->chip_info->have_z)
+ finger[i].t = buf[i + 6];
+ }
}
return 0;
@@ -104,13 +106,16 @@ static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
goto end;
/* multi touch protocol */
- for (i = 0; i < MAX_FINGERS; i++) {
+ for (i = 0; i < ts->chip_info->max_fingers; i++) {
if (!finger[i].is_valid)
continue;
- input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);
- input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
- input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
+ if (ts->chip_info->have_z)
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
+ finger[i].t);
+
+ touchscreen_report_pos(input_dev, &ts->prop,
+ finger[i].x, finger[i].y, true);
input_mt_sync(input_dev);
count++;
}
@@ -138,17 +143,45 @@ end:
static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron)
{
- if (gpio_is_valid(ts->reset_gpio))
- gpio_direction_output(ts->reset_gpio, poweron);
+ if (ts->reset_gpio)
+ gpiod_set_value_cansleep(ts->reset_gpio, !poweron);
}
+static const struct st_chip_info st1232_chip_info = {
+ .have_z = true,
+ .max_x = 0x31f, /* 800 - 1 */
+ .max_y = 0x1df, /* 480 -1 */
+ .max_area = 0xff,
+ .max_fingers = 2,
+ .start_reg = 0x12,
+};
+
+static const struct st_chip_info st1633_chip_info = {
+ .have_z = false,
+ .max_x = 0x13f, /* 320 - 1 */
+ .max_y = 0x1df, /* 480 -1 */
+ .max_area = 0x00,
+ .max_fingers = 5,
+ .start_reg = 0x12,
+};
+
static int st1232_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ const struct st_chip_info *match;
struct st1232_ts_data *ts;
+ struct st1232_ts_finger *finger;
struct input_dev *input_dev;
int error;
+ match = device_get_match_data(&client->dev);
+ if (!match && id)
+ match = (const void *)id->driver_data;
+ if (!match) {
+ dev_err(&client->dev, "unknown device model\n");
+ return -ENODEV;
+ }
+
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "need I2C_FUNC_I2C\n");
return -EIO;
@@ -163,6 +196,19 @@ static int st1232_ts_probe(struct i2c_client *client,
if (!ts)
return -ENOMEM;
+ ts->chip_info = match;
+ ts->finger = devm_kcalloc(&client->dev,
+ ts->chip_info->max_fingers, sizeof(*finger),
+ GFP_KERNEL);
+ if (!ts->finger)
+ return -ENOMEM;
+
+ /* allocate a buffer according to the number of registers to read */
+ ts->read_buf_len = ts->chip_info->max_fingers * 4;
+ ts->read_buf = devm_kzalloc(&client->dev, ts->read_buf_len, GFP_KERNEL);
+ if (!ts->read_buf)
+ return -ENOMEM;
+
input_dev = devm_input_allocate_device(&client->dev);
if (!input_dev)
return -ENOMEM;
@@ -170,15 +216,13 @@ static int st1232_ts_probe(struct i2c_client *client,
ts->client = client;
ts->input_dev = input_dev;
- ts->reset_gpio = of_get_gpio(client->dev.of_node, 0);
- if (gpio_is_valid(ts->reset_gpio)) {
- error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL);
- if (error) {
- dev_err(&client->dev,
- "Unable to request GPIO pin %d.\n",
- ts->reset_gpio);
- return error;
- }
+ ts->reset_gpio = devm_gpiod_get_optional(&client->dev, NULL,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ts->reset_gpio)) {
+ error = PTR_ERR(ts->reset_gpio);
+ dev_err(&client->dev, "Unable to request GPIO pin: %d.\n",
+ error);
+ return error;
}
st1232_ts_power(ts, true);
@@ -192,9 +236,16 @@ static int st1232_ts_probe(struct i2c_client *client,
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(EV_ABS, input_dev->evbit);
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0);
+ if (ts->chip_info->have_z)
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
+ ts->chip_info->max_area, 0, 0);
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, ts->chip_info->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, ts->chip_info->max_y, 0, 0);
+
+ touchscreen_parse_properties(input_dev, true, &ts->prop);
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, st1232_ts_irq_handler,
@@ -261,13 +312,15 @@ static SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops,
st1232_ts_suspend, st1232_ts_resume);
static const struct i2c_device_id st1232_ts_id[] = {
- { ST1232_TS_NAME, 0 },
+ { ST1232_TS_NAME, (unsigned long)&st1232_chip_info },
+ { ST1633_TS_NAME, (unsigned long)&st1633_chip_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, st1232_ts_id);
static const struct of_device_id st1232_ts_dt_ids[] = {
- { .compatible = "sitronix,st1232", },
+ { .compatible = "sitronix,st1232", .data = &st1232_chip_info },
+ { .compatible = "sitronix,st1633", .data = &st1633_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids);
@@ -286,5 +339,6 @@ static struct i2c_driver st1232_ts_driver = {
module_i2c_driver(st1232_ts_driver);
MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>");
+MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@ginzinger.com>");
MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
index 704e99046916..b6f95f20f924 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -106,27 +106,29 @@ struct stmfts_data {
bool running;
};
-static void stmfts_brightness_set(struct led_classdev *led_cdev,
+static int stmfts_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct stmfts_data *sdata = container_of(led_cdev,
struct stmfts_data, led_cdev);
int err;
- if (value == sdata->led_status || !sdata->ledvdd)
- return;
-
- if (!value) {
- regulator_disable(sdata->ledvdd);
- } else {
- err = regulator_enable(sdata->ledvdd);
- if (err)
- dev_warn(&sdata->client->dev,
- "failed to disable ledvdd regulator: %d\n",
- err);
+ if (value != sdata->led_status && sdata->ledvdd) {
+ if (!value) {
+ regulator_disable(sdata->ledvdd);
+ } else {
+ err = regulator_enable(sdata->ledvdd);
+ if (err) {
+ dev_warn(&sdata->client->dev,
+ "failed to disable ledvdd regulator: %d\n",
+ err);
+ return err;
+ }
+ }
+ sdata->led_status = value;
}
- sdata->led_status = value;
+ return 0;
}
static enum led_brightness stmfts_brightness_get(struct led_classdev *led_cdev)
@@ -608,7 +610,7 @@ static int stmfts_enable_led(struct stmfts_data *sdata)
sdata->led_cdev.name = STMFTS_DEV_NAME;
sdata->led_cdev.max_brightness = LED_ON;
sdata->led_cdev.brightness = LED_OFF;
- sdata->led_cdev.brightness_set = stmfts_brightness_set;
+ sdata->led_cdev.brightness_set_blocking = stmfts_brightness_set;
sdata->led_cdev.brightness_get = stmfts_brightness_get;
err = devm_led_classdev_register(&sdata->client->dev, &sdata->led_cdev);
diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
index ed29db3ec731..dbdf4898aa17 100644
--- a/drivers/input/touchscreen/sx8654.c
+++ b/drivers/input/touchscreen/sx8654.c
@@ -27,12 +27,16 @@
* published by the Free Software Foundation.
*/
-#include <linux/input.h>
-#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/touchscreen.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
/* register addresses */
#define I2C_REG_TOUCH0 0x00
@@ -42,25 +46,28 @@
#define I2C_REG_IRQSRC 0x23
#define I2C_REG_SOFTRESET 0x3f
+#define I2C_REG_SX8650_STAT 0x05
+#define SX8650_STAT_CONVIRQ BIT(7)
+
/* commands */
#define CMD_READ_REGISTER 0x40
-#define CMD_MANUAL 0xc0
#define CMD_PENTRG 0xe0
/* value for I2C_REG_SOFTRESET */
#define SOFTRESET_VALUE 0xde
/* bits for I2C_REG_IRQSRC */
-#define IRQ_PENTOUCH_TOUCHCONVDONE 0x08
-#define IRQ_PENRELEASE 0x04
+#define IRQ_PENTOUCH_TOUCHCONVDONE BIT(3)
+#define IRQ_PENRELEASE BIT(2)
/* bits for RegTouch1 */
#define CONDIRQ 0x20
+#define RPDNT_100K 0x00
#define FILT_7SA 0x03
/* bits for I2C_REG_CHANMASK */
-#define CONV_X 0x80
-#define CONV_Y 0x40
+#define CONV_X BIT(7)
+#define CONV_Y BIT(6)
/* coordinates rate: higher nibble of CTRL0 register */
#define RATE_MANUAL 0x00
@@ -69,13 +76,122 @@
/* power delay: lower nibble of CTRL0 register */
#define POWDLY_1_1MS 0x0b
+/* for sx8650, as we have no pen release IRQ there: timeout in ns following the
+ * last PENIRQ after which we assume the pen is lifted.
+ */
+#define SX8650_PENIRQ_TIMEOUT msecs_to_jiffies(10)
+
#define MAX_12BIT ((1 << 12) - 1)
+#define MAX_I2C_READ_LEN 10 /* see datasheet section 5.1.5 */
+
+/* channel definition */
+#define CH_X 0x00
+#define CH_Y 0x01
+
+struct sx865x_data {
+ u8 cmd_manual;
+ u8 chan_mask;
+ bool has_irq_penrelease;
+ bool has_reg_irqmask;
+ irq_handler_t irqh;
+};
struct sx8654 {
struct input_dev *input;
struct i2c_client *client;
+ struct gpio_desc *gpio_reset;
+
+ spinlock_t lock; /* for input reporting from irq/timer */
+ struct timer_list timer;
+
+ struct touchscreen_properties props;
+
+ const struct sx865x_data *data;
};
+static inline void sx865x_penrelease(struct sx8654 *ts)
+{
+ struct input_dev *input_dev = ts->input;
+
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_sync(input_dev);
+}
+
+static void sx865x_penrelease_timer_handler(struct timer_list *t)
+{
+ struct sx8654 *ts = from_timer(ts, t, timer);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ts->lock, flags);
+ sx865x_penrelease(ts);
+ spin_unlock_irqrestore(&ts->lock, flags);
+ dev_dbg(&ts->client->dev, "penrelease by timer\n");
+}
+
+static irqreturn_t sx8650_irq(int irq, void *handle)
+{
+ struct sx8654 *ts = handle;
+ struct device *dev = &ts->client->dev;
+ int len, i;
+ unsigned long flags;
+ u8 stat;
+ u16 x, y;
+ u16 ch;
+ u16 chdata;
+ __be16 data[MAX_I2C_READ_LEN / sizeof(__be16)];
+ u8 nchan = hweight32(ts->data->chan_mask);
+ u8 readlen = nchan * sizeof(*data);
+
+ stat = i2c_smbus_read_byte_data(ts->client, CMD_READ_REGISTER
+ | I2C_REG_SX8650_STAT);
+
+ if (!(stat & SX8650_STAT_CONVIRQ)) {
+ dev_dbg(dev, "%s ignore stat [0x%02x]", __func__, stat);
+ return IRQ_HANDLED;
+ }
+
+ len = i2c_master_recv(ts->client, (u8 *)data, readlen);
+ if (len != readlen) {
+ dev_dbg(dev, "ignore short recv (%d)\n", len);
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&ts->lock, flags);
+
+ x = 0;
+ y = 0;
+ for (i = 0; i < nchan; i++) {
+ chdata = be16_to_cpu(data[i]);
+
+ if (unlikely(chdata == 0xFFFF)) {
+ dev_dbg(dev, "invalid qualified data @ %d\n", i);
+ continue;
+ } else if (unlikely(chdata & 0x8000)) {
+ dev_warn(dev, "hibit @ %d [0x%04x]\n", i, chdata);
+ continue;
+ }
+
+ ch = chdata >> 12;
+ if (ch == CH_X)
+ x = chdata & MAX_12BIT;
+ else if (ch == CH_Y)
+ y = chdata & MAX_12BIT;
+ else
+ dev_warn(dev, "unknown channel %d [0x%04x]\n", ch,
+ chdata);
+ }
+
+ touchscreen_report_pos(ts->input, &ts->props, x, y, false);
+ input_report_key(ts->input, BTN_TOUCH, 1);
+ input_sync(ts->input);
+ dev_dbg(dev, "point(%4d,%4d)\n", x, y);
+
+ mod_timer(&ts->timer, jiffies + SX8650_PENIRQ_TIMEOUT);
+ spin_unlock_irqrestore(&ts->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t sx8654_irq(int irq, void *handle)
{
struct sx8654 *sx8654 = handle;
@@ -112,8 +228,8 @@ static irqreturn_t sx8654_irq(int irq, void *handle)
x = ((data[0] & 0xf) << 8) | (data[1]);
y = ((data[2] & 0xf) << 8) | (data[3]);
- input_report_abs(sx8654->input, ABS_X, x);
- input_report_abs(sx8654->input, ABS_Y, y);
+ touchscreen_report_pos(sx8654->input, &sx8654->props, x, y,
+ false);
input_report_key(sx8654->input, BTN_TOUCH, 1);
input_sync(sx8654->input);
@@ -124,6 +240,25 @@ out:
return IRQ_HANDLED;
}
+static int sx8654_reset(struct sx8654 *ts)
+{
+ int err;
+
+ if (ts->gpio_reset) {
+ gpiod_set_value_cansleep(ts->gpio_reset, 1);
+ udelay(2); /* Tpulse > 1µs */
+ gpiod_set_value_cansleep(ts->gpio_reset, 0);
+ } else {
+ dev_dbg(&ts->client->dev, "NRST unavailable, try softreset\n");
+ err = i2c_smbus_write_byte_data(ts->client, I2C_REG_SOFTRESET,
+ SOFTRESET_VALUE);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static int sx8654_open(struct input_dev *dev)
{
struct sx8654 *sx8654 = input_get_drvdata(dev);
@@ -157,14 +292,17 @@ static void sx8654_close(struct input_dev *dev)
disable_irq(client->irq);
+ if (!sx8654->data->has_irq_penrelease)
+ del_timer_sync(&sx8654->timer);
+
/* enable manual mode mode */
- error = i2c_smbus_write_byte(client, CMD_MANUAL);
+ error = i2c_smbus_write_byte(client, sx8654->data->cmd_manual);
if (error) {
dev_err(&client->dev, "writing command CMD_MANUAL failed");
return;
}
- error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0);
+ error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, RATE_MANUAL);
if (error) {
dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
return;
@@ -186,6 +324,31 @@ static int sx8654_probe(struct i2c_client *client,
if (!sx8654)
return -ENOMEM;
+ sx8654->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(sx8654->gpio_reset)) {
+ error = PTR_ERR(sx8654->gpio_reset);
+ if (error != -EPROBE_DEFER)
+ dev_err(&client->dev, "unable to get reset-gpio: %d\n",
+ error);
+ return error;
+ }
+ dev_dbg(&client->dev, "got GPIO reset pin\n");
+
+ sx8654->data = device_get_match_data(&client->dev);
+ if (!sx8654->data)
+ sx8654->data = (const struct sx865x_data *)id->driver_data;
+ if (!sx8654->data) {
+ dev_err(&client->dev, "invalid or missing device data\n");
+ return -EINVAL;
+ }
+
+ if (!sx8654->data->has_irq_penrelease) {
+ dev_dbg(&client->dev, "use timer for penrelease\n");
+ timer_setup(&sx8654->timer, sx865x_penrelease_timer_handler, 0);
+ spin_lock_init(&sx8654->lock);
+ }
+
input = devm_input_allocate_device(&client->dev);
if (!input)
return -ENOMEM;
@@ -201,43 +364,46 @@ static int sx8654_probe(struct i2c_client *client,
input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0);
input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0);
+ touchscreen_parse_properties(input, false, &sx8654->props);
+
sx8654->client = client;
sx8654->input = input;
input_set_drvdata(sx8654->input, sx8654);
- error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET,
- SOFTRESET_VALUE);
+ error = sx8654_reset(sx8654);
if (error) {
- dev_err(&client->dev, "writing softreset value failed");
+ dev_err(&client->dev, "reset failed");
return error;
}
error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
- CONV_X | CONV_Y);
+ sx8654->data->chan_mask);
if (error) {
dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
return error;
}
- error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
- IRQ_PENTOUCH_TOUCHCONVDONE |
- IRQ_PENRELEASE);
- if (error) {
- dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed");
- return error;
+ if (sx8654->data->has_reg_irqmask) {
+ error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
+ IRQ_PENTOUCH_TOUCHCONVDONE |
+ IRQ_PENRELEASE);
+ if (error) {
+ dev_err(&client->dev, "writing I2C_REG_IRQMASK failed");
+ return error;
+ }
}
error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1,
- CONDIRQ | FILT_7SA);
+ CONDIRQ | RPDNT_100K | FILT_7SA);
if (error) {
dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed");
return error;
}
error = devm_request_threaded_irq(&client->dev, client->irq,
- NULL, sx8654_irq,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ NULL, sx8654->data->irqh,
+ IRQF_ONESHOT,
client->name, sx8654);
if (error) {
dev_err(&client->dev,
@@ -256,17 +422,48 @@ static int sx8654_probe(struct i2c_client *client,
return 0;
}
+static const struct sx865x_data sx8650_data = {
+ .cmd_manual = 0xb0,
+ .has_irq_penrelease = false,
+ .has_reg_irqmask = false,
+ .chan_mask = (CONV_X | CONV_Y),
+ .irqh = sx8650_irq,
+};
+
+static const struct sx865x_data sx8654_data = {
+ .cmd_manual = 0xc0,
+ .has_irq_penrelease = true,
+ .has_reg_irqmask = true,
+ .chan_mask = (CONV_X | CONV_Y),
+ .irqh = sx8654_irq,
+};
+
#ifdef CONFIG_OF
static const struct of_device_id sx8654_of_match[] = {
- { .compatible = "semtech,sx8654", },
- { },
+ {
+ .compatible = "semtech,sx8650",
+ .data = &sx8650_data,
+ }, {
+ .compatible = "semtech,sx8654",
+ .data = &sx8654_data,
+ }, {
+ .compatible = "semtech,sx8655",
+ .data = &sx8654_data,
+ }, {
+ .compatible = "semtech,sx8656",
+ .data = &sx8654_data,
+ },
+ { }
};
MODULE_DEVICE_TABLE(of, sx8654_of_match);
#endif
static const struct i2c_device_id sx8654_id_table[] = {
- { "semtech_sx8654", 0 },
- { },
+ { .name = "semtech_sx8650", .driver_data = (long)&sx8650_data },
+ { .name = "semtech_sx8654", .driver_data = (long)&sx8654_data },
+ { .name = "semtech_sx8655", .driver_data = (long)&sx8654_data },
+ { .name = "semtech_sx8656", .driver_data = (long)&sx8654_data },
+ { }
};
MODULE_DEVICE_TABLE(i2c, sx8654_id_table);
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index 9e8684ab48f4..83e685557a19 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -507,10 +507,8 @@ static int titsc_remove(struct platform_device *pdev)
static int __maybe_unused titsc_suspend(struct device *dev)
{
struct titsc *ts_dev = dev_get_drvdata(dev);
- struct ti_tscadc_dev *tscadc_dev;
unsigned int idle;
- tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
if (device_may_wakeup(dev)) {
titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK);
idle = titsc_readl(ts_dev, REG_IRQENABLE);
@@ -524,9 +522,7 @@ static int __maybe_unused titsc_suspend(struct device *dev)
static int __maybe_unused titsc_resume(struct device *dev)
{
struct titsc *ts_dev = dev_get_drvdata(dev);
- struct ti_tscadc_dev *tscadc_dev;
- tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
if (device_may_wakeup(dev)) {
titsc_writel(ts_dev, REG_IRQWAKEUP,
0x00);
diff --git a/include/linux/input/ili210x.h b/include/linux/input/ili210x.h
deleted file mode 100644
index b76e7c1404cd..000000000000
--- a/include/linux/input/ili210x.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _ILI210X_H
-#define _ILI210X_H
-
-struct ili210x_platform_data {
- unsigned long irq_flags;
- unsigned int poll_period;
- bool (*get_pendown_state)(void);
-};
-
-#endif