Input: add support for Semtech SX8654 I2C touchscreen controller
authorSébastien Szymanski <sebastien.szymanski@armadeus.com>
Sat, 7 Mar 2015 00:49:38 +0000 (16:49 -0800)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Sat, 7 Mar 2015 01:18:17 +0000 (17:18 -0800)
Signed-off-by: Sébastien Szymanski <sebastien.szymanski@armadeus.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Documentation/devicetree/bindings/input/touchscreen/sx8654.txt [new file with mode: 0644]
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/sx8654.c [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt
new file mode 100644 (file)
index 0000000..5aaa6b3
--- /dev/null
@@ -0,0 +1,16 @@
+* Semtech SX8654 I2C Touchscreen Controller
+
+Required properties:
+- compatible: must be "semtech,sx8654"
+- reg: i2c slave address
+- interrupt-parent: the phandle for the interrupt controller
+- interrupts: touch controller interrupt
+
+Example:
+
+       sx8654@48 {
+               compatible = "semtech,sx8654";
+               reg = <0x48>;
+               interrupt-parent = <&gpio6>;
+               interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
+       };
index 2adf7244b0255e14be024381c353151cb4f54a05..2310d863a6f1d62ad5d23c3141ad0767b44d1c7b 100644 (file)
@@ -962,6 +962,17 @@ config TOUCHSCREEN_SUR40
          To compile this driver as a module, choose M here: the
          module will be called sur40.
 
+config TOUCHSCREEN_SX8654
+       tristate "Semtech SX8654 touchscreen"
+       depends on I2C
+       help
+         Say Y here if you have a Semtech SX8654 touchscreen controller.
+
+         If unsure, say N
+
+         To compile this driver as a module, choose M here: the
+         module will be called sx8654.
+
 config TOUCHSCREEN_TPS6507X
        tristate "TPS6507x based touchscreens"
        depends on I2C
index 0242fea2102ac7cc5782224cb4a6705499c9431d..a06a752966fecd4eaf59fdb8ceee7ba68d1baf1d 100644 (file)
@@ -79,5 +79,6 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL)        += atmel-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)     += mainstone-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)      += zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)      += w90p910_ts.o
+obj-$(CONFIG_TOUCHSCREEN_SX8654)       += sx8654.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)     += tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)       += zforce_ts.o
diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
new file mode 100644 (file)
index 0000000..8e531ac
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Driver for Semtech SX8654 I2C touchscreen controller.
+ *
+ * Copyright (c) 2015 Armadeus Systems
+ *     Sébastien Szymanski <sebastien.szymanski@armadeus.com>
+ *
+ * Using code from:
+ *  - sx865x.c
+ *     Copyright (c) 2013 U-MoBo Srl
+ *     Pierluigi Passaro <p.passaro@u-mobo.com>
+ *  - sx8650.c
+ *      Copyright (c) 2009 Wayne Roberts
+ *  - tsc2007.c
+ *      Copyright (c) 2008 Kwangwoo Lee
+ *  - ads7846.c
+ *      Copyright (c) 2005 David Brownell
+ *      Copyright (c) 2006 Nokia Corporation
+ *  - corgi_ts.c
+ *      Copyright (C) 2004-2005 Richard Purdie
+ *  - omap_ts.[hc], ads7846.h, ts_osk.c
+ *      Copyright (C) 2002 MontaVista Software
+ *      Copyright (C) 2004 Texas Instruments
+ *      Copyright (C) 2005 Dirk Behme
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+/* register addresses */
+#define I2C_REG_TOUCH0                 0x00
+#define I2C_REG_TOUCH1                 0x01
+#define I2C_REG_CHANMASK               0x04
+#define I2C_REG_IRQMASK                        0x22
+#define I2C_REG_IRQSRC                 0x23
+#define I2C_REG_SOFTRESET              0x3f
+
+/* 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
+
+/* bits for RegTouch1 */
+#define CONDIRQ                                0x20
+#define FILT_7SA                       0x03
+
+/* bits for I2C_REG_CHANMASK */
+#define CONV_X                         0x80
+#define CONV_Y                         0x40
+
+/* coordinates rate: higher nibble of CTRL0 register */
+#define RATE_MANUAL                    0x00
+#define RATE_5000CPS                   0xf0
+
+/* power delay: lower nibble of CTRL0 register */
+#define POWDLY_1_1MS                   0x0b
+
+#define MAX_12BIT                      ((1 << 12) - 1)
+
+struct sx8654 {
+       struct input_dev *input;
+       struct i2c_client *client;
+};
+
+static irqreturn_t sx8654_irq(int irq, void *handle)
+{
+       struct sx8654 *sx8654 = handle;
+       u8 irqsrc;
+       u8 data[4];
+       unsigned int x, y;
+       int retval;
+
+       irqsrc = i2c_smbus_read_byte_data(sx8654->client,
+                                         CMD_READ_REGISTER | I2C_REG_IRQSRC);
+       dev_dbg(&sx8654->client->dev, "irqsrc = 0x%x", irqsrc);
+
+       if (irqsrc < 0)
+               goto out;
+
+       if (irqsrc & IRQ_PENRELEASE) {
+               dev_dbg(&sx8654->client->dev, "pen release interrupt");
+
+               input_report_key(sx8654->input, BTN_TOUCH, 0);
+               input_sync(sx8654->input);
+       }
+
+       if (irqsrc & IRQ_PENTOUCH_TOUCHCONVDONE) {
+               dev_dbg(&sx8654->client->dev, "pen touch interrupt");
+
+               retval = i2c_master_recv(sx8654->client, data, sizeof(data));
+               if (retval != sizeof(data))
+                       goto out;
+
+               /* invalid data */
+               if (unlikely(data[0] & 0x80 || data[2] & 0x80))
+                       goto out;
+
+               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);
+               input_report_key(sx8654->input, BTN_TOUCH, 1);
+               input_sync(sx8654->input);
+
+               dev_dbg(&sx8654->client->dev, "point(%4d,%4d)\n", x, y);
+       }
+
+out:
+       return IRQ_HANDLED;
+}
+
+static int sx8654_open(struct input_dev *dev)
+{
+       struct sx8654 *sx8654 = input_get_drvdata(dev);
+       struct i2c_client *client = sx8654->client;
+       int error;
+
+       /* enable pen trigger mode */
+       error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0,
+                                         RATE_5000CPS | POWDLY_1_1MS);
+       if (error) {
+               dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
+               return error;
+       }
+
+       error = i2c_smbus_write_byte(client, CMD_PENTRG);
+       if (error) {
+               dev_err(&client->dev, "writing command CMD_PENTRG failed");
+               return error;
+       }
+
+       enable_irq(client->irq);
+
+       return 0;
+}
+
+static void sx8654_close(struct input_dev *dev)
+{
+       struct sx8654 *sx8654 = input_get_drvdata(dev);
+       struct i2c_client *client = sx8654->client;
+       int error;
+
+       disable_irq(client->irq);
+
+       /* enable manual mode mode */
+       error = i2c_smbus_write_byte(client, 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);
+       if (error) {
+               dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
+               return;
+       }
+}
+
+static int sx8654_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct sx8654 *sx8654;
+       struct input_dev *input;
+       int error;
+
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_READ_WORD_DATA))
+               return -ENXIO;
+
+       sx8654 = devm_kzalloc(&client->dev, sizeof(*sx8654), GFP_KERNEL);
+       if (!sx8654)
+               return -ENOMEM;
+
+       input = devm_input_allocate_device(&client->dev);
+       if (!sx8654)
+               return -ENOMEM;
+
+       input->name = "SX8654 I2C Touchscreen";
+       input->id.bustype = BUS_I2C;
+       input->dev.parent = &client->dev;
+       input->open = sx8654_open;
+       input->close = sx8654_close;
+
+       __set_bit(INPUT_PROP_DIRECT, input->propbit);
+       input_set_capability(input, EV_KEY, BTN_TOUCH);
+       input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0);
+       input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0);
+
+       sx8654->client = client;
+       sx8654->input = input;
+
+       input_set_drvdata(sx8654->input, sx8654);
+
+       error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET,
+                                         SOFTRESET_VALUE);
+       if (error) {
+               dev_err(&client->dev, "writing softreset value failed");
+               return error;
+       }
+
+       error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
+                                         CONV_X | CONV_Y);
+       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;
+       }
+
+       error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1,
+                                         CONDIRQ | 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,
+                                         client->name, sx8654);
+       if (error) {
+               dev_err(&client->dev,
+                       "Failed to enable IRQ %d, error: %d\n",
+                       client->irq, error);
+               return error;
+       }
+
+       /* Disable the IRQ, we'll enable it in sx8654_open() */
+       disable_irq(client->irq);
+
+       error = input_register_device(sx8654->input);
+       if (error)
+               return error;
+
+       i2c_set_clientdata(client, sx8654);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sx8654_of_match[] = {
+       { .compatible = "semtech,sx8654", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, sx8654_of_match);
+#endif
+
+static const struct i2c_device_id sx8654_id_table[] = {
+       { "semtech_sx8654", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, sx8654_id_table);
+
+static struct i2c_driver sx8654_driver = {
+       .driver = {
+               .name = "sx8654",
+               .of_match_table = of_match_ptr(sx8654_of_match),
+       },
+       .id_table = sx8654_id_table,
+       .probe = sx8654_probe,
+};
+module_i2c_driver(sx8654_driver);
+
+MODULE_AUTHOR("Sébastien Szymanski <sebastien.szymanski@armadeus.com>");
+MODULE_DESCRIPTION("Semtech SX8654 I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
This page took 0.030386 seconds and 5 git commands to generate.