summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
commit57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch)
tree5e910f0e82173f4ef4f51111366a3f1299037a7b /drivers/input/touchscreen
Initial import
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r--drivers/input/touchscreen/88pm860x-ts.c304
-rw-r--r--drivers/input/touchscreen/Kconfig1030
-rw-r--r--drivers/input/touchscreen/Makefile86
-rw-r--r--drivers/input/touchscreen/ad7877.c858
-rw-r--r--drivers/input/touchscreen/ad7879-i2c.c109
-rw-r--r--drivers/input/touchscreen/ad7879-spi.c164
-rw-r--r--drivers/input/touchscreen/ad7879.c647
-rw-r--r--drivers/input/touchscreen/ad7879.h30
-rw-r--r--drivers/input/touchscreen/ads7846.c1510
-rw-r--r--drivers/input/touchscreen/ar1021_i2c.c181
-rw-r--r--drivers/input/touchscreen/atmel-wm97xx.c436
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c2683
-rw-r--r--drivers/input/touchscreen/auo-pixcir-ts.c702
-rw-r--r--drivers/input/touchscreen/bcm_iproc_tsc.c522
-rw-r--r--drivers/input/touchscreen/bu21013_ts.c733
-rw-r--r--drivers/input/touchscreen/chipone_icn8318.c316
-rw-r--r--drivers/input/touchscreen/cy8ctmg110_ts.c363
-rw-r--r--drivers/input/touchscreen/cyttsp4_core.c2160
-rw-r--r--drivers/input/touchscreen/cyttsp4_core.h472
-rw-r--r--drivers/input/touchscreen/cyttsp4_i2c.c90
-rw-r--r--drivers/input/touchscreen/cyttsp4_spi.c200
-rw-r--r--drivers/input/touchscreen/cyttsp_core.c638
-rw-r--r--drivers/input/touchscreen/cyttsp_core.h154
-rw-r--r--drivers/input/touchscreen/cyttsp_i2c.c90
-rw-r--r--drivers/input/touchscreen/cyttsp_i2c_common.c95
-rw-r--r--drivers/input/touchscreen/cyttsp_spi.c197
-rw-r--r--drivers/input/touchscreen/da9034-ts.c368
-rw-r--r--drivers/input/touchscreen/da9052_tsi.c348
-rw-r--r--drivers/input/touchscreen/dynapro.c190
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c1156
-rw-r--r--drivers/input/touchscreen/eeti_ts.c327
-rw-r--r--drivers/input/touchscreen/egalax_ts.c283
-rw-r--r--drivers/input/touchscreen/elants_i2c.c1275
-rw-r--r--drivers/input/touchscreen/elo.c407
-rw-r--r--drivers/input/touchscreen/fujitsu_ts.c177
-rw-r--r--drivers/input/touchscreen/goodix.c417
-rw-r--r--drivers/input/touchscreen/gunze.c188
-rw-r--r--drivers/input/touchscreen/hampshire.c189
-rw-r--r--drivers/input/touchscreen/hp680_ts_input.c127
-rw-r--r--drivers/input/touchscreen/htcpen.c248
-rw-r--r--drivers/input/touchscreen/ili210x.c358
-rw-r--r--drivers/input/touchscreen/inexio.c191
-rw-r--r--drivers/input/touchscreen/intel-mid-touch.c654
-rw-r--r--drivers/input/touchscreen/ipaq-micro-ts.c164
-rw-r--r--drivers/input/touchscreen/jornada720_ts.c153
-rw-r--r--drivers/input/touchscreen/lpc32xx_ts.c408
-rw-r--r--drivers/input/touchscreen/mainstone-wm97xx.c309
-rw-r--r--drivers/input/touchscreen/max11801_ts.c242
-rw-r--r--drivers/input/touchscreen/mc13783_ts.c255
-rw-r--r--drivers/input/touchscreen/mcs5000_ts.c294
-rw-r--r--drivers/input/touchscreen/migor_ts.c249
-rw-r--r--drivers/input/touchscreen/mk712.c219
-rw-r--r--drivers/input/touchscreen/mms114.c593
-rw-r--r--drivers/input/touchscreen/mtouch.c204
-rw-r--r--drivers/input/touchscreen/of_touchscreen.c87
-rw-r--r--drivers/input/touchscreen/pcap_ts.c258
-rw-r--r--drivers/input/touchscreen/penmount.c319
-rw-r--r--drivers/input/touchscreen/pixcir_i2c_ts.c619
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c439
-rw-r--r--drivers/input/touchscreen/st1232.c309
-rw-r--r--drivers/input/touchscreen/stmpe-ts.c396
-rw-r--r--drivers/input/touchscreen/sun4i-ts.c426
-rw-r--r--drivers/input/touchscreen/sur40.c876
-rw-r--r--drivers/input/touchscreen/sx8654.c286
-rw-r--r--drivers/input/touchscreen/ti_am335x_tsc.c554
-rw-r--r--drivers/input/touchscreen/touchit213.c218
-rw-r--r--drivers/input/touchscreen/touchright.c178
-rw-r--r--drivers/input/touchscreen/touchwin.c185
-rw-r--r--drivers/input/touchscreen/tps6507x-ts.c326
-rw-r--r--drivers/input/touchscreen/tsc2005.c827
-rw-r--r--drivers/input/touchscreen/tsc2007.c497
-rw-r--r--drivers/input/touchscreen/tsc40.c172
-rw-r--r--drivers/input/touchscreen/ucb1400_ts.c463
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c1769
-rw-r--r--drivers/input/touchscreen/w90p910_ts.c336
-rw-r--r--drivers/input/touchscreen/wacom_i2c.c286
-rw-r--r--drivers/input/touchscreen/wacom_w8001.c596
-rw-r--r--drivers/input/touchscreen/wm831x-ts.c405
-rw-r--r--drivers/input/touchscreen/wm9705.c350
-rw-r--r--drivers/input/touchscreen/wm9712.c471
-rw-r--r--drivers/input/touchscreen/wm9713.c481
-rw-r--r--drivers/input/touchscreen/wm97xx-core.c859
-rw-r--r--drivers/input/touchscreen/zforce_ts.c934
-rw-r--r--drivers/input/touchscreen/zylonite-wm97xx.c231
84 files changed, 38916 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/88pm860x-ts.c b/drivers/input/touchscreen/88pm860x-ts.c
new file mode 100644
index 000000000..251ff2aa0
--- /dev/null
+++ b/drivers/input/touchscreen/88pm860x-ts.c
@@ -0,0 +1,304 @@
+/*
+ * Touchscreen driver for Marvell 88PM860x
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/mfd/88pm860x.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#define MEAS_LEN (8)
+#define ACCURATE_BIT (12)
+
+/* touch register */
+#define MEAS_EN3 (0x52)
+
+#define MEAS_TSIX_1 (0x8D)
+#define MEAS_TSIX_2 (0x8E)
+#define MEAS_TSIY_1 (0x8F)
+#define MEAS_TSIY_2 (0x90)
+#define MEAS_TSIZ1_1 (0x91)
+#define MEAS_TSIZ1_2 (0x92)
+#define MEAS_TSIZ2_1 (0x93)
+#define MEAS_TSIZ2_2 (0x94)
+
+/* bit definitions of touch */
+#define MEAS_PD_EN (1 << 3)
+#define MEAS_TSIX_EN (1 << 4)
+#define MEAS_TSIY_EN (1 << 5)
+#define MEAS_TSIZ1_EN (1 << 6)
+#define MEAS_TSIZ2_EN (1 << 7)
+
+struct pm860x_touch {
+ struct input_dev *idev;
+ struct i2c_client *i2c;
+ struct pm860x_chip *chip;
+ int irq;
+ int res_x; /* resistor of Xplate */
+};
+
+static irqreturn_t pm860x_touch_handler(int irq, void *data)
+{
+ struct pm860x_touch *touch = data;
+ struct pm860x_chip *chip = touch->chip;
+ unsigned char buf[MEAS_LEN];
+ int x, y, pen_down;
+ int z1, z2, rt = 0;
+ int ret;
+
+ ret = pm860x_bulk_read(touch->i2c, MEAS_TSIX_1, MEAS_LEN, buf);
+ if (ret < 0)
+ goto out;
+
+ pen_down = buf[1] & (1 << 6);
+ x = ((buf[0] & 0xFF) << 4) | (buf[1] & 0x0F);
+ y = ((buf[2] & 0xFF) << 4) | (buf[3] & 0x0F);
+ z1 = ((buf[4] & 0xFF) << 4) | (buf[5] & 0x0F);
+ z2 = ((buf[6] & 0xFF) << 4) | (buf[7] & 0x0F);
+
+ if (pen_down) {
+ if ((x != 0) && (z1 != 0) && (touch->res_x != 0)) {
+ rt = z2 / z1 - 1;
+ rt = (rt * touch->res_x * x) >> ACCURATE_BIT;
+ dev_dbg(chip->dev, "z1:%d, z2:%d, rt:%d\n",
+ z1, z2, rt);
+ }
+ input_report_abs(touch->idev, ABS_X, x);
+ input_report_abs(touch->idev, ABS_Y, y);
+ input_report_abs(touch->idev, ABS_PRESSURE, rt);
+ input_report_key(touch->idev, BTN_TOUCH, 1);
+ dev_dbg(chip->dev, "pen down at [%d, %d].\n", x, y);
+ } else {
+ input_report_abs(touch->idev, ABS_PRESSURE, 0);
+ input_report_key(touch->idev, BTN_TOUCH, 0);
+ dev_dbg(chip->dev, "pen release\n");
+ }
+ input_sync(touch->idev);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int pm860x_touch_open(struct input_dev *dev)
+{
+ struct pm860x_touch *touch = input_get_drvdata(dev);
+ int data, ret;
+
+ data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN
+ | MEAS_TSIZ1_EN | MEAS_TSIZ2_EN;
+ ret = pm860x_set_bits(touch->i2c, MEAS_EN3, data, data);
+ if (ret < 0)
+ goto out;
+ return 0;
+out:
+ return ret;
+}
+
+static void pm860x_touch_close(struct input_dev *dev)
+{
+ struct pm860x_touch *touch = input_get_drvdata(dev);
+ int data;
+
+ data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN
+ | MEAS_TSIZ1_EN | MEAS_TSIZ2_EN;
+ pm860x_set_bits(touch->i2c, MEAS_EN3, data, 0);
+}
+
+#ifdef CONFIG_OF
+static int pm860x_touch_dt_init(struct platform_device *pdev,
+ struct pm860x_chip *chip,
+ int *res_x)
+{
+ struct device_node *np = pdev->dev.parent->of_node;
+ struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
+ : chip->companion;
+ int data, n, ret;
+ if (!np)
+ return -ENODEV;
+ np = of_find_node_by_name(np, "touch");
+ if (!np) {
+ dev_err(&pdev->dev, "Can't find touch node\n");
+ return -EINVAL;
+ }
+ /* set GPADC MISC1 register */
+ data = 0;
+ if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-prebias", &n))
+ data |= (n << 1) & PM8607_GPADC_PREBIAS_MASK;
+ if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-slot-cycle", &n))
+ data |= (n << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
+ if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-off-scale", &n))
+ data |= (n << 5) & PM8607_GPADC_OFF_SCALE_MASK;
+ if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-sw-cal", &n))
+ data |= (n << 7) & PM8607_GPADC_SW_CAL_MASK;
+ if (data) {
+ ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
+ if (ret < 0)
+ return -EINVAL;
+ }
+ /* set tsi prebias time */
+ if (!of_property_read_u32(np, "marvell,88pm860x-tsi-prebias", &data)) {
+ ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
+ if (ret < 0)
+ return -EINVAL;
+ }
+ /* set prebias & prechg time of pen detect */
+ data = 0;
+ if (!of_property_read_u32(np, "marvell,88pm860x-pen-prebias", &n))
+ data |= n & PM8607_PD_PREBIAS_MASK;
+ if (!of_property_read_u32(np, "marvell,88pm860x-pen-prechg", &n))
+ data |= n & PM8607_PD_PRECHG_MASK;
+ if (data) {
+ ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
+ if (ret < 0)
+ return -EINVAL;
+ }
+ of_property_read_u32(np, "marvell,88pm860x-resistor-X", res_x);
+ return 0;
+}
+#else
+#define pm860x_touch_dt_init(x, y, z) (-1)
+#endif
+
+static int pm860x_touch_probe(struct platform_device *pdev)
+{
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct pm860x_touch_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct pm860x_touch *touch;
+ struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
+ : chip->companion;
+ int irq, ret, res_x = 0, data = 0;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource!\n");
+ return -EINVAL;
+ }
+
+ if (pm860x_touch_dt_init(pdev, chip, &res_x)) {
+ if (pdata) {
+ /* set GPADC MISC1 register */
+ data = 0;
+ data |= (pdata->gpadc_prebias << 1)
+ & PM8607_GPADC_PREBIAS_MASK;
+ data |= (pdata->slot_cycle << 3)
+ & PM8607_GPADC_SLOT_CYCLE_MASK;
+ data |= (pdata->off_scale << 5)
+ & PM8607_GPADC_OFF_SCALE_MASK;
+ data |= (pdata->sw_cal << 7)
+ & PM8607_GPADC_SW_CAL_MASK;
+ if (data) {
+ ret = pm860x_reg_write(i2c,
+ PM8607_GPADC_MISC1, data);
+ if (ret < 0)
+ return -EINVAL;
+ }
+ /* set tsi prebias time */
+ if (pdata->tsi_prebias) {
+ data = pdata->tsi_prebias;
+ ret = pm860x_reg_write(i2c,
+ PM8607_TSI_PREBIAS, data);
+ if (ret < 0)
+ return -EINVAL;
+ }
+ /* set prebias & prechg time of pen detect */
+ data = 0;
+ data |= pdata->pen_prebias
+ & PM8607_PD_PREBIAS_MASK;
+ data |= (pdata->pen_prechg << 5)
+ & PM8607_PD_PRECHG_MASK;
+ if (data) {
+ ret = pm860x_reg_write(i2c,
+ PM8607_PD_PREBIAS, data);
+ if (ret < 0)
+ return -EINVAL;
+ }
+ res_x = pdata->res_x;
+ } else {
+ dev_err(&pdev->dev, "failed to get platform data\n");
+ return -EINVAL;
+ }
+ }
+ /* enable GPADC */
+ ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1, PM8607_GPADC_EN,
+ PM8607_GPADC_EN);
+ if (ret)
+ return ret;
+
+ touch = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_touch),
+ GFP_KERNEL);
+ if (!touch)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, touch);
+
+ touch->idev = devm_input_allocate_device(&pdev->dev);
+ if (!touch->idev) {
+ dev_err(&pdev->dev, "Failed to allocate input device!\n");
+ return -ENOMEM;
+ }
+
+ touch->idev->name = "88pm860x-touch";
+ touch->idev->phys = "88pm860x/input0";
+ touch->idev->id.bustype = BUS_I2C;
+ touch->idev->dev.parent = &pdev->dev;
+ touch->idev->open = pm860x_touch_open;
+ touch->idev->close = pm860x_touch_close;
+ touch->chip = chip;
+ touch->i2c = i2c;
+ touch->irq = irq;
+ touch->res_x = res_x;
+ input_set_drvdata(touch->idev, touch);
+
+ ret = devm_request_threaded_irq(&pdev->dev, touch->irq, NULL,
+ pm860x_touch_handler, IRQF_ONESHOT,
+ "touch", touch);
+ if (ret < 0)
+ return ret;
+
+ __set_bit(EV_ABS, touch->idev->evbit);
+ __set_bit(ABS_X, touch->idev->absbit);
+ __set_bit(ABS_Y, touch->idev->absbit);
+ __set_bit(ABS_PRESSURE, touch->idev->absbit);
+ __set_bit(EV_SYN, touch->idev->evbit);
+ __set_bit(EV_KEY, touch->idev->evbit);
+ __set_bit(BTN_TOUCH, touch->idev->keybit);
+
+ input_set_abs_params(touch->idev, ABS_X, 0, 1 << ACCURATE_BIT, 0, 0);
+ input_set_abs_params(touch->idev, ABS_Y, 0, 1 << ACCURATE_BIT, 0, 0);
+ input_set_abs_params(touch->idev, ABS_PRESSURE, 0, 1 << ACCURATE_BIT,
+ 0, 0);
+
+ ret = input_register_device(touch->idev);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to register touch!\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, touch);
+ return 0;
+}
+
+static struct platform_driver pm860x_touch_driver = {
+ .driver = {
+ .name = "88pm860x-touch",
+ },
+ .probe = pm860x_touch_probe,
+};
+module_platform_driver(pm860x_touch_driver);
+
+MODULE_DESCRIPTION("Touchscreen driver for Marvell Semiconductor 88PM860x");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:88pm860x-touch");
+
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
new file mode 100644
index 000000000..80f638670
--- /dev/null
+++ b/drivers/input/touchscreen/Kconfig
@@ -0,0 +1,1030 @@
+#
+# Touchscreen driver configuration
+#
+menuconfig INPUT_TOUCHSCREEN
+ bool "Touchscreens"
+ help
+ Say Y here, and a list of supported touchscreens will be displayed.
+ This option doesn't affect the kernel.
+
+ If unsure, say Y.
+
+if INPUT_TOUCHSCREEN
+
+config OF_TOUCHSCREEN
+ def_tristate INPUT
+ depends on INPUT && OF
+
+config TOUCHSCREEN_88PM860X
+ tristate "Marvell 88PM860x touchscreen"
+ depends on MFD_88PM860X
+ help
+ Say Y here if you have a 88PM860x PMIC and want to enable
+ support for the built-in touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called 88pm860x-ts.
+
+config TOUCHSCREEN_ADS7846
+ tristate "ADS7846/TSC2046/AD7873 and AD(S)7843 based touchscreens"
+ depends on SPI_MASTER
+ depends on HWMON = n || HWMON
+ help
+ Say Y here if you have a touchscreen interface using the
+ ADS7846/TSC2046/AD7873 or ADS7843/AD7843 controller,
+ and your board-specific setup code includes that in its
+ table of SPI devices.
+
+ If HWMON is selected, and the driver is told the reference voltage
+ on your board, you will also get hwmon interfaces for the voltage
+ (and on ads7846/tsc2046/ad7873, temperature) sensors of this chip.
+
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called ads7846.
+
+config TOUCHSCREEN_AD7877
+ tristate "AD7877 based touchscreens"
+ depends on SPI_MASTER
+ help
+ Say Y here if you have a touchscreen interface using the
+ AD7877 controller, and your board-specific initialization
+ code includes that in its table of SPI devices.
+
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7877.
+
+config TOUCHSCREEN_AD7879
+ tristate "Analog Devices AD7879-1/AD7889-1 touchscreen interface"
+ help
+ Say Y here if you want to support a touchscreen interface using
+ the AD7879-1/AD7889-1 controller.
+
+ You should select a bus connection too.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7879.
+
+config TOUCHSCREEN_AD7879_I2C
+ tristate "support I2C bus connection"
+ depends on TOUCHSCREEN_AD7879 && I2C
+ help
+ Say Y here if you have AD7879-1/AD7889-1 hooked to an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7879-i2c.
+
+config TOUCHSCREEN_AD7879_SPI
+ tristate "support SPI bus connection"
+ depends on TOUCHSCREEN_AD7879 && SPI_MASTER
+ help
+ Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus.
+
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7879-spi.
+
+config TOUCHSCREEN_AR1021_I2C
+ tristate "Microchip AR1021 i2c touchscreen"
+ depends on I2C && OF
+ help
+ Say Y here if you have the Microchip AR1021 touchscreen controller
+ chip in your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ar1021_i2c.
+
+config TOUCHSCREEN_ATMEL_MXT
+ tristate "Atmel mXT I2C Touchscreen"
+ depends on I2C
+ select FW_LOADER
+ help
+ Say Y here if you have Atmel mXT series I2C touchscreen,
+ such as AT42QT602240/ATMXT224, connected to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atmel_mxt_ts.
+
+config TOUCHSCREEN_AUO_PIXCIR
+ tristate "AUO in-cell touchscreen using Pixcir ICs"
+ depends on I2C
+ depends on GPIOLIB
+ help
+ Say Y here if you have a AUO display with in-cell touchscreen
+ using Pixcir ICs.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called auo-pixcir-ts.
+
+config TOUCHSCREEN_BU21013
+ tristate "BU21013 based touch panel controllers"
+ depends on I2C
+ help
+ Say Y here if you have a bu21013 touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bu21013_ts.
+
+config TOUCHSCREEN_CHIPONE_ICN8318
+ tristate "chipone icn8318 touchscreen controller"
+ depends on GPIOLIB
+ depends on I2C
+ depends on OF
+ help
+ Say Y here if you have a ChipOne icn8318 based I2C touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called chipone_icn8318.
+
+config TOUCHSCREEN_CY8CTMG110
+ tristate "cy8ctmg110 touchscreen"
+ depends on I2C
+ depends on GPIOLIB
+ help
+ Say Y here if you have a cy8ctmg110 capacitive touchscreen on
+ an AAVA device.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cy8ctmg110_ts.
+
+config TOUCHSCREEN_CYTTSP_CORE
+ tristate "Cypress TTSP touchscreen"
+ help
+ Say Y here if you have a touchscreen using controller from
+ the Cypress TrueTouch(tm) Standard Product family connected
+ to your system. You will also need to select appropriate
+ bus connection below.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cyttsp_core.
+
+config TOUCHSCREEN_CYTTSP_I2C
+ tristate "support I2C bus connection"
+ depends on TOUCHSCREEN_CYTTSP_CORE && I2C
+ help
+ Say Y here if the touchscreen is connected via I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cyttsp_i2c.
+
+config TOUCHSCREEN_CYTTSP_SPI
+ tristate "support SPI bus connection"
+ depends on TOUCHSCREEN_CYTTSP_CORE && SPI_MASTER
+ help
+ Say Y here if the touchscreen is connected via SPI bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cyttsp_spi.
+
+config TOUCHSCREEN_CYTTSP4_CORE
+ tristate "Cypress TrueTouch Gen4 Touchscreen Driver"
+ help
+ Core driver for Cypress TrueTouch(tm) Standard Product
+ Generation4 touchscreen controllers.
+
+ Say Y here if you have a Cypress Gen4 touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here.
+
+config TOUCHSCREEN_CYTTSP4_I2C
+ tristate "support I2C bus connection"
+ depends on TOUCHSCREEN_CYTTSP4_CORE && I2C
+ help
+ Say Y here if the touchscreen is connected via I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cyttsp4_i2c.
+
+config TOUCHSCREEN_CYTTSP4_SPI
+ tristate "support SPI bus connection"
+ depends on TOUCHSCREEN_CYTTSP4_CORE && SPI_MASTER
+ help
+ Say Y here if the touchscreen is connected via SPI bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cyttsp4_spi.
+
+config TOUCHSCREEN_DA9034
+ tristate "Touchscreen support for Dialog Semiconductor DA9034"
+ depends on PMIC_DA903X
+ default y
+ help
+ Say Y here to enable the support for the touchscreen found
+ on Dialog Semiconductor DA9034 PMIC.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called da9034-ts.
+
+config TOUCHSCREEN_DA9052
+ tristate "Dialog DA9052/DA9053 TSI"
+ depends on PMIC_DA9052
+ help
+ Say Y here to support the touchscreen found on Dialog Semiconductor
+ DA9052-BC and DA9053-AA/Bx PMICs.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called da9052_tsi.
+
+config TOUCHSCREEN_DYNAPRO
+ tristate "Dynapro serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Dynapro serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dynapro.
+
+config TOUCHSCREEN_HAMPSHIRE
+ tristate "Hampshire serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Hampshire serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hampshire.
+
+config TOUCHSCREEN_EETI
+ tristate "EETI touchscreen panel support"
+ depends on I2C
+ help
+ Say Y here to enable support for I2C connected EETI touch panels.
+
+ To compile this driver as a module, choose M here: the
+ module will be called eeti_ts.
+
+config TOUCHSCREEN_EGALAX
+ tristate "EETI eGalax multi-touch panel support"
+ depends on I2C && OF
+ help
+ Say Y here to enable support for I2C connected EETI
+ eGalax multi-touch panels.
+
+ To compile this driver as a module, choose M here: the
+ module will be called egalax_ts.
+
+config TOUCHSCREEN_FUJITSU
+ tristate "Fujitsu serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have the Fujitsu touchscreen (such as one
+ installed in Lifebook P series laptop) connected to your
+ system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fujitsu-ts.
+
+config TOUCHSCREEN_GOODIX
+ tristate "Goodix I2C touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have the Goodix touchscreen (such as one
+ installed in Onda v975w tablets) connected to your
+ system. It also supports 5-finger chip models, which can be
+ found on ARM tablets, like Wexler TAB7200 and MSI Primo73.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called goodix.
+
+config TOUCHSCREEN_ILI210X
+ tristate "Ilitek ILI210X based touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have a ILI210X based touchscreen
+ controller. This driver supports models ILI2102,
+ ILI2102s, ILI2103, ILI2103s and ILI2105.
+ Such kind of chipsets can be found in Amazon Kindle Fire
+ touchscreens.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ili210x.
+
+config TOUCHSCREEN_IPROC
+ tristate "IPROC touch panel driver support"
+ depends on ARCH_BCM_IPROC || COMPILE_TEST
+ help
+ Say Y here if you want to add support for the IPROC touch
+ controller to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bcm_iproc_tsc.
+
+config TOUCHSCREEN_S3C2410
+ tristate "Samsung S3C2410/generic touchscreen input driver"
+ depends on ARCH_S3C24XX || SAMSUNG_DEV_TS
+ select S3C_ADC
+ help
+ Say Y here if you have the s3c2410 touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s3c2410_ts.
+
+config TOUCHSCREEN_GUNZE
+ tristate "Gunze AHL-51S touchscreen"
+ select SERIO
+ help
+ Say Y here if you have the Gunze AHL-51 touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gunze.
+
+config TOUCHSCREEN_ELAN
+ tristate "Elan eKTH I2C touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have an Elan eKTH I2C touchscreen
+ connected to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called elants_i2c.
+
+config TOUCHSCREEN_ELO
+ tristate "Elo serial touchscreens"
+ select SERIO
+ help
+ Say Y here if you have an Elo serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called elo.
+
+config TOUCHSCREEN_WACOM_W8001
+ tristate "Wacom W8001 penabled serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have an Wacom W8001 penabled serial touchscreen
+ connected to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wacom_w8001.
+
+config TOUCHSCREEN_WACOM_I2C
+ tristate "Wacom Tablet support (I2C)"
+ depends on I2C
+ help
+ Say Y here if you want to use the I2C version of the Wacom
+ Pen Tablet.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called wacom_i2c.
+
+config TOUCHSCREEN_LPC32XX
+ tristate "LPC32XX touchscreen controller"
+ depends on ARCH_LPC32XX
+ help
+ Say Y here if you have a LPC32XX device and want
+ to support the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called lpc32xx_ts.
+
+config TOUCHSCREEN_MAX11801
+ tristate "MAX11801 based touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a MAX11801 based touchscreen
+ controller.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called max11801_ts.
+
+config TOUCHSCREEN_MCS5000
+ tristate "MELFAS MCS-5000 touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have the MELFAS MCS-5000 touchscreen controller
+ chip in your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mcs5000_ts.
+
+config TOUCHSCREEN_MMS114
+ tristate "MELFAS MMS114 touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have the MELFAS MMS114 touchscreen controller
+ chip in your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mms114.
+
+config TOUCHSCREEN_MTOUCH
+ tristate "MicroTouch serial touchscreens"
+ select SERIO
+ help
+ Say Y here if you have a MicroTouch (3M) serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mtouch.
+
+config TOUCHSCREEN_INEXIO
+ tristate "iNexio serial touchscreens"
+ select SERIO
+ help
+ Say Y here if you have an iNexio serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called inexio.
+
+config TOUCHSCREEN_INTEL_MID
+ tristate "Intel MID platform resistive touchscreen"
+ depends on INTEL_SCU_IPC
+ help
+ Say Y here if you have a Intel MID based touchscreen in
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called intel_mid_touch.
+
+config TOUCHSCREEN_MK712
+ tristate "ICS MicroClock MK712 touchscreen"
+ help
+ Say Y here if you have the ICS MicroClock MK712 touchscreen
+ controller chip in your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mk712.
+
+config TOUCHSCREEN_HP600
+ tristate "HP Jornada 6xx touchscreen"
+ depends on SH_HP6XX && SH_ADC
+ help
+ Say Y here if you have a HP Jornada 620/660/680/690 and want to
+ support the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hp680_ts_input.
+
+config TOUCHSCREEN_HP7XX
+ tristate "HP Jornada 7xx touchscreen"
+ depends on SA1100_JORNADA720_SSP
+ help
+ Say Y here if you have a HP Jornada 710/720/728 and want
+ to support the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called jornada720_ts.
+
+config TOUCHSCREEN_IPAQ_MICRO
+ tristate "HP iPAQ Atmel Micro ASIC touchscreen"
+ depends on MFD_IPAQ_MICRO
+ help
+ Say Y here to enable support for the touchscreen attached to
+ the Atmel Micro peripheral controller on iPAQ h3100/h3600/h3700
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ipaq-micro-ts.
+
+config TOUCHSCREEN_HTCPEN
+ tristate "HTC Shift X9500 touchscreen"
+ depends on ISA
+ help
+ Say Y here if you have an HTC Shift UMPC also known as HTC X9500
+ Clio / Shangrila and want to support the built-in touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called htcpen.
+
+config TOUCHSCREEN_PENMOUNT
+ tristate "Penmount serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Penmount serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called penmount.
+
+config TOUCHSCREEN_EDT_FT5X06
+ tristate "EDT FocalTech FT5x06 I2C Touchscreen support"
+ depends on I2C
+ help
+ Say Y here if you have an EDT "Polytouch" touchscreen based
+ on the FocalTech FT5x06 family of controllers connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called edt-ft5x06.
+
+config TOUCHSCREEN_MIGOR
+ tristate "Renesas MIGO-R touchscreen"
+ depends on SH_MIGOR && I2C
+ help
+ Say Y here to enable MIGO-R touchscreen support.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called migor_ts.
+
+config TOUCHSCREEN_TOUCHRIGHT
+ tristate "Touchright serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Touchright serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called touchright.
+
+config TOUCHSCREEN_TOUCHWIN
+ tristate "Touchwin serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Touchwin serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called touchwin.
+
+config TOUCHSCREEN_TI_AM335X_TSC
+ tristate "TI Touchscreen Interface"
+ depends on MFD_TI_AM335X_TSCADC
+ help
+ Say Y here if you have 4/5/8 wire touchscreen controller
+ to be connected to the ADC controller on your TI AM335x SoC.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ti_am335x_tsc.
+
+config TOUCHSCREEN_UCB1400
+ tristate "Philips UCB1400 touchscreen"
+ depends on AC97_BUS
+ depends on UCB1400_CORE
+ help
+ This enables support for the Philips UCB1400 touchscreen interface.
+ The UCB1400 is an AC97 audio codec. The touchscreen interface
+ will be initialized only after the ALSA subsystem has been
+ brought up and the UCB1400 detected. You therefore have to
+ configure ALSA support as well (either built-in or modular,
+ independently of whether this driver is itself built-in or
+ modular) for this driver to work.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ucb1400_ts.
+
+config TOUCHSCREEN_PIXCIR
+ tristate "PIXCIR I2C touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a pixcir i2c touchscreen
+ controller.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pixcir_i2c_ts.
+
+config TOUCHSCREEN_WM831X
+ tristate "Support for WM831x touchscreen controllers"
+ depends on MFD_WM831X
+ help
+ This enables support for the touchscreen controller on the WM831x
+ series of PMICs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wm831x-ts.
+
+config TOUCHSCREEN_WM97XX
+ tristate "Support for WM97xx AC97 touchscreen controllers"
+ depends on AC97_BUS
+ help
+ Say Y here if you have a Wolfson Microelectronics WM97xx
+ touchscreen connected to your system. Note that this option
+ only enables core driver, you will also need to select
+ support for appropriate chip below.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wm97xx-ts.
+
+config TOUCHSCREEN_WM9705
+ bool "WM9705 Touchscreen interface support"
+ depends on TOUCHSCREEN_WM97XX
+ default y
+ help
+ Say Y here to enable support for the Wolfson Microelectronics
+ WM9705 touchscreen controller.
+
+config TOUCHSCREEN_WM9712
+ bool "WM9712 Touchscreen interface support"
+ depends on TOUCHSCREEN_WM97XX
+ default y
+ help
+ Say Y here to enable support for the Wolfson Microelectronics
+ WM9712 touchscreen controller.
+
+config TOUCHSCREEN_WM9713
+ bool "WM9713 Touchscreen interface support"
+ depends on TOUCHSCREEN_WM97XX
+ default y
+ help
+ Say Y here to enable support for the Wolfson Microelectronics
+ WM9713 touchscreen controller.
+
+config TOUCHSCREEN_WM97XX_ATMEL
+ tristate "WM97xx Atmel accelerated touch"
+ depends on TOUCHSCREEN_WM97XX && AVR32
+ help
+ Say Y here for support for streaming mode with WM97xx touchscreens
+ on Atmel AT91 or AVR32 systems with an AC97C module.
+
+ Be aware that this will use channel B in the controller for
+ streaming data, this must not conflict with other AC97C drivers.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the module will
+ be called atmel-wm97xx.
+
+config TOUCHSCREEN_WM97XX_MAINSTONE
+ tristate "WM97xx Mainstone/Palm accelerated touch"
+ depends on TOUCHSCREEN_WM97XX && ARCH_PXA
+ help
+ Say Y here for support for streaming mode with WM97xx touchscreens
+ on Mainstone, Palm Tungsten T5, TX and LifeDrive systems.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mainstone-wm97xx.
+
+config TOUCHSCREEN_WM97XX_ZYLONITE
+ tristate "Zylonite accelerated touch"
+ depends on TOUCHSCREEN_WM97XX && MACH_ZYLONITE
+ select TOUCHSCREEN_WM9713
+ help
+ Say Y here for support for streaming mode with the touchscreen
+ on Zylonite systems.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zylonite-wm97xx.
+
+config TOUCHSCREEN_USB_COMPOSITE
+ tristate "USB Touchscreen Driver"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ USB Touchscreen driver for:
+ - eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700)
+ - PanJit TouchSet USB
+ - 3M MicroTouch USB (EX II series)
+ - ITM
+ - some other eTurboTouch
+ - Gunze AHL61
+ - DMC TSC-10/25
+ - IRTOUCHSYSTEMS/UNITOP
+ - IdealTEK URTC1000
+ - GoTop Super_Q2/GogoPen/PenPower tablets
+ - JASTEC USB Touch Controller/DigiTech DTR-02U
+ - Zytronic controllers
+ - Elo TouchSystems 2700 IntelliTouch
+ - EasyTouch USB Touch Controller from Data Modul
+ - e2i (Mimo monitors)
+
+ Have a look at <http://linux.chapter7.ch/touchkit/> for
+ a usage description and the required user-space stuff.
+
+ To compile this driver as a module, choose M here: the
+ module will be called usbtouchscreen.
+
+config TOUCHSCREEN_MC13783
+ tristate "Freescale MC13783 touchscreen input driver"
+ depends on MFD_MC13XXX
+ help
+ Say Y here if you have an Freescale MC13783 PMIC on your
+ board and want to use its touchscreen
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mc13783_ts.
+
+config TOUCHSCREEN_USB_EGALAX
+ default y
+ bool "eGalax, eTurboTouch CT-410/510/700 device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_PANJIT
+ default y
+ bool "PanJit device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_3M
+ default y
+ bool "3M/Microtouch EX II series device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ITM
+ default y
+ bool "ITM device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ETURBO
+ default y
+ bool "eTurboTouch (non-eGalax compatible) device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_GUNZE
+ default y
+ bool "Gunze AHL61 device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_DMC_TSC10
+ default y
+ bool "DMC TSC-10/25 device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_IRTOUCH
+ default y
+ bool "IRTOUCHSYSTEMS/UNITOP device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_IDEALTEK
+ default y
+ bool "IdealTEK URTC1000 device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_GENERAL_TOUCH
+ default y
+ bool "GeneralTouch Touchscreen device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_GOTOP
+ default y
+ bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_JASTEC
+ default y
+ bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ELO
+ default y
+ bool "Elo TouchSystems 2700 IntelliTouch controller device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_E2I
+ default y
+ bool "e2i Touchscreen controller (e.g. from Mimo 740)" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ZYTRONIC
+ default y
+ bool "Zytronic controller" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ETT_TC45USB
+ default y
+ bool "ET&T USB series TC4UM/TC5UH touchscreen controller support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_NEXIO
+ default y
+ bool "NEXIO/iNexio device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_EASYTOUCH
+ default y
+ bool "EasyTouch USB Touch controller device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+ help
+ Say Y here if you have an EasyTouch USB Touch controller.
+ If unsure, say N.
+
+config TOUCHSCREEN_TOUCHIT213
+ tristate "Sahara TouchIT-213 touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Sahara TouchIT-213 Tablet PC.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called touchit213.
+
+config TOUCHSCREEN_TSC_SERIO
+ tristate "TSC-10/25/40 serial touchscreen support"
+ select SERIO
+ help
+ Say Y here if you have a TSC-10, 25 or 40 serial touchscreen connected
+ to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tsc40.
+
+config TOUCHSCREEN_TSC2005
+ tristate "TSC2005 based touchscreens"
+ depends on SPI_MASTER
+ help
+ Say Y here if you have a TSC2005 based touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tsc2005.
+
+config TOUCHSCREEN_TSC2007
+ tristate "TSC2007 based touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a TSC2007 based touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tsc2007.
+
+config TOUCHSCREEN_W90X900
+ tristate "W90P910 touchscreen driver"
+ depends on ARCH_W90X900
+ help
+ Say Y here if you have a W90P910 based touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called w90p910_ts.
+
+config TOUCHSCREEN_PCAP
+ tristate "Motorola PCAP touchscreen"
+ depends on EZX_PCAP
+ help
+ Say Y here if you have a Motorola EZX telephone and
+ want to enable support for the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pcap_ts.
+
+config TOUCHSCREEN_ST1232
+ tristate "Sitronix ST1232 touchscreen controllers"
+ depends on I2C
+ help
+ Say Y here if you want to support Sitronix ST1232
+ touchscreen controller.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called st1232_ts.
+
+config TOUCHSCREEN_STMPE
+ tristate "STMicroelectronics STMPE touchscreens"
+ depends on MFD_STMPE
+ help
+ Say Y here if you want support for STMicroelectronics
+ STMPE touchscreen controllers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stmpe-ts.
+
+config TOUCHSCREEN_SUN4I
+ tristate "Allwinner sun4i resistive touchscreen controller support"
+ depends on ARCH_SUNXI || COMPILE_TEST
+ depends on HWMON
+ depends on THERMAL || !THERMAL_OF
+ help
+ This selects support for the resistive touchscreen controller
+ found on Allwinner sunxi SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sun4i-ts.
+
+config TOUCHSCREEN_SUR40
+ tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen"
+ depends on USB
+ depends on MEDIA_USB_SUPPORT
+ select INPUT_POLLDEV
+ select VIDEOBUF2_DMA_SG
+ help
+ Say Y here if you want support for the Samsung SUR40 touchscreen
+ (also known as Microsoft Surface 2.0 or Microsoft PixelSense).
+
+ 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
+ select INPUT_POLLDEV
+ help
+ Say Y here if you have a TPS6507x based touchscreen
+ controller.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tps6507x_ts.
+
+config TOUCHSCREEN_ZFORCE
+ tristate "Neonode zForce infrared touchscreens"
+ depends on I2C
+ depends on GPIOLIB
+ help
+ Say Y here if you have a touchscreen using the zforce
+ infraread technology from Neonode.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zforce_ts.
+
+endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
new file mode 100644
index 000000000..44deea743
--- /dev/null
+++ b/drivers/input/touchscreen/Makefile
@@ -0,0 +1,86 @@
+#
+# Makefile for the touchscreen drivers.
+#
+
+# Each configuration option enables a list of files.
+
+wm97xx-ts-y := wm97xx-core.o
+
+obj-$(CONFIG_OF_TOUCHSCREEN) += of_touchscreen.o
+obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o
+obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o
+obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o
+obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o
+obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
+obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
+obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
+obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o
+obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_CORE) += cyttsp4_core.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_I2C) += cyttsp4_i2c.o cyttsp_i2c_common.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_SPI) += cyttsp4_spi.o
+obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
+obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o
+obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
+obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o
+obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
+obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
+obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
+obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
+obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
+obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
+obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
+obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
+obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
+obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o
+obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MMS114) += mms114.o
+obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
+obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
+obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
+obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o
+obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o
+obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
+obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
+obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
+obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
+obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
+obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o
+obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o
+obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
+obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
+obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
+obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
+obj-$(CONFIG_TOUCHSCREEN_WACOM_I2C) += wacom_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
+wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
+wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
+wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
+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/ad7877.c b/drivers/input/touchscreen/ad7877.c
new file mode 100644
index 000000000..da4e5bb5e
--- /dev/null
+++ b/drivers/input/touchscreen/ad7877.c
@@ -0,0 +1,858 @@
+/*
+ * Copyright (C) 2006-2008 Michael Hennerich, Analog Devices Inc.
+ *
+ * Description: AD7877 based touchscreen, sensor (ADCs), DAC and GPIO driver
+ * Based on: ads7846.c
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * History:
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * Various changes: Imre Deak <imre.deak@nokia.com>
+ *
+ * Using code from:
+ * - 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
+ */
+
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ad7877.h>
+#include <linux/module.h>
+#include <asm/irq.h>
+
+#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(100)
+
+#define MAX_SPI_FREQ_HZ 20000000
+#define MAX_12BIT ((1<<12)-1)
+
+#define AD7877_REG_ZEROS 0
+#define AD7877_REG_CTRL1 1
+#define AD7877_REG_CTRL2 2
+#define AD7877_REG_ALERT 3
+#define AD7877_REG_AUX1HIGH 4
+#define AD7877_REG_AUX1LOW 5
+#define AD7877_REG_BAT1HIGH 6
+#define AD7877_REG_BAT1LOW 7
+#define AD7877_REG_BAT2HIGH 8
+#define AD7877_REG_BAT2LOW 9
+#define AD7877_REG_TEMP1HIGH 10
+#define AD7877_REG_TEMP1LOW 11
+#define AD7877_REG_SEQ0 12
+#define AD7877_REG_SEQ1 13
+#define AD7877_REG_DAC 14
+#define AD7877_REG_NONE1 15
+#define AD7877_REG_EXTWRITE 15
+#define AD7877_REG_XPLUS 16
+#define AD7877_REG_YPLUS 17
+#define AD7877_REG_Z2 18
+#define AD7877_REG_aux1 19
+#define AD7877_REG_aux2 20
+#define AD7877_REG_aux3 21
+#define AD7877_REG_bat1 22
+#define AD7877_REG_bat2 23
+#define AD7877_REG_temp1 24
+#define AD7877_REG_temp2 25
+#define AD7877_REG_Z1 26
+#define AD7877_REG_GPIOCTRL1 27
+#define AD7877_REG_GPIOCTRL2 28
+#define AD7877_REG_GPIODATA 29
+#define AD7877_REG_NONE2 30
+#define AD7877_REG_NONE3 31
+
+#define AD7877_SEQ_YPLUS_BIT (1<<11)
+#define AD7877_SEQ_XPLUS_BIT (1<<10)
+#define AD7877_SEQ_Z2_BIT (1<<9)
+#define AD7877_SEQ_AUX1_BIT (1<<8)
+#define AD7877_SEQ_AUX2_BIT (1<<7)
+#define AD7877_SEQ_AUX3_BIT (1<<6)
+#define AD7877_SEQ_BAT1_BIT (1<<5)
+#define AD7877_SEQ_BAT2_BIT (1<<4)
+#define AD7877_SEQ_TEMP1_BIT (1<<3)
+#define AD7877_SEQ_TEMP2_BIT (1<<2)
+#define AD7877_SEQ_Z1_BIT (1<<1)
+
+enum {
+ AD7877_SEQ_YPOS = 0,
+ AD7877_SEQ_XPOS = 1,
+ AD7877_SEQ_Z2 = 2,
+ AD7877_SEQ_AUX1 = 3,
+ AD7877_SEQ_AUX2 = 4,
+ AD7877_SEQ_AUX3 = 5,
+ AD7877_SEQ_BAT1 = 6,
+ AD7877_SEQ_BAT2 = 7,
+ AD7877_SEQ_TEMP1 = 8,
+ AD7877_SEQ_TEMP2 = 9,
+ AD7877_SEQ_Z1 = 10,
+ AD7877_NR_SENSE = 11,
+};
+
+/* DAC Register Default RANGE 0 to Vcc, Volatge Mode, DAC On */
+#define AD7877_DAC_CONF 0x1
+
+/* If gpio3 is set AUX3/GPIO3 acts as GPIO Output */
+#define AD7877_EXTW_GPIO_3_CONF 0x1C4
+#define AD7877_EXTW_GPIO_DATA 0x200
+
+/* Control REG 2 */
+#define AD7877_TMR(x) ((x & 0x3) << 0)
+#define AD7877_REF(x) ((x & 0x1) << 2)
+#define AD7877_POL(x) ((x & 0x1) << 3)
+#define AD7877_FCD(x) ((x & 0x3) << 4)
+#define AD7877_PM(x) ((x & 0x3) << 6)
+#define AD7877_ACQ(x) ((x & 0x3) << 8)
+#define AD7877_AVG(x) ((x & 0x3) << 10)
+
+/* Control REG 1 */
+#define AD7877_SER (1 << 11) /* non-differential */
+#define AD7877_DFR (0 << 11) /* differential */
+
+#define AD7877_MODE_NOC (0) /* Do not convert */
+#define AD7877_MODE_SCC (1) /* Single channel conversion */
+#define AD7877_MODE_SEQ0 (2) /* Sequence 0 in Slave Mode */
+#define AD7877_MODE_SEQ1 (3) /* Sequence 1 in Master Mode */
+
+#define AD7877_CHANADD(x) ((x&0xF)<<7)
+#define AD7877_READADD(x) ((x)<<2)
+#define AD7877_WRITEADD(x) ((x)<<12)
+
+#define AD7877_READ_CHAN(x) (AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_SER | \
+ AD7877_MODE_SCC | AD7877_CHANADD(AD7877_REG_ ## x) | \
+ AD7877_READADD(AD7877_REG_ ## x))
+
+#define AD7877_MM_SEQUENCE (AD7877_SEQ_YPLUS_BIT | AD7877_SEQ_XPLUS_BIT | \
+ AD7877_SEQ_Z2_BIT | AD7877_SEQ_Z1_BIT)
+
+/*
+ * Non-touchscreen sensors only use single-ended conversions.
+ */
+
+struct ser_req {
+ u16 reset;
+ u16 ref_on;
+ u16 command;
+ struct spi_message msg;
+ struct spi_transfer xfer[6];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ u16 sample ____cacheline_aligned;
+};
+
+struct ad7877 {
+ struct input_dev *input;
+ char phys[32];
+
+ struct spi_device *spi;
+ u16 model;
+ u16 vref_delay_usecs;
+ u16 x_plate_ohms;
+ u16 pressure_max;
+
+ u16 cmd_crtl1;
+ u16 cmd_crtl2;
+ u16 cmd_dummy;
+ u16 dac;
+
+ u8 stopacq_polarity;
+ u8 first_conversion_delay;
+ u8 acquisition_time;
+ u8 averaging;
+ u8 pen_down_acc_interval;
+
+ struct spi_transfer xfer[AD7877_NR_SENSE + 2];
+ struct spi_message msg;
+
+ struct mutex mutex;
+ bool disabled; /* P: mutex */
+ bool gpio3; /* P: mutex */
+ bool gpio4; /* P: mutex */
+
+ spinlock_t lock;
+ struct timer_list timer; /* P: lock */
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ u16 conversion_data[AD7877_NR_SENSE] ____cacheline_aligned;
+};
+
+static bool gpio3;
+module_param(gpio3, bool, 0);
+MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3");
+
+static int ad7877_read(struct spi_device *spi, u16 reg)
+{
+ struct ser_req *req;
+ int status, ret;
+
+ req = kzalloc(sizeof *req, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ spi_message_init(&req->msg);
+
+ req->command = (u16) (AD7877_WRITEADD(AD7877_REG_CTRL1) |
+ AD7877_READADD(reg));
+ req->xfer[0].tx_buf = &req->command;
+ req->xfer[0].len = 2;
+ req->xfer[0].cs_change = 1;
+
+ req->xfer[1].rx_buf = &req->sample;
+ req->xfer[1].len = 2;
+
+ spi_message_add_tail(&req->xfer[0], &req->msg);
+ spi_message_add_tail(&req->xfer[1], &req->msg);
+
+ status = spi_sync(spi, &req->msg);
+ ret = status ? : req->sample;
+
+ kfree(req);
+
+ return ret;
+}
+
+static int ad7877_write(struct spi_device *spi, u16 reg, u16 val)
+{
+ struct ser_req *req;
+ int status;
+
+ req = kzalloc(sizeof *req, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ spi_message_init(&req->msg);
+
+ req->command = (u16) (AD7877_WRITEADD(reg) | (val & MAX_12BIT));
+ req->xfer[0].tx_buf = &req->command;
+ req->xfer[0].len = 2;
+
+ spi_message_add_tail(&req->xfer[0], &req->msg);
+
+ status = spi_sync(spi, &req->msg);
+
+ kfree(req);
+
+ return status;
+}
+
+static int ad7877_read_adc(struct spi_device *spi, unsigned command)
+{
+ struct ad7877 *ts = spi_get_drvdata(spi);
+ struct ser_req *req;
+ int status;
+ int sample;
+ int i;
+
+ req = kzalloc(sizeof *req, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ spi_message_init(&req->msg);
+
+ /* activate reference, so it has time to settle; */
+ req->ref_on = AD7877_WRITEADD(AD7877_REG_CTRL2) |
+ AD7877_POL(ts->stopacq_polarity) |
+ AD7877_AVG(0) | AD7877_PM(2) | AD7877_TMR(0) |
+ AD7877_ACQ(ts->acquisition_time) | AD7877_FCD(0);
+
+ req->reset = AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_MODE_NOC;
+
+ req->command = (u16) command;
+
+ req->xfer[0].tx_buf = &req->reset;
+ req->xfer[0].len = 2;
+ req->xfer[0].cs_change = 1;
+
+ req->xfer[1].tx_buf = &req->ref_on;
+ req->xfer[1].len = 2;
+ req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+ req->xfer[1].cs_change = 1;
+
+ req->xfer[2].tx_buf = &req->command;
+ req->xfer[2].len = 2;
+ req->xfer[2].delay_usecs = ts->vref_delay_usecs;
+ req->xfer[2].cs_change = 1;
+
+ req->xfer[3].rx_buf = &req->sample;
+ req->xfer[3].len = 2;
+ req->xfer[3].cs_change = 1;
+
+ req->xfer[4].tx_buf = &ts->cmd_crtl2; /*REF OFF*/
+ req->xfer[4].len = 2;
+ req->xfer[4].cs_change = 1;
+
+ req->xfer[5].tx_buf = &ts->cmd_crtl1; /*DEFAULT*/
+ req->xfer[5].len = 2;
+
+ /* group all the transfers together, so we can't interfere with
+ * reading touchscreen state; disable penirq while sampling
+ */
+ for (i = 0; i < 6; i++)
+ spi_message_add_tail(&req->xfer[i], &req->msg);
+
+ status = spi_sync(spi, &req->msg);
+ sample = req->sample;
+
+ kfree(req);
+
+ return status ? : sample;
+}
+
+static int ad7877_process_data(struct ad7877 *ts)
+{
+ struct input_dev *input_dev = ts->input;
+ unsigned Rt;
+ u16 x, y, z1, z2;
+
+ x = ts->conversion_data[AD7877_SEQ_XPOS] & MAX_12BIT;
+ y = ts->conversion_data[AD7877_SEQ_YPOS] & MAX_12BIT;
+ z1 = ts->conversion_data[AD7877_SEQ_Z1] & MAX_12BIT;
+ z2 = ts->conversion_data[AD7877_SEQ_Z2] & MAX_12BIT;
+
+ /*
+ * The samples processed here are already preprocessed by the AD7877.
+ * The preprocessing function consists of an averaging filter.
+ * The combination of 'first conversion delay' and averaging provides a robust solution,
+ * discarding the spurious noise in the signal and keeping only the data of interest.
+ * The size of the averaging filter is programmable. (dev.platform_data, see linux/spi/ad7877.h)
+ * Other user-programmable conversion controls include variable acquisition time,
+ * and first conversion delay. Up to 16 averages can be taken per conversion.
+ */
+
+ if (likely(x && z1)) {
+ /* compute touch pressure resistance using equation #1 */
+ Rt = (z2 - z1) * x * ts->x_plate_ohms;
+ Rt /= z1;
+ Rt = (Rt + 2047) >> 12;
+
+ /*
+ * Sample found inconsistent, pressure is beyond
+ * the maximum. Don't report it to user space.
+ */
+ if (Rt > ts->pressure_max)
+ return -EINVAL;
+
+ if (!timer_pending(&ts->timer))
+ input_report_key(input_dev, BTN_TOUCH, 1);
+
+ input_report_abs(input_dev, ABS_X, x);
+ input_report_abs(input_dev, ABS_Y, y);
+ input_report_abs(input_dev, ABS_PRESSURE, Rt);
+ input_sync(input_dev);
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static inline void ad7877_ts_event_release(struct ad7877 *ts)
+{
+ struct input_dev *input_dev = ts->input;
+
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_sync(input_dev);
+}
+
+static void ad7877_timer(unsigned long handle)
+{
+ struct ad7877 *ts = (void *)handle;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ts->lock, flags);
+ ad7877_ts_event_release(ts);
+ spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+static irqreturn_t ad7877_irq(int irq, void *handle)
+{
+ struct ad7877 *ts = handle;
+ unsigned long flags;
+ int error;
+
+ error = spi_sync(ts->spi, &ts->msg);
+ if (error) {
+ dev_err(&ts->spi->dev, "spi_sync --> %d\n", error);
+ goto out;
+ }
+
+ spin_lock_irqsave(&ts->lock, flags);
+ error = ad7877_process_data(ts);
+ if (!error)
+ mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
+ spin_unlock_irqrestore(&ts->lock, flags);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static void ad7877_disable(struct ad7877 *ts)
+{
+ mutex_lock(&ts->mutex);
+
+ if (!ts->disabled) {
+ ts->disabled = true;
+ disable_irq(ts->spi->irq);
+
+ if (del_timer_sync(&ts->timer))
+ ad7877_ts_event_release(ts);
+ }
+
+ /*
+ * We know the chip's in lowpower mode since we always
+ * leave it that way after every request
+ */
+
+ mutex_unlock(&ts->mutex);
+}
+
+static void ad7877_enable(struct ad7877 *ts)
+{
+ mutex_lock(&ts->mutex);
+
+ if (ts->disabled) {
+ ts->disabled = false;
+ enable_irq(ts->spi->irq);
+ }
+
+ mutex_unlock(&ts->mutex);
+}
+
+#define SHOW(name) static ssize_t \
+name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct ad7877 *ts = dev_get_drvdata(dev); \
+ ssize_t v = ad7877_read_adc(ts->spi, \
+ AD7877_READ_CHAN(name)); \
+ if (v < 0) \
+ return v; \
+ return sprintf(buf, "%u\n", (unsigned) v); \
+} \
+static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);
+
+SHOW(aux1)
+SHOW(aux2)
+SHOW(aux3)
+SHOW(bat1)
+SHOW(bat2)
+SHOW(temp1)
+SHOW(temp2)
+
+static ssize_t ad7877_disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t ad7877_disable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ if (val)
+ ad7877_disable(ts);
+ else
+ ad7877_enable(ts);
+
+ return count;
+}
+
+static DEVICE_ATTR(disable, 0664, ad7877_disable_show, ad7877_disable_store);
+
+static ssize_t ad7877_dac_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->dac);
+}
+
+static ssize_t ad7877_dac_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ts->mutex);
+ ts->dac = val & 0xFF;
+ ad7877_write(ts->spi, AD7877_REG_DAC, (ts->dac << 4) | AD7877_DAC_CONF);
+ mutex_unlock(&ts->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(dac, 0664, ad7877_dac_show, ad7877_dac_store);
+
+static ssize_t ad7877_gpio3_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->gpio3);
+}
+
+static ssize_t ad7877_gpio3_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ts->mutex);
+ ts->gpio3 = !!val;
+ ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA |
+ (ts->gpio4 << 4) | (ts->gpio3 << 5));
+ mutex_unlock(&ts->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(gpio3, 0664, ad7877_gpio3_show, ad7877_gpio3_store);
+
+static ssize_t ad7877_gpio4_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->gpio4);
+}
+
+static ssize_t ad7877_gpio4_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ts->mutex);
+ ts->gpio4 = !!val;
+ ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA |
+ (ts->gpio4 << 4) | (ts->gpio3 << 5));
+ mutex_unlock(&ts->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(gpio4, 0664, ad7877_gpio4_show, ad7877_gpio4_store);
+
+static struct attribute *ad7877_attributes[] = {
+ &dev_attr_temp1.attr,
+ &dev_attr_temp2.attr,
+ &dev_attr_aux1.attr,
+ &dev_attr_aux2.attr,
+ &dev_attr_aux3.attr,
+ &dev_attr_bat1.attr,
+ &dev_attr_bat2.attr,
+ &dev_attr_disable.attr,
+ &dev_attr_dac.attr,
+ &dev_attr_gpio3.attr,
+ &dev_attr_gpio4.attr,
+ NULL
+};
+
+static umode_t ad7877_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ umode_t mode = attr->mode;
+
+ if (attr == &dev_attr_aux3.attr) {
+ if (gpio3)
+ mode = 0;
+ } else if (attr == &dev_attr_gpio3.attr) {
+ if (!gpio3)
+ mode = 0;
+ }
+
+ return mode;
+}
+
+static const struct attribute_group ad7877_attr_group = {
+ .is_visible = ad7877_attr_is_visible,
+ .attrs = ad7877_attributes,
+};
+
+static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)
+{
+ struct spi_message *m;
+ int i;
+
+ ts->cmd_crtl2 = AD7877_WRITEADD(AD7877_REG_CTRL2) |
+ AD7877_POL(ts->stopacq_polarity) |
+ AD7877_AVG(ts->averaging) | AD7877_PM(1) |
+ AD7877_TMR(ts->pen_down_acc_interval) |
+ AD7877_ACQ(ts->acquisition_time) |
+ AD7877_FCD(ts->first_conversion_delay);
+
+ ad7877_write(spi, AD7877_REG_CTRL2, ts->cmd_crtl2);
+
+ ts->cmd_crtl1 = AD7877_WRITEADD(AD7877_REG_CTRL1) |
+ AD7877_READADD(AD7877_REG_XPLUS-1) |
+ AD7877_MODE_SEQ1 | AD7877_DFR;
+
+ ad7877_write(spi, AD7877_REG_CTRL1, ts->cmd_crtl1);
+
+ ts->cmd_dummy = 0;
+
+ m = &ts->msg;
+
+ spi_message_init(m);
+
+ m->context = ts;
+
+ ts->xfer[0].tx_buf = &ts->cmd_crtl1;
+ ts->xfer[0].len = 2;
+ ts->xfer[0].cs_change = 1;
+
+ spi_message_add_tail(&ts->xfer[0], m);
+
+ ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */
+ ts->xfer[1].len = 2;
+ ts->xfer[1].cs_change = 1;
+
+ spi_message_add_tail(&ts->xfer[1], m);
+
+ for (i = 0; i < AD7877_NR_SENSE; i++) {
+ ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i];
+ ts->xfer[i + 2].len = 2;
+ if (i < (AD7877_NR_SENSE - 1))
+ ts->xfer[i + 2].cs_change = 1;
+ spi_message_add_tail(&ts->xfer[i + 2], m);
+ }
+}
+
+static int ad7877_probe(struct spi_device *spi)
+{
+ struct ad7877 *ts;
+ struct input_dev *input_dev;
+ struct ad7877_platform_data *pdata = dev_get_platdata(&spi->dev);
+ int err;
+ u16 verify;
+
+ if (!spi->irq) {
+ dev_dbg(&spi->dev, "no IRQ?\n");
+ return -ENODEV;
+ }
+
+ if (!pdata) {
+ dev_dbg(&spi->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ /* don't exceed max specified SPI CLK frequency */
+ if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
+ dev_dbg(&spi->dev, "SPI CLK %d Hz?\n",spi->max_speed_hz);
+ return -EINVAL;
+ }
+
+ spi->bits_per_word = 16;
+ err = spi_setup(spi);
+ if (err) {
+ dev_dbg(&spi->dev, "spi master doesn't support 16 bits/word\n");
+ return err;
+ }
+
+ ts = kzalloc(sizeof(struct ad7877), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ spi_set_drvdata(spi, ts);
+ ts->spi = spi;
+ ts->input = input_dev;
+
+ setup_timer(&ts->timer, ad7877_timer, (unsigned long) ts);
+ mutex_init(&ts->mutex);
+ spin_lock_init(&ts->lock);
+
+ ts->model = pdata->model ? : 7877;
+ ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
+ ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+ ts->pressure_max = pdata->pressure_max ? : ~0;
+
+ ts->stopacq_polarity = pdata->stopacq_polarity;
+ ts->first_conversion_delay = pdata->first_conversion_delay;
+ ts->acquisition_time = pdata->acquisition_time;
+ ts->averaging = pdata->averaging;
+ ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
+
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
+
+ input_dev->name = "AD7877 Touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->dev.parent = &spi->dev;
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(ABS_X, input_dev->absbit);
+ __set_bit(ABS_Y, input_dev->absbit);
+ __set_bit(ABS_PRESSURE, input_dev->absbit);
+
+ input_set_abs_params(input_dev, ABS_X,
+ pdata->x_min ? : 0,
+ pdata->x_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ pdata->y_min ? : 0,
+ pdata->y_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ pdata->pressure_min, pdata->pressure_max, 0, 0);
+
+ ad7877_write(spi, AD7877_REG_SEQ1, AD7877_MM_SEQUENCE);
+
+ verify = ad7877_read(spi, AD7877_REG_SEQ1);
+
+ if (verify != AD7877_MM_SEQUENCE){
+ dev_err(&spi->dev, "%s: Failed to probe %s\n",
+ dev_name(&spi->dev), input_dev->name);
+ err = -ENODEV;
+ goto err_free_mem;
+ }
+
+ if (gpio3)
+ ad7877_write(spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_3_CONF);
+
+ ad7877_setup_ts_def_msg(spi, ts);
+
+ /* Request AD7877 /DAV GPIO interrupt */
+
+ err = request_threaded_irq(spi->irq, NULL, ad7877_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ spi->dev.driver->name, ts);
+ if (err) {
+ dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
+ goto err_free_mem;
+ }
+
+ err = sysfs_create_group(&spi->dev.kobj, &ad7877_attr_group);
+ if (err)
+ goto err_free_irq;
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_remove_attr_group;
+
+ return 0;
+
+err_remove_attr_group:
+ sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
+err_free_irq:
+ free_irq(spi->irq, ts);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+ return err;
+}
+
+static int ad7877_remove(struct spi_device *spi)
+{
+ struct ad7877 *ts = spi_get_drvdata(spi);
+
+ sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
+
+ ad7877_disable(ts);
+ free_irq(ts->spi->irq, ts);
+
+ input_unregister_device(ts->input);
+ kfree(ts);
+
+ dev_dbg(&spi->dev, "unregistered touchscreen\n");
+
+ return 0;
+}
+
+static int __maybe_unused ad7877_suspend(struct device *dev)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+
+ ad7877_disable(ts);
+
+ return 0;
+}
+
+static int __maybe_unused ad7877_resume(struct device *dev)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+
+ ad7877_enable(ts);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ad7877_pm, ad7877_suspend, ad7877_resume);
+
+static struct spi_driver ad7877_driver = {
+ .driver = {
+ .name = "ad7877",
+ .owner = THIS_MODULE,
+ .pm = &ad7877_pm,
+ },
+ .probe = ad7877_probe,
+ .remove = ad7877_remove,
+};
+
+module_spi_driver(ad7877_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("AD7877 touchscreen Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:ad7877");
diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c
new file mode 100644
index 000000000..dcf390771
--- /dev/null
+++ b/drivers/input/touchscreen/ad7879-i2c.c
@@ -0,0 +1,109 @@
+/*
+ * AD7879-1/AD7889-1 touchscreen (I2C bus)
+ *
+ * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h> /* BUS_I2C */
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/pm.h>
+
+#include "ad7879.h"
+
+#define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */
+
+/* All registers are word-sized.
+ * AD7879 uses a high-byte first convention.
+ */
+static int ad7879_i2c_read(struct device *dev, u8 reg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int ad7879_i2c_multi_read(struct device *dev,
+ u8 first_reg, u8 count, u16 *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 idx;
+
+ i2c_smbus_read_i2c_block_data(client, first_reg, count * 2, (u8 *)buf);
+
+ for (idx = 0; idx < count; ++idx)
+ buf[idx] = swab16(buf[idx]);
+
+ return 0;
+}
+
+static int ad7879_i2c_write(struct device *dev, u8 reg, u16 val)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return i2c_smbus_write_word_swapped(client, reg, val);
+}
+
+static const struct ad7879_bus_ops ad7879_i2c_bus_ops = {
+ .bustype = BUS_I2C,
+ .read = ad7879_i2c_read,
+ .multi_read = ad7879_i2c_multi_read,
+ .write = ad7879_i2c_write,
+};
+
+static int ad7879_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ad7879 *ts;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_err(&client->dev, "SMBUS Word Data not Supported\n");
+ return -EIO;
+ }
+
+ ts = ad7879_probe(&client->dev, AD7879_DEVID, client->irq,
+ &ad7879_i2c_bus_ops);
+ if (IS_ERR(ts))
+ return PTR_ERR(ts);
+
+ i2c_set_clientdata(client, ts);
+
+ return 0;
+}
+
+static int ad7879_i2c_remove(struct i2c_client *client)
+{
+ struct ad7879 *ts = i2c_get_clientdata(client);
+
+ ad7879_remove(ts);
+
+ return 0;
+}
+
+static const struct i2c_device_id ad7879_id[] = {
+ { "ad7879", 0 },
+ { "ad7889", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ad7879_id);
+
+static struct i2c_driver ad7879_i2c_driver = {
+ .driver = {
+ .name = "ad7879",
+ .owner = THIS_MODULE,
+ .pm = &ad7879_pm_ops,
+ },
+ .probe = ad7879_i2c_probe,
+ .remove = ad7879_i2c_remove,
+ .id_table = ad7879_id,
+};
+
+module_i2c_driver(ad7879_i2c_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c
new file mode 100644
index 000000000..1a7b11435
--- /dev/null
+++ b/drivers/input/touchscreen/ad7879-spi.c
@@ -0,0 +1,164 @@
+/*
+ * AD7879/AD7889 touchscreen (SPI bus)
+ *
+ * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h> /* BUS_SPI */
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+
+#include "ad7879.h"
+
+#define AD7879_DEVID 0x7A /* AD7879/AD7889 */
+
+#define MAX_SPI_FREQ_HZ 5000000
+#define AD7879_CMD_MAGIC 0xE000
+#define AD7879_CMD_READ (1 << 10)
+#define AD7879_CMD(reg) (AD7879_CMD_MAGIC | ((reg) & 0xF))
+#define AD7879_WRITECMD(reg) (AD7879_CMD(reg))
+#define AD7879_READCMD(reg) (AD7879_CMD(reg) | AD7879_CMD_READ)
+
+/*
+ * ad7879_read/write are only used for initial setup and for sysfs controls.
+ * The main traffic is done in ad7879_collect().
+ */
+
+static int ad7879_spi_xfer(struct spi_device *spi,
+ u16 cmd, u8 count, u16 *tx_buf, u16 *rx_buf)
+{
+ struct spi_message msg;
+ struct spi_transfer *xfers;
+ void *spi_data;
+ u16 *command;
+ u16 *_rx_buf = _rx_buf; /* shut gcc up */
+ u8 idx;
+ int ret;
+
+ xfers = spi_data = kzalloc(sizeof(*xfers) * (count + 2), GFP_KERNEL);
+ if (!spi_data)
+ return -ENOMEM;
+
+ spi_message_init(&msg);
+
+ command = spi_data;
+ command[0] = cmd;
+ if (count == 1) {
+ /* ad7879_spi_{read,write} gave us buf on stack */
+ command[1] = *tx_buf;
+ tx_buf = &command[1];
+ _rx_buf = rx_buf;
+ rx_buf = &command[2];
+ }
+
+ ++xfers;
+ xfers[0].tx_buf = command;
+ xfers[0].len = 2;
+ spi_message_add_tail(&xfers[0], &msg);
+ ++xfers;
+
+ for (idx = 0; idx < count; ++idx) {
+ if (rx_buf)
+ xfers[idx].rx_buf = &rx_buf[idx];
+ if (tx_buf)
+ xfers[idx].tx_buf = &tx_buf[idx];
+ xfers[idx].len = 2;
+ spi_message_add_tail(&xfers[idx], &msg);
+ }
+
+ ret = spi_sync(spi, &msg);
+
+ if (count == 1)
+ _rx_buf[0] = command[2];
+
+ kfree(spi_data);
+
+ return ret;
+}
+
+static int ad7879_spi_multi_read(struct device *dev,
+ u8 first_reg, u8 count, u16 *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ return ad7879_spi_xfer(spi, AD7879_READCMD(first_reg), count, NULL, buf);
+}
+
+static int ad7879_spi_read(struct device *dev, u8 reg)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ u16 ret, dummy;
+
+ return ad7879_spi_xfer(spi, AD7879_READCMD(reg), 1, &dummy, &ret) ? : ret;
+}
+
+static int ad7879_spi_write(struct device *dev, u8 reg, u16 val)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ u16 dummy;
+
+ return ad7879_spi_xfer(spi, AD7879_WRITECMD(reg), 1, &val, &dummy);
+}
+
+static const struct ad7879_bus_ops ad7879_spi_bus_ops = {
+ .bustype = BUS_SPI,
+ .read = ad7879_spi_read,
+ .multi_read = ad7879_spi_multi_read,
+ .write = ad7879_spi_write,
+};
+
+static int ad7879_spi_probe(struct spi_device *spi)
+{
+ struct ad7879 *ts;
+ int err;
+
+ /* don't exceed max specified SPI CLK frequency */
+ if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
+ dev_err(&spi->dev, "SPI CLK %d Hz?\n", spi->max_speed_hz);
+ return -EINVAL;
+ }
+
+ spi->bits_per_word = 16;
+ err = spi_setup(spi);
+ if (err) {
+ dev_dbg(&spi->dev, "spi master doesn't support 16 bits/word\n");
+ return err;
+ }
+
+ ts = ad7879_probe(&spi->dev, AD7879_DEVID, spi->irq, &ad7879_spi_bus_ops);
+ if (IS_ERR(ts))
+ return PTR_ERR(ts);
+
+ spi_set_drvdata(spi, ts);
+
+ return 0;
+}
+
+static int ad7879_spi_remove(struct spi_device *spi)
+{
+ struct ad7879 *ts = spi_get_drvdata(spi);
+
+ ad7879_remove(ts);
+
+ return 0;
+}
+
+static struct spi_driver ad7879_spi_driver = {
+ .driver = {
+ .name = "ad7879",
+ .owner = THIS_MODULE,
+ .pm = &ad7879_pm_ops,
+ },
+ .probe = ad7879_spi_probe,
+ .remove = ad7879_spi_remove,
+};
+
+module_spi_driver(ad7879_spi_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:ad7879");
diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c
new file mode 100644
index 000000000..fec66ad80
--- /dev/null
+++ b/drivers/input/touchscreen/ad7879.c
@@ -0,0 +1,647 @@
+/*
+ * AD7879/AD7889 based touchscreen and GPIO driver
+ *
+ * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ *
+ * History:
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * Various changes: Imre Deak <imre.deak@nokia.com>
+ *
+ * Using code from:
+ * - 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
+ * - ad7877.c
+ * Copyright (C) 2006-2008 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+
+#include <linux/spi/ad7879.h>
+#include <linux/module.h>
+#include "ad7879.h"
+
+#define AD7879_REG_ZEROS 0
+#define AD7879_REG_CTRL1 1
+#define AD7879_REG_CTRL2 2
+#define AD7879_REG_CTRL3 3
+#define AD7879_REG_AUX1HIGH 4
+#define AD7879_REG_AUX1LOW 5
+#define AD7879_REG_TEMP1HIGH 6
+#define AD7879_REG_TEMP1LOW 7
+#define AD7879_REG_XPLUS 8
+#define AD7879_REG_YPLUS 9
+#define AD7879_REG_Z1 10
+#define AD7879_REG_Z2 11
+#define AD7879_REG_AUXVBAT 12
+#define AD7879_REG_TEMP 13
+#define AD7879_REG_REVID 14
+
+/* Control REG 1 */
+#define AD7879_TMR(x) ((x & 0xFF) << 0)
+#define AD7879_ACQ(x) ((x & 0x3) << 8)
+#define AD7879_MODE_NOC (0 << 10) /* Do not convert */
+#define AD7879_MODE_SCC (1 << 10) /* Single channel conversion */
+#define AD7879_MODE_SEQ0 (2 << 10) /* Sequence 0 in Slave Mode */
+#define AD7879_MODE_SEQ1 (3 << 10) /* Sequence 1 in Master Mode */
+#define AD7879_MODE_INT (1 << 15) /* PENIRQ disabled INT enabled */
+
+/* Control REG 2 */
+#define AD7879_FCD(x) ((x & 0x3) << 0)
+#define AD7879_RESET (1 << 4)
+#define AD7879_MFS(x) ((x & 0x3) << 5)
+#define AD7879_AVG(x) ((x & 0x3) << 7)
+#define AD7879_SER (1 << 9) /* non-differential */
+#define AD7879_DFR (0 << 9) /* differential */
+#define AD7879_GPIOPOL (1 << 10)
+#define AD7879_GPIODIR (1 << 11)
+#define AD7879_GPIO_DATA (1 << 12)
+#define AD7879_GPIO_EN (1 << 13)
+#define AD7879_PM(x) ((x & 0x3) << 14)
+#define AD7879_PM_SHUTDOWN (0)
+#define AD7879_PM_DYN (1)
+#define AD7879_PM_FULLON (2)
+
+/* Control REG 3 */
+#define AD7879_TEMPMASK_BIT (1<<15)
+#define AD7879_AUXVBATMASK_BIT (1<<14)
+#define AD7879_INTMODE_BIT (1<<13)
+#define AD7879_GPIOALERTMASK_BIT (1<<12)
+#define AD7879_AUXLOW_BIT (1<<11)
+#define AD7879_AUXHIGH_BIT (1<<10)
+#define AD7879_TEMPLOW_BIT (1<<9)
+#define AD7879_TEMPHIGH_BIT (1<<8)
+#define AD7879_YPLUS_BIT (1<<7)
+#define AD7879_XPLUS_BIT (1<<6)
+#define AD7879_Z1_BIT (1<<5)
+#define AD7879_Z2_BIT (1<<4)
+#define AD7879_AUX_BIT (1<<3)
+#define AD7879_VBAT_BIT (1<<2)
+#define AD7879_TEMP_BIT (1<<1)
+
+enum {
+ AD7879_SEQ_XPOS = 0,
+ AD7879_SEQ_YPOS = 1,
+ AD7879_SEQ_Z1 = 2,
+ AD7879_SEQ_Z2 = 3,
+ AD7879_NR_SENSE = 4,
+};
+
+#define MAX_12BIT ((1<<12)-1)
+#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50)
+
+struct ad7879 {
+ const struct ad7879_bus_ops *bops;
+
+ struct device *dev;
+ struct input_dev *input;
+ struct timer_list timer;
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gc;
+ struct mutex mutex;
+#endif
+ unsigned int irq;
+ bool disabled; /* P: input->mutex */
+ bool suspended; /* P: input->mutex */
+ bool swap_xy;
+ u16 conversion_data[AD7879_NR_SENSE];
+ char phys[32];
+ u8 first_conversion_delay;
+ u8 acquisition_time;
+ u8 averaging;
+ u8 pen_down_acc_interval;
+ u8 median;
+ u16 x_plate_ohms;
+ u16 pressure_max;
+ u16 cmd_crtl1;
+ u16 cmd_crtl2;
+ u16 cmd_crtl3;
+ int x;
+ int y;
+ int Rt;
+};
+
+static int ad7879_read(struct ad7879 *ts, u8 reg)
+{
+ return ts->bops->read(ts->dev, reg);
+}
+
+static int ad7879_multi_read(struct ad7879 *ts, u8 first_reg, u8 count, u16 *buf)
+{
+ return ts->bops->multi_read(ts->dev, first_reg, count, buf);
+}
+
+static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val)
+{
+ return ts->bops->write(ts->dev, reg, val);
+}
+
+static int ad7879_report(struct ad7879 *ts)
+{
+ struct input_dev *input_dev = ts->input;
+ unsigned Rt;
+ u16 x, y, z1, z2;
+
+ x = ts->conversion_data[AD7879_SEQ_XPOS] & MAX_12BIT;
+ y = ts->conversion_data[AD7879_SEQ_YPOS] & MAX_12BIT;
+ z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT;
+ z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT;
+
+ if (ts->swap_xy)
+ swap(x, y);
+
+ /*
+ * The samples processed here are already preprocessed by the AD7879.
+ * The preprocessing function consists of a median and an averaging
+ * filter. The combination of these two techniques provides a robust
+ * solution, discarding the spurious noise in the signal and keeping
+ * only the data of interest. The size of both filters is
+ * programmable. (dev.platform_data, see linux/spi/ad7879.h) Other
+ * user-programmable conversion controls include variable acquisition
+ * time, and first conversion delay. Up to 16 averages can be taken
+ * per conversion.
+ */
+
+ if (likely(x && z1)) {
+ /* compute touch pressure resistance using equation #1 */
+ Rt = (z2 - z1) * x * ts->x_plate_ohms;
+ Rt /= z1;
+ Rt = (Rt + 2047) >> 12;
+
+ /*
+ * Sample found inconsistent, pressure is beyond
+ * the maximum. Don't report it to user space.
+ */
+ if (Rt > ts->pressure_max)
+ return -EINVAL;
+
+ /*
+ * Note that we delay reporting events by one sample.
+ * This is done to avoid reporting last sample of the
+ * touch sequence, which may be incomplete if finger
+ * leaves the surface before last reading is taken.
+ */
+ if (timer_pending(&ts->timer)) {
+ /* Touch continues */
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ input_report_abs(input_dev, ABS_X, ts->x);
+ input_report_abs(input_dev, ABS_Y, ts->y);
+ input_report_abs(input_dev, ABS_PRESSURE, ts->Rt);
+ input_sync(input_dev);
+ }
+
+ ts->x = x;
+ ts->y = y;
+ ts->Rt = Rt;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static void ad7879_ts_event_release(struct ad7879 *ts)
+{
+ struct input_dev *input_dev = ts->input;
+
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_sync(input_dev);
+}
+
+static void ad7879_timer(unsigned long handle)
+{
+ struct ad7879 *ts = (void *)handle;
+
+ ad7879_ts_event_release(ts);
+}
+
+static irqreturn_t ad7879_irq(int irq, void *handle)
+{
+ struct ad7879 *ts = handle;
+
+ ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data);
+
+ if (!ad7879_report(ts))
+ mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
+
+ return IRQ_HANDLED;
+}
+
+static void __ad7879_enable(struct ad7879 *ts)
+{
+ ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);
+ ad7879_write(ts, AD7879_REG_CTRL3, ts->cmd_crtl3);
+ ad7879_write(ts, AD7879_REG_CTRL1, ts->cmd_crtl1);
+
+ enable_irq(ts->irq);
+}
+
+static void __ad7879_disable(struct ad7879 *ts)
+{
+ u16 reg = (ts->cmd_crtl2 & ~AD7879_PM(-1)) |
+ AD7879_PM(AD7879_PM_SHUTDOWN);
+ disable_irq(ts->irq);
+
+ if (del_timer_sync(&ts->timer))
+ ad7879_ts_event_release(ts);
+
+ ad7879_write(ts, AD7879_REG_CTRL2, reg);
+}
+
+
+static int ad7879_open(struct input_dev *input)
+{
+ struct ad7879 *ts = input_get_drvdata(input);
+
+ /* protected by input->mutex */
+ if (!ts->disabled && !ts->suspended)
+ __ad7879_enable(ts);
+
+ return 0;
+}
+
+static void ad7879_close(struct input_dev* input)
+{
+ struct ad7879 *ts = input_get_drvdata(input);
+
+ /* protected by input->mutex */
+ if (!ts->disabled && !ts->suspended)
+ __ad7879_disable(ts);
+}
+
+static int __maybe_unused ad7879_suspend(struct device *dev)
+{
+ struct ad7879 *ts = dev_get_drvdata(dev);
+
+ mutex_lock(&ts->input->mutex);
+
+ if (!ts->suspended && !ts->disabled && ts->input->users)
+ __ad7879_disable(ts);
+
+ ts->suspended = true;
+
+ mutex_unlock(&ts->input->mutex);
+
+ return 0;
+}
+
+static int __maybe_unused ad7879_resume(struct device *dev)
+{
+ struct ad7879 *ts = dev_get_drvdata(dev);
+
+ mutex_lock(&ts->input->mutex);
+
+ if (ts->suspended && !ts->disabled && ts->input->users)
+ __ad7879_enable(ts);
+
+ ts->suspended = false;
+
+ mutex_unlock(&ts->input->mutex);
+
+ return 0;
+}
+
+SIMPLE_DEV_PM_OPS(ad7879_pm_ops, ad7879_suspend, ad7879_resume);
+EXPORT_SYMBOL(ad7879_pm_ops);
+
+static void ad7879_toggle(struct ad7879 *ts, bool disable)
+{
+ mutex_lock(&ts->input->mutex);
+
+ if (!ts->suspended && ts->input->users != 0) {
+
+ if (disable) {
+ if (ts->disabled)
+ __ad7879_enable(ts);
+ } else {
+ if (!ts->disabled)
+ __ad7879_disable(ts);
+ }
+ }
+
+ ts->disabled = disable;
+
+ mutex_unlock(&ts->input->mutex);
+}
+
+static ssize_t ad7879_disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad7879 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t ad7879_disable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ad7879 *ts = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ ad7879_toggle(ts, val);
+
+ return count;
+}
+
+static DEVICE_ATTR(disable, 0664, ad7879_disable_show, ad7879_disable_store);
+
+static struct attribute *ad7879_attributes[] = {
+ &dev_attr_disable.attr,
+ NULL
+};
+
+static const struct attribute_group ad7879_attr_group = {
+ .attrs = ad7879_attributes,
+};
+
+#ifdef CONFIG_GPIOLIB
+static int ad7879_gpio_direction_input(struct gpio_chip *chip,
+ unsigned gpio)
+{
+ struct ad7879 *ts = container_of(chip, struct ad7879, gc);
+ int err;
+
+ mutex_lock(&ts->mutex);
+ ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIODIR | AD7879_GPIOPOL;
+ err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);
+ mutex_unlock(&ts->mutex);
+
+ return err;
+}
+
+static int ad7879_gpio_direction_output(struct gpio_chip *chip,
+ unsigned gpio, int level)
+{
+ struct ad7879 *ts = container_of(chip, struct ad7879, gc);
+ int err;
+
+ mutex_lock(&ts->mutex);
+ ts->cmd_crtl2 &= ~AD7879_GPIODIR;
+ ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIOPOL;
+ if (level)
+ ts->cmd_crtl2 |= AD7879_GPIO_DATA;
+ else
+ ts->cmd_crtl2 &= ~AD7879_GPIO_DATA;
+
+ err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);
+ mutex_unlock(&ts->mutex);
+
+ return err;
+}
+
+static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ad7879 *ts = container_of(chip, struct ad7879, gc);
+ u16 val;
+
+ mutex_lock(&ts->mutex);
+ val = ad7879_read(ts, AD7879_REG_CTRL2);
+ mutex_unlock(&ts->mutex);
+
+ return !!(val & AD7879_GPIO_DATA);
+}
+
+static void ad7879_gpio_set_value(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct ad7879 *ts = container_of(chip, struct ad7879, gc);
+
+ mutex_lock(&ts->mutex);
+ if (value)
+ ts->cmd_crtl2 |= AD7879_GPIO_DATA;
+ else
+ ts->cmd_crtl2 &= ~AD7879_GPIO_DATA;
+
+ ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);
+ mutex_unlock(&ts->mutex);
+}
+
+static int ad7879_gpio_add(struct ad7879 *ts,
+ const struct ad7879_platform_data *pdata)
+{
+ int ret = 0;
+
+ mutex_init(&ts->mutex);
+
+ if (pdata->gpio_export) {
+ ts->gc.direction_input = ad7879_gpio_direction_input;
+ ts->gc.direction_output = ad7879_gpio_direction_output;
+ ts->gc.get = ad7879_gpio_get_value;
+ ts->gc.set = ad7879_gpio_set_value;
+ ts->gc.can_sleep = 1;
+ ts->gc.base = pdata->gpio_base;
+ ts->gc.ngpio = 1;
+ ts->gc.label = "AD7879-GPIO";
+ ts->gc.owner = THIS_MODULE;
+ ts->gc.dev = ts->dev;
+
+ ret = gpiochip_add(&ts->gc);
+ if (ret)
+ dev_err(ts->dev, "failed to register gpio %d\n",
+ ts->gc.base);
+ }
+
+ return ret;
+}
+
+static void ad7879_gpio_remove(struct ad7879 *ts)
+{
+ const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev);
+
+ if (pdata->gpio_export)
+ gpiochip_remove(&ts->gc);
+
+}
+#else
+static inline int ad7879_gpio_add(struct ad7879 *ts,
+ const struct ad7879_platform_data *pdata)
+{
+ return 0;
+}
+
+static inline void ad7879_gpio_remove(struct ad7879 *ts)
+{
+}
+#endif
+
+struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
+ const struct ad7879_bus_ops *bops)
+{
+ struct ad7879_platform_data *pdata = dev_get_platdata(dev);
+ struct ad7879 *ts;
+ struct input_dev *input_dev;
+ int err;
+ u16 revid;
+
+ if (!irq) {
+ dev_err(dev, "no IRQ?\n");
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ if (!pdata) {
+ dev_err(dev, "no platform data?\n");
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ts->bops = bops;
+ ts->dev = dev;
+ ts->input = input_dev;
+ ts->irq = irq;
+ ts->swap_xy = pdata->swap_xy;
+
+ setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);
+
+ ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+ ts->pressure_max = pdata->pressure_max ? : ~0;
+
+ ts->first_conversion_delay = pdata->first_conversion_delay;
+ ts->acquisition_time = pdata->acquisition_time;
+ ts->averaging = pdata->averaging;
+ ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
+ ts->median = pdata->median;
+
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
+
+ input_dev->name = "AD7879 Touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->dev.parent = dev;
+ input_dev->id.bustype = bops->bustype;
+
+ input_dev->open = ad7879_open;
+ input_dev->close = ad7879_close;
+
+ input_set_drvdata(input_dev, ts);
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(ABS_X, input_dev->absbit);
+ __set_bit(ABS_Y, input_dev->absbit);
+ __set_bit(ABS_PRESSURE, input_dev->absbit);
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ input_set_abs_params(input_dev, ABS_X,
+ pdata->x_min ? : 0,
+ pdata->x_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ pdata->y_min ? : 0,
+ pdata->y_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ pdata->pressure_min, pdata->pressure_max, 0, 0);
+
+ err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET);
+ if (err < 0) {
+ dev_err(dev, "Failed to write %s\n", input_dev->name);
+ goto err_free_mem;
+ }
+
+ revid = ad7879_read(ts, AD7879_REG_REVID);
+ input_dev->id.product = (revid & 0xff);
+ input_dev->id.version = revid >> 8;
+ if (input_dev->id.product != devid) {
+ dev_err(dev, "Failed to probe %s (%x vs %x)\n",
+ input_dev->name, devid, revid);
+ err = -ENODEV;
+ goto err_free_mem;
+ }
+
+ ts->cmd_crtl3 = AD7879_YPLUS_BIT |
+ AD7879_XPLUS_BIT |
+ AD7879_Z2_BIT |
+ AD7879_Z1_BIT |
+ AD7879_TEMPMASK_BIT |
+ AD7879_AUXVBATMASK_BIT |
+ AD7879_GPIOALERTMASK_BIT;
+
+ ts->cmd_crtl2 = AD7879_PM(AD7879_PM_DYN) | AD7879_DFR |
+ AD7879_AVG(ts->averaging) |
+ AD7879_MFS(ts->median) |
+ AD7879_FCD(ts->first_conversion_delay);
+
+ ts->cmd_crtl1 = AD7879_MODE_INT | AD7879_MODE_SEQ1 |
+ AD7879_ACQ(ts->acquisition_time) |
+ AD7879_TMR(ts->pen_down_acc_interval);
+
+ err = request_threaded_irq(ts->irq, NULL, ad7879_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(dev), ts);
+ if (err) {
+ dev_err(dev, "irq %d busy?\n", ts->irq);
+ goto err_free_mem;
+ }
+
+ __ad7879_disable(ts);
+
+ err = sysfs_create_group(&dev->kobj, &ad7879_attr_group);
+ if (err)
+ goto err_free_irq;
+
+ err = ad7879_gpio_add(ts, pdata);
+ if (err)
+ goto err_remove_attr;
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_remove_gpio;
+
+ return ts;
+
+err_remove_gpio:
+ ad7879_gpio_remove(ts);
+err_remove_attr:
+ sysfs_remove_group(&dev->kobj, &ad7879_attr_group);
+err_free_irq:
+ free_irq(ts->irq, ts);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+err_out:
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL(ad7879_probe);
+
+void ad7879_remove(struct ad7879 *ts)
+{
+ ad7879_gpio_remove(ts);
+ sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
+ free_irq(ts->irq, ts);
+ input_unregister_device(ts->input);
+ kfree(ts);
+}
+EXPORT_SYMBOL(ad7879_remove);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ad7879.h b/drivers/input/touchscreen/ad7879.h
new file mode 100644
index 000000000..6fd13c48d
--- /dev/null
+++ b/drivers/input/touchscreen/ad7879.h
@@ -0,0 +1,30 @@
+/*
+ * AD7879/AD7889 touchscreen (bus interfaces)
+ *
+ * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _AD7879_H_
+#define _AD7879_H_
+
+#include <linux/types.h>
+
+struct ad7879;
+struct device;
+
+struct ad7879_bus_ops {
+ u16 bustype;
+ int (*read)(struct device *dev, u8 reg);
+ int (*multi_read)(struct device *dev, u8 first_reg, u8 count, u16 *buf);
+ int (*write)(struct device *dev, u8 reg, u16 val);
+};
+
+extern const struct dev_pm_ops ad7879_pm_ops;
+
+struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq,
+ const struct ad7879_bus_ops *bops);
+void ad7879_remove(struct ad7879 *);
+
+#endif
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
new file mode 100644
index 000000000..e4eb8a6c6
--- /dev/null
+++ b/drivers/input/touchscreen/ads7846.c
@@ -0,0 +1,1510 @@
+/*
+ * ADS7846 based touchscreen and sensor driver
+ *
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * Various changes: Imre Deak <imre.deak@nokia.com>
+ *
+ * Using code from:
+ * - 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/types.h>
+#include <linux/hwmon.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+#include <asm/irq.h>
+
+/*
+ * This code has been heavily tested on a Nokia 770, and lightly
+ * tested on other ads7846 devices (OSK/Mistral, Lubbock, Spitz).
+ * TSC2046 is just newer ads7846 silicon.
+ * Support for ads7843 tested on Atmel at91sam926x-EK.
+ * Support for ads7845 has only been stubbed in.
+ * Support for Analog Devices AD7873 and AD7843 tested.
+ *
+ * IRQ handling needs a workaround because of a shortcoming in handling
+ * edge triggered IRQs on some platforms like the OMAP1/2. These
+ * platforms don't handle the ARM lazy IRQ disabling properly, thus we
+ * have to maintain our own SW IRQ disabled status. This should be
+ * removed as soon as the affected platform's IRQ handling is fixed.
+ *
+ * App note sbaa036 talks in more detail about accurate sampling...
+ * that ought to help in situations like LCDs inducing noise (which
+ * can also be helped by using synch signals) and more generally.
+ * This driver tries to utilize the measures described in the app
+ * note. The strength of filtering can be set in the board-* specific
+ * files.
+ */
+
+#define TS_POLL_DELAY 1 /* ms delay before the first sample */
+#define TS_POLL_PERIOD 5 /* ms delay between samples */
+
+/* this driver doesn't aim at the peak continuous sample rate */
+#define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */)
+
+struct ts_event {
+ /*
+ * For portability, we can't read 12 bit values using SPI (which
+ * would make the controller deliver them as native byte order u16
+ * with msbs zeroed). Instead, we read them as two 8-bit values,
+ * *** WHICH NEED BYTESWAPPING *** and range adjustment.
+ */
+ u16 x;
+ u16 y;
+ u16 z1, z2;
+ bool ignore;
+ u8 x_buf[3];
+ u8 y_buf[3];
+};
+
+/*
+ * We allocate this separately to avoid cache line sharing issues when
+ * driver is used with DMA-based SPI controllers (like atmel_spi) on
+ * systems where main memory is not DMA-coherent (most non-x86 boards).
+ */
+struct ads7846_packet {
+ u8 read_x, read_y, read_z1, read_z2, pwrdown;
+ u16 dummy; /* for the pwrdown read */
+ struct ts_event tc;
+ /* for ads7845 with mpc5121 psc spi we use 3-byte buffers */
+ u8 read_x_cmd[3], read_y_cmd[3], pwrdown_cmd[3];
+};
+
+struct ads7846 {
+ struct input_dev *input;
+ char phys[32];
+ char name[32];
+
+ struct spi_device *spi;
+ struct regulator *reg;
+
+#if IS_ENABLED(CONFIG_HWMON)
+ struct device *hwmon;
+#endif
+
+ u16 model;
+ u16 vref_mv;
+ u16 vref_delay_usecs;
+ u16 x_plate_ohms;
+ u16 pressure_max;
+
+ bool swap_xy;
+ bool use_internal;
+
+ struct ads7846_packet *packet;
+
+ struct spi_transfer xfer[18];
+ struct spi_message msg[5];
+ int msg_count;
+ wait_queue_head_t wait;
+
+ bool pendown;
+
+ int read_cnt;
+ int read_rep;
+ int last_read;
+
+ u16 debounce_max;
+ u16 debounce_tol;
+ u16 debounce_rep;
+
+ u16 penirq_recheck_delay_usecs;
+
+ struct mutex lock;
+ bool stopped; /* P: lock */
+ bool disabled; /* P: lock */
+ bool suspended; /* P: lock */
+
+ int (*filter)(void *data, int data_idx, int *val);
+ void *filter_data;
+ void (*filter_cleanup)(void *data);
+ int (*get_pendown_state)(void);
+ int gpio_pendown;
+
+ void (*wait_for_sync)(void);
+};
+
+/* leave chip selected when we're done, for quicker re-select? */
+#if 0
+#define CS_CHANGE(xfer) ((xfer).cs_change = 1)
+#else
+#define CS_CHANGE(xfer) ((xfer).cs_change = 0)
+#endif
+
+/*--------------------------------------------------------------------------*/
+
+/* The ADS7846 has touchscreen and other sensors.
+ * Earlier ads784x chips are somewhat compatible.
+ */
+#define ADS_START (1 << 7)
+#define ADS_A2A1A0_d_y (1 << 4) /* differential */
+#define ADS_A2A1A0_d_z1 (3 << 4) /* differential */
+#define ADS_A2A1A0_d_z2 (4 << 4) /* differential */
+#define ADS_A2A1A0_d_x (5 << 4) /* differential */
+#define ADS_A2A1A0_temp0 (0 << 4) /* non-differential */
+#define ADS_A2A1A0_vbatt (2 << 4) /* non-differential */
+#define ADS_A2A1A0_vaux (6 << 4) /* non-differential */
+#define ADS_A2A1A0_temp1 (7 << 4) /* non-differential */
+#define ADS_8_BIT (1 << 3)
+#define ADS_12_BIT (0 << 3)
+#define ADS_SER (1 << 2) /* non-differential */
+#define ADS_DFR (0 << 2) /* differential */
+#define ADS_PD10_PDOWN (0 << 0) /* low power mode + penirq */
+#define ADS_PD10_ADC_ON (1 << 0) /* ADC on */
+#define ADS_PD10_REF_ON (2 << 0) /* vREF on + penirq */
+#define ADS_PD10_ALL_ON (3 << 0) /* ADC + vREF on */
+
+#define MAX_12BIT ((1<<12)-1)
+
+/* leave ADC powered up (disables penirq) between differential samples */
+#define READ_12BIT_DFR(x, adc, vref) (ADS_START | ADS_A2A1A0_d_ ## x \
+ | ADS_12_BIT | ADS_DFR | \
+ (adc ? ADS_PD10_ADC_ON : 0) | (vref ? ADS_PD10_REF_ON : 0))
+
+#define READ_Y(vref) (READ_12BIT_DFR(y, 1, vref))
+#define READ_Z1(vref) (READ_12BIT_DFR(z1, 1, vref))
+#define READ_Z2(vref) (READ_12BIT_DFR(z2, 1, vref))
+
+#define READ_X(vref) (READ_12BIT_DFR(x, 1, vref))
+#define PWRDOWN (READ_12BIT_DFR(y, 0, 0)) /* LAST */
+
+/* single-ended samples need to first power up reference voltage;
+ * we leave both ADC and VREF powered
+ */
+#define READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \
+ | ADS_12_BIT | ADS_SER)
+
+#define REF_ON (READ_12BIT_DFR(x, 1, 1))
+#define REF_OFF (READ_12BIT_DFR(y, 0, 0))
+
+/* Must be called with ts->lock held */
+static void ads7846_stop(struct ads7846 *ts)
+{
+ if (!ts->disabled && !ts->suspended) {
+ /* Signal IRQ thread to stop polling and disable the handler. */
+ ts->stopped = true;
+ mb();
+ wake_up(&ts->wait);
+ disable_irq(ts->spi->irq);
+ }
+}
+
+/* Must be called with ts->lock held */
+static void ads7846_restart(struct ads7846 *ts)
+{
+ if (!ts->disabled && !ts->suspended) {
+ /* Tell IRQ thread that it may poll the device. */
+ ts->stopped = false;
+ mb();
+ enable_irq(ts->spi->irq);
+ }
+}
+
+/* Must be called with ts->lock held */
+static void __ads7846_disable(struct ads7846 *ts)
+{
+ ads7846_stop(ts);
+ regulator_disable(ts->reg);
+
+ /*
+ * We know the chip's in low power mode since we always
+ * leave it that way after every request
+ */
+}
+
+/* Must be called with ts->lock held */
+static void __ads7846_enable(struct ads7846 *ts)
+{
+ int error;
+
+ error = regulator_enable(ts->reg);
+ if (error != 0)
+ dev_err(&ts->spi->dev, "Failed to enable supply: %d\n", error);
+
+ ads7846_restart(ts);
+}
+
+static void ads7846_disable(struct ads7846 *ts)
+{
+ mutex_lock(&ts->lock);
+
+ if (!ts->disabled) {
+
+ if (!ts->suspended)
+ __ads7846_disable(ts);
+
+ ts->disabled = true;
+ }
+
+ mutex_unlock(&ts->lock);
+}
+
+static void ads7846_enable(struct ads7846 *ts)
+{
+ mutex_lock(&ts->lock);
+
+ if (ts->disabled) {
+
+ ts->disabled = false;
+
+ if (!ts->suspended)
+ __ads7846_enable(ts);
+ }
+
+ mutex_unlock(&ts->lock);
+}
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Non-touchscreen sensors only use single-ended conversions.
+ * The range is GND..vREF. The ads7843 and ads7835 must use external vREF;
+ * ads7846 lets that pin be unconnected, to use internal vREF.
+ */
+
+struct ser_req {
+ u8 ref_on;
+ u8 command;
+ u8 ref_off;
+ u16 scratch;
+ struct spi_message msg;
+ struct spi_transfer xfer[6];
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ __be16 sample ____cacheline_aligned;
+};
+
+struct ads7845_ser_req {
+ u8 command[3];
+ struct spi_message msg;
+ struct spi_transfer xfer[2];
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ u8 sample[3] ____cacheline_aligned;
+};
+
+static int ads7846_read12_ser(struct device *dev, unsigned command)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct ads7846 *ts = dev_get_drvdata(dev);
+ struct ser_req *req;
+ int status;
+
+ req = kzalloc(sizeof *req, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ spi_message_init(&req->msg);
+
+ /* maybe turn on internal vREF, and let it settle */
+ if (ts->use_internal) {
+ req->ref_on = REF_ON;
+ req->xfer[0].tx_buf = &req->ref_on;
+ req->xfer[0].len = 1;
+ spi_message_add_tail(&req->xfer[0], &req->msg);
+
+ req->xfer[1].rx_buf = &req->scratch;
+ req->xfer[1].len = 2;
+
+ /* for 1uF, settle for 800 usec; no cap, 100 usec. */
+ req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+ spi_message_add_tail(&req->xfer[1], &req->msg);
+
+ /* Enable reference voltage */
+ command |= ADS_PD10_REF_ON;
+ }
+
+ /* Enable ADC in every case */
+ command |= ADS_PD10_ADC_ON;
+
+ /* take sample */
+ req->command = (u8) command;
+ req->xfer[2].tx_buf = &req->command;
+ req->xfer[2].len = 1;
+ spi_message_add_tail(&req->xfer[2], &req->msg);
+
+ req->xfer[3].rx_buf = &req->sample;
+ req->xfer[3].len = 2;
+ spi_message_add_tail(&req->xfer[3], &req->msg);
+
+ /* REVISIT: take a few more samples, and compare ... */
+
+ /* converter in low power mode & enable PENIRQ */
+ req->ref_off = PWRDOWN;
+ req->xfer[4].tx_buf = &req->ref_off;
+ req->xfer[4].len = 1;
+ spi_message_add_tail(&req->xfer[4], &req->msg);
+
+ req->xfer[5].rx_buf = &req->scratch;
+ req->xfer[5].len = 2;
+ CS_CHANGE(req->xfer[5]);
+ spi_message_add_tail(&req->xfer[5], &req->msg);
+
+ mutex_lock(&ts->lock);
+ ads7846_stop(ts);
+ status = spi_sync(spi, &req->msg);
+ ads7846_restart(ts);
+ mutex_unlock(&ts->lock);
+
+ if (status == 0) {
+ /* on-wire is a must-ignore bit, a BE12 value, then padding */
+ status = be16_to_cpu(req->sample);
+ status = status >> 3;
+ status &= 0x0fff;
+ }
+
+ kfree(req);
+ return status;
+}
+
+static int ads7845_read12_ser(struct device *dev, unsigned command)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct ads7846 *ts = dev_get_drvdata(dev);
+ struct ads7845_ser_req *req;
+ int status;
+
+ req = kzalloc(sizeof *req, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ spi_message_init(&req->msg);
+
+ req->command[0] = (u8) command;
+ req->xfer[0].tx_buf = req->command;
+ req->xfer[0].rx_buf = req->sample;
+ req->xfer[0].len = 3;
+ spi_message_add_tail(&req->xfer[0], &req->msg);
+
+ mutex_lock(&ts->lock);
+ ads7846_stop(ts);
+ status = spi_sync(spi, &req->msg);
+ ads7846_restart(ts);
+ mutex_unlock(&ts->lock);
+
+ if (status == 0) {
+ /* BE12 value, then padding */
+ status = be16_to_cpu(*((u16 *)&req->sample[1]));
+ status = status >> 3;
+ status &= 0x0fff;
+ }
+
+ kfree(req);
+ return status;
+}
+
+#if IS_ENABLED(CONFIG_HWMON)
+
+#define SHOW(name, var, adjust) static ssize_t \
+name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct ads7846 *ts = dev_get_drvdata(dev); \
+ ssize_t v = ads7846_read12_ser(&ts->spi->dev, \
+ READ_12BIT_SER(var)); \
+ if (v < 0) \
+ return v; \
+ return sprintf(buf, "%u\n", adjust(ts, v)); \
+} \
+static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);
+
+
+/* Sysfs conventions report temperatures in millidegrees Celsius.
+ * ADS7846 could use the low-accuracy two-sample scheme, but can't do the high
+ * accuracy scheme without calibration data. For now we won't try either;
+ * userspace sees raw sensor values, and must scale/calibrate appropriately.
+ */
+static inline unsigned null_adjust(struct ads7846 *ts, ssize_t v)
+{
+ return v;
+}
+
+SHOW(temp0, temp0, null_adjust) /* temp1_input */
+SHOW(temp1, temp1, null_adjust) /* temp2_input */
+
+
+/* sysfs conventions report voltages in millivolts. We can convert voltages
+ * if we know vREF. userspace may need to scale vAUX to match the board's
+ * external resistors; we assume that vBATT only uses the internal ones.
+ */
+static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v)
+{
+ unsigned retval = v;
+
+ /* external resistors may scale vAUX into 0..vREF */
+ retval *= ts->vref_mv;
+ retval = retval >> 12;
+
+ return retval;
+}
+
+static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v)
+{
+ unsigned retval = vaux_adjust(ts, v);
+
+ /* ads7846 has a resistor ladder to scale this signal down */
+ if (ts->model == 7846)
+ retval *= 4;
+
+ return retval;
+}
+
+SHOW(in0_input, vaux, vaux_adjust)
+SHOW(in1_input, vbatt, vbatt_adjust)
+
+static umode_t ads7846_is_visible(struct kobject *kobj, struct attribute *attr,
+ int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct ads7846 *ts = dev_get_drvdata(dev);
+
+ if (ts->model == 7843 && index < 2) /* in0, in1 */
+ return 0;
+ if (ts->model == 7845 && index != 2) /* in0 */
+ return 0;
+
+ return attr->mode;
+}
+
+static struct attribute *ads7846_attributes[] = {
+ &dev_attr_temp0.attr, /* 0 */
+ &dev_attr_temp1.attr, /* 1 */
+ &dev_attr_in0_input.attr, /* 2 */
+ &dev_attr_in1_input.attr, /* 3 */
+ NULL,
+};
+
+static struct attribute_group ads7846_attr_group = {
+ .attrs = ads7846_attributes,
+ .is_visible = ads7846_is_visible,
+};
+__ATTRIBUTE_GROUPS(ads7846_attr);
+
+static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
+{
+ /* hwmon sensors need a reference voltage */
+ switch (ts->model) {
+ case 7846:
+ if (!ts->vref_mv) {
+ dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n");
+ ts->vref_mv = 2500;
+ ts->use_internal = true;
+ }
+ break;
+ case 7845:
+ case 7843:
+ if (!ts->vref_mv) {
+ dev_warn(&spi->dev,
+ "external vREF for ADS%d not specified\n",
+ ts->model);
+ return 0;
+ }
+ break;
+ }
+
+ ts->hwmon = hwmon_device_register_with_groups(&spi->dev, spi->modalias,
+ ts, ads7846_attr_groups);
+ if (IS_ERR(ts->hwmon))
+ return PTR_ERR(ts->hwmon);
+
+ return 0;
+}
+
+static void ads784x_hwmon_unregister(struct spi_device *spi,
+ struct ads7846 *ts)
+{
+ if (ts->hwmon)
+ hwmon_device_unregister(ts->hwmon);
+}
+
+#else
+static inline int ads784x_hwmon_register(struct spi_device *spi,
+ struct ads7846 *ts)
+{
+ return 0;
+}
+
+static inline void ads784x_hwmon_unregister(struct spi_device *spi,
+ struct ads7846 *ts)
+{
+}
+#endif
+
+static ssize_t ads7846_pen_down_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ads7846 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->pendown);
+}
+
+static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL);
+
+static ssize_t ads7846_disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ads7846 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t ads7846_disable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ads7846 *ts = dev_get_drvdata(dev);
+ unsigned int i;
+ int err;
+
+ err = kstrtouint(buf, 10, &i);
+ if (err)
+ return err;
+
+ if (i)
+ ads7846_disable(ts);
+ else
+ ads7846_enable(ts);
+
+ return count;
+}
+
+static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store);
+
+static struct attribute *ads784x_attributes[] = {
+ &dev_attr_pen_down.attr,
+ &dev_attr_disable.attr,
+ NULL,
+};
+
+static struct attribute_group ads784x_attr_group = {
+ .attrs = ads784x_attributes,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int get_pendown_state(struct ads7846 *ts)
+{
+ if (ts->get_pendown_state)
+ return ts->get_pendown_state();
+
+ return !gpio_get_value(ts->gpio_pendown);
+}
+
+static void null_wait_for_sync(void)
+{
+}
+
+static int ads7846_debounce_filter(void *ads, int data_idx, int *val)
+{
+ struct ads7846 *ts = ads;
+
+ if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) {
+ /* Start over collecting consistent readings. */
+ ts->read_rep = 0;
+ /*
+ * Repeat it, if this was the first read or the read
+ * wasn't consistent enough.
+ */
+ if (ts->read_cnt < ts->debounce_max) {
+ ts->last_read = *val;
+ ts->read_cnt++;
+ return ADS7846_FILTER_REPEAT;
+ } else {
+ /*
+ * Maximum number of debouncing reached and still
+ * not enough number of consistent readings. Abort
+ * the whole sample, repeat it in the next sampling
+ * period.
+ */
+ ts->read_cnt = 0;
+ return ADS7846_FILTER_IGNORE;
+ }
+ } else {
+ if (++ts->read_rep > ts->debounce_rep) {
+ /*
+ * Got a good reading for this coordinate,
+ * go for the next one.
+ */
+ ts->read_cnt = 0;
+ ts->read_rep = 0;
+ return ADS7846_FILTER_OK;
+ } else {
+ /* Read more values that are consistent. */
+ ts->read_cnt++;
+ return ADS7846_FILTER_REPEAT;
+ }
+ }
+}
+
+static int ads7846_no_filter(void *ads, int data_idx, int *val)
+{
+ return ADS7846_FILTER_OK;
+}
+
+static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m)
+{
+ struct spi_transfer *t =
+ list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
+
+ if (ts->model == 7845) {
+ return be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3;
+ } else {
+ /*
+ * adjust: on-wire is a must-ignore bit, a BE12 value, then
+ * padding; built from two 8 bit values written msb-first.
+ */
+ return be16_to_cpup((__be16 *)t->rx_buf) >> 3;
+ }
+}
+
+static void ads7846_update_value(struct spi_message *m, int val)
+{
+ struct spi_transfer *t =
+ list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
+
+ *(u16 *)t->rx_buf = val;
+}
+
+static void ads7846_read_state(struct ads7846 *ts)
+{
+ struct ads7846_packet *packet = ts->packet;
+ struct spi_message *m;
+ int msg_idx = 0;
+ int val;
+ int action;
+ int error;
+
+ while (msg_idx < ts->msg_count) {
+
+ ts->wait_for_sync();
+
+ m = &ts->msg[msg_idx];
+ error = spi_sync(ts->spi, m);
+ if (error) {
+ dev_err(&ts->spi->dev, "spi_sync --> %d\n", error);
+ packet->tc.ignore = true;
+ return;
+ }
+
+ /*
+ * Last message is power down request, no need to convert
+ * or filter the value.
+ */
+ if (msg_idx < ts->msg_count - 1) {
+
+ val = ads7846_get_value(ts, m);
+
+ action = ts->filter(ts->filter_data, msg_idx, &val);
+ switch (action) {
+ case ADS7846_FILTER_REPEAT:
+ continue;
+
+ case ADS7846_FILTER_IGNORE:
+ packet->tc.ignore = true;
+ msg_idx = ts->msg_count - 1;
+ continue;
+
+ case ADS7846_FILTER_OK:
+ ads7846_update_value(m, val);
+ packet->tc.ignore = false;
+ msg_idx++;
+ break;
+
+ default:
+ BUG();
+ }
+ } else {
+ msg_idx++;
+ }
+ }
+}
+
+static void ads7846_report_state(struct ads7846 *ts)
+{
+ struct ads7846_packet *packet = ts->packet;
+ unsigned int Rt;
+ u16 x, y, z1, z2;
+
+ /*
+ * ads7846_get_value() does in-place conversion (including byte swap)
+ * from on-the-wire format as part of debouncing to get stable
+ * readings.
+ */
+ if (ts->model == 7845) {
+ x = *(u16 *)packet->tc.x_buf;
+ y = *(u16 *)packet->tc.y_buf;
+ z1 = 0;
+ z2 = 0;
+ } else {
+ x = packet->tc.x;
+ y = packet->tc.y;
+ z1 = packet->tc.z1;
+ z2 = packet->tc.z2;
+ }
+
+ /* range filtering */
+ if (x == MAX_12BIT)
+ x = 0;
+
+ if (ts->model == 7843) {
+ Rt = ts->pressure_max / 2;
+ } else if (ts->model == 7845) {
+ if (get_pendown_state(ts))
+ Rt = ts->pressure_max / 2;
+ else
+ Rt = 0;
+ dev_vdbg(&ts->spi->dev, "x/y: %d/%d, PD %d\n", x, y, Rt);
+ } else if (likely(x && z1)) {
+ /* compute touch pressure resistance using equation #2 */
+ Rt = z2;
+ Rt -= z1;
+ Rt *= x;
+ Rt *= ts->x_plate_ohms;
+ Rt /= z1;
+ Rt = (Rt + 2047) >> 12;
+ } else {
+ Rt = 0;
+ }
+
+ /*
+ * Sample found inconsistent by debouncing or pressure is beyond
+ * the maximum. Don't report it to user space, repeat at least
+ * once more the measurement
+ */
+ if (packet->tc.ignore || Rt > ts->pressure_max) {
+ dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n",
+ packet->tc.ignore, Rt);
+ return;
+ }
+
+ /*
+ * Maybe check the pendown state before reporting. This discards
+ * false readings when the pen is lifted.
+ */
+ if (ts->penirq_recheck_delay_usecs) {
+ udelay(ts->penirq_recheck_delay_usecs);
+ if (!get_pendown_state(ts))
+ Rt = 0;
+ }
+
+ /*
+ * NOTE: We can't rely on the pressure to determine the pen down
+ * state, even this controller has a pressure sensor. The pressure
+ * value can fluctuate for quite a while after lifting the pen and
+ * in some cases may not even settle at the expected value.
+ *
+ * The only safe way to check for the pen up condition is in the
+ * timer by reading the pen signal state (it's a GPIO _and_ IRQ).
+ */
+ if (Rt) {
+ struct input_dev *input = ts->input;
+
+ if (ts->swap_xy)
+ swap(x, y);
+
+ if (!ts->pendown) {
+ input_report_key(input, BTN_TOUCH, 1);
+ ts->pendown = true;
+ dev_vdbg(&ts->spi->dev, "DOWN\n");
+ }
+
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt);
+
+ input_sync(input);
+ dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
+ }
+}
+
+static irqreturn_t ads7846_hard_irq(int irq, void *handle)
+{
+ struct ads7846 *ts = handle;
+
+ return get_pendown_state(ts) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+}
+
+
+static irqreturn_t ads7846_irq(int irq, void *handle)
+{
+ struct ads7846 *ts = handle;
+
+ /* Start with a small delay before checking pendown state */
+ msleep(TS_POLL_DELAY);
+
+ while (!ts->stopped && get_pendown_state(ts)) {
+
+ /* pen is down, continue with the measurement */
+ ads7846_read_state(ts);
+
+ if (!ts->stopped)
+ ads7846_report_state(ts);
+
+ wait_event_timeout(ts->wait, ts->stopped,
+ msecs_to_jiffies(TS_POLL_PERIOD));
+ }
+
+ if (ts->pendown) {
+ struct input_dev *input = ts->input;
+
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_sync(input);
+
+ ts->pendown = false;
+ dev_vdbg(&ts->spi->dev, "UP\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __maybe_unused ads7846_suspend(struct device *dev)
+{
+ struct ads7846 *ts = dev_get_drvdata(dev);
+
+ mutex_lock(&ts->lock);
+
+ if (!ts->suspended) {
+
+ if (!ts->disabled)
+ __ads7846_disable(ts);
+
+ if (device_may_wakeup(&ts->spi->dev))
+ enable_irq_wake(ts->spi->irq);
+
+ ts->suspended = true;
+ }
+
+ mutex_unlock(&ts->lock);
+
+ return 0;
+}
+
+static int __maybe_unused ads7846_resume(struct device *dev)
+{
+ struct ads7846 *ts = dev_get_drvdata(dev);
+
+ mutex_lock(&ts->lock);
+
+ if (ts->suspended) {
+
+ ts->suspended = false;
+
+ if (device_may_wakeup(&ts->spi->dev))
+ disable_irq_wake(ts->spi->irq);
+
+ if (!ts->disabled)
+ __ads7846_enable(ts);
+ }
+
+ mutex_unlock(&ts->lock);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ads7846_pm, ads7846_suspend, ads7846_resume);
+
+static int ads7846_setup_pendown(struct spi_device *spi,
+ struct ads7846 *ts,
+ const struct ads7846_platform_data *pdata)
+{
+ int err;
+
+ /*
+ * REVISIT when the irq can be triggered active-low, or if for some
+ * reason the touchscreen isn't hooked up, we don't need to access
+ * the pendown state.
+ */
+
+ if (pdata->get_pendown_state) {
+ ts->get_pendown_state = pdata->get_pendown_state;
+ } else if (gpio_is_valid(pdata->gpio_pendown)) {
+
+ err = gpio_request_one(pdata->gpio_pendown, GPIOF_IN,
+ "ads7846_pendown");
+ if (err) {
+ dev_err(&spi->dev,
+ "failed to request/setup pendown GPIO%d: %d\n",
+ pdata->gpio_pendown, err);
+ return err;
+ }
+
+ ts->gpio_pendown = pdata->gpio_pendown;
+
+ if (pdata->gpio_pendown_debounce)
+ gpio_set_debounce(pdata->gpio_pendown,
+ pdata->gpio_pendown_debounce);
+ } else {
+ dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Set up the transfers to read touchscreen state; this assumes we
+ * use formula #2 for pressure, not #3.
+ */
+static void ads7846_setup_spi_msg(struct ads7846 *ts,
+ const struct ads7846_platform_data *pdata)
+{
+ struct spi_message *m = &ts->msg[0];
+ struct spi_transfer *x = ts->xfer;
+ struct ads7846_packet *packet = ts->packet;
+ int vref = pdata->keep_vref_on;
+
+ if (ts->model == 7873) {
+ /*
+ * The AD7873 is almost identical to the ADS7846
+ * keep VREF off during differential/ratiometric
+ * conversion modes.
+ */
+ ts->model = 7846;
+ vref = 0;
+ }
+
+ ts->msg_count = 1;
+ spi_message_init(m);
+ m->context = ts;
+
+ if (ts->model == 7845) {
+ packet->read_y_cmd[0] = READ_Y(vref);
+ packet->read_y_cmd[1] = 0;
+ packet->read_y_cmd[2] = 0;
+ x->tx_buf = &packet->read_y_cmd[0];
+ x->rx_buf = &packet->tc.y_buf[0];
+ x->len = 3;
+ spi_message_add_tail(x, m);
+ } else {
+ /* y- still on; turn on only y+ (and ADC) */
+ packet->read_y = READ_Y(vref);
+ x->tx_buf = &packet->read_y;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &packet->tc.y;
+ x->len = 2;
+ spi_message_add_tail(x, m);
+ }
+
+ /*
+ * The first sample after switching drivers can be low quality;
+ * optionally discard it, using a second one after the signals
+ * have had enough time to stabilize.
+ */
+ if (pdata->settle_delay_usecs) {
+ x->delay_usecs = pdata->settle_delay_usecs;
+
+ x++;
+ x->tx_buf = &packet->read_y;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &packet->tc.y;
+ x->len = 2;
+ spi_message_add_tail(x, m);
+ }
+
+ ts->msg_count++;
+ m++;
+ spi_message_init(m);
+ m->context = ts;
+
+ if (ts->model == 7845) {
+ x++;
+ packet->read_x_cmd[0] = READ_X(vref);
+ packet->read_x_cmd[1] = 0;
+ packet->read_x_cmd[2] = 0;
+ x->tx_buf = &packet->read_x_cmd[0];
+ x->rx_buf = &packet->tc.x_buf[0];
+ x->len = 3;
+ spi_message_add_tail(x, m);
+ } else {
+ /* turn y- off, x+ on, then leave in lowpower */
+ x++;
+ packet->read_x = READ_X(vref);
+ x->tx_buf = &packet->read_x;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &packet->tc.x;
+ x->len = 2;
+ spi_message_add_tail(x, m);
+ }
+
+ /* ... maybe discard first sample ... */
+ if (pdata->settle_delay_usecs) {
+ x->delay_usecs = pdata->settle_delay_usecs;
+
+ x++;
+ x->tx_buf = &packet->read_x;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &packet->tc.x;
+ x->len = 2;
+ spi_message_add_tail(x, m);
+ }
+
+ /* turn y+ off, x- on; we'll use formula #2 */
+ if (ts->model == 7846) {
+ ts->msg_count++;
+ m++;
+ spi_message_init(m);
+ m->context = ts;
+
+ x++;
+ packet->read_z1 = READ_Z1(vref);
+ x->tx_buf = &packet->read_z1;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &packet->tc.z1;
+ x->len = 2;
+ spi_message_add_tail(x, m);
+
+ /* ... maybe discard first sample ... */
+ if (pdata->settle_delay_usecs) {
+ x->delay_usecs = pdata->settle_delay_usecs;
+
+ x++;
+ x->tx_buf = &packet->read_z1;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &packet->tc.z1;
+ x->len = 2;
+ spi_message_add_tail(x, m);
+ }
+
+ ts->msg_count++;
+ m++;
+ spi_message_init(m);
+ m->context = ts;
+
+ x++;
+ packet->read_z2 = READ_Z2(vref);
+ x->tx_buf = &packet->read_z2;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &packet->tc.z2;
+ x->len = 2;
+ spi_message_add_tail(x, m);
+
+ /* ... maybe discard first sample ... */
+ if (pdata->settle_delay_usecs) {
+ x->delay_usecs = pdata->settle_delay_usecs;
+
+ x++;
+ x->tx_buf = &packet->read_z2;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &packet->tc.z2;
+ x->len = 2;
+ spi_message_add_tail(x, m);
+ }
+ }
+
+ /* power down */
+ ts->msg_count++;
+ m++;
+ spi_message_init(m);
+ m->context = ts;
+
+ if (ts->model == 7845) {
+ x++;
+ packet->pwrdown_cmd[0] = PWRDOWN;
+ packet->pwrdown_cmd[1] = 0;
+ packet->pwrdown_cmd[2] = 0;
+ x->tx_buf = &packet->pwrdown_cmd[0];
+ x->len = 3;
+ } else {
+ x++;
+ packet->pwrdown = PWRDOWN;
+ x->tx_buf = &packet->pwrdown;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &packet->dummy;
+ x->len = 2;
+ }
+
+ CS_CHANGE(*x);
+ spi_message_add_tail(x, m);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ads7846_dt_ids[] = {
+ { .compatible = "ti,tsc2046", .data = (void *) 7846 },
+ { .compatible = "ti,ads7843", .data = (void *) 7843 },
+ { .compatible = "ti,ads7845", .data = (void *) 7845 },
+ { .compatible = "ti,ads7846", .data = (void *) 7846 },
+ { .compatible = "ti,ads7873", .data = (void *) 7873 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ads7846_dt_ids);
+
+static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev)
+{
+ struct ads7846_platform_data *pdata;
+ struct device_node *node = dev->of_node;
+ const struct of_device_id *match;
+
+ if (!node) {
+ dev_err(dev, "Device does not have associated DT data\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ match = of_match_device(ads7846_dt_ids, dev);
+ if (!match) {
+ dev_err(dev, "Unknown device model\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->model = (unsigned long)match->data;
+
+ of_property_read_u16(node, "ti,vref-delay-usecs",
+ &pdata->vref_delay_usecs);
+ of_property_read_u16(node, "ti,vref-mv", &pdata->vref_mv);
+ pdata->keep_vref_on = of_property_read_bool(node, "ti,keep-vref-on");
+
+ pdata->swap_xy = of_property_read_bool(node, "ti,swap-xy");
+
+ of_property_read_u16(node, "ti,settle-delay-usec",
+ &pdata->settle_delay_usecs);
+ of_property_read_u16(node, "ti,penirq-recheck-delay-usecs",
+ &pdata->penirq_recheck_delay_usecs);
+
+ of_property_read_u16(node, "ti,x-plate-ohms", &pdata->x_plate_ohms);
+ of_property_read_u16(node, "ti,y-plate-ohms", &pdata->y_plate_ohms);
+
+ of_property_read_u16(node, "ti,x-min", &pdata->x_min);
+ of_property_read_u16(node, "ti,y-min", &pdata->y_min);
+ of_property_read_u16(node, "ti,x-max", &pdata->x_max);
+ of_property_read_u16(node, "ti,y-max", &pdata->y_max);
+
+ of_property_read_u16(node, "ti,pressure-min", &pdata->pressure_min);
+ of_property_read_u16(node, "ti,pressure-max", &pdata->pressure_max);
+
+ of_property_read_u16(node, "ti,debounce-max", &pdata->debounce_max);
+ of_property_read_u16(node, "ti,debounce-tol", &pdata->debounce_tol);
+ of_property_read_u16(node, "ti,debounce-rep", &pdata->debounce_rep);
+
+ of_property_read_u32(node, "ti,pendown-gpio-debounce",
+ &pdata->gpio_pendown_debounce);
+
+ pdata->wakeup = of_property_read_bool(node, "linux,wakeup");
+
+ pdata->gpio_pendown = of_get_named_gpio(dev->of_node, "pendown-gpio", 0);
+
+ return pdata;
+}
+#else
+static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev)
+{
+ dev_err(dev, "no platform data defined\n");
+ return ERR_PTR(-EINVAL);
+}
+#endif
+
+static int ads7846_probe(struct spi_device *spi)
+{
+ const struct ads7846_platform_data *pdata;
+ struct ads7846 *ts;
+ struct ads7846_packet *packet;
+ struct input_dev *input_dev;
+ unsigned long irq_flags;
+ int err;
+
+ if (!spi->irq) {
+ dev_dbg(&spi->dev, "no IRQ?\n");
+ return -EINVAL;
+ }
+
+ /* don't exceed max specified sample rate */
+ if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) {
+ dev_err(&spi->dev, "f(sample) %d KHz?\n",
+ (spi->max_speed_hz/SAMPLE_BITS)/1000);
+ return -EINVAL;
+ }
+
+ /*
+ * We'd set TX word size 8 bits and RX word size to 13 bits ... except
+ * that even if the hardware can do that, the SPI controller driver
+ * may not. So we stick to very-portable 8 bit words, both RX and TX.
+ */
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_0;
+ err = spi_setup(spi);
+ if (err < 0)
+ return err;
+
+ ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL);
+ packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !packet || !input_dev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ spi_set_drvdata(spi, ts);
+
+ ts->packet = packet;
+ ts->spi = spi;
+ ts->input = input_dev;
+
+ mutex_init(&ts->lock);
+ init_waitqueue_head(&ts->wait);
+
+ pdata = dev_get_platdata(&spi->dev);
+ if (!pdata) {
+ pdata = ads7846_probe_dt(&spi->dev);
+ if (IS_ERR(pdata)) {
+ err = PTR_ERR(pdata);
+ goto err_free_mem;
+ }
+ }
+
+ ts->model = pdata->model ? : 7846;
+ ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
+ ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+ ts->pressure_max = pdata->pressure_max ? : ~0;
+
+ ts->vref_mv = pdata->vref_mv;
+ ts->swap_xy = pdata->swap_xy;
+
+ if (pdata->filter != NULL) {
+ if (pdata->filter_init != NULL) {
+ err = pdata->filter_init(pdata, &ts->filter_data);
+ if (err < 0)
+ goto err_free_mem;
+ }
+ ts->filter = pdata->filter;
+ ts->filter_cleanup = pdata->filter_cleanup;
+ } else if (pdata->debounce_max) {
+ ts->debounce_max = pdata->debounce_max;
+ if (ts->debounce_max < 2)
+ ts->debounce_max = 2;
+ ts->debounce_tol = pdata->debounce_tol;
+ ts->debounce_rep = pdata->debounce_rep;
+ ts->filter = ads7846_debounce_filter;
+ ts->filter_data = ts;
+ } else {
+ ts->filter = ads7846_no_filter;
+ }
+
+ err = ads7846_setup_pendown(spi, ts, pdata);
+ if (err)
+ goto err_cleanup_filter;
+
+ if (pdata->penirq_recheck_delay_usecs)
+ ts->penirq_recheck_delay_usecs =
+ pdata->penirq_recheck_delay_usecs;
+
+ ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync;
+
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
+ snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model);
+
+ input_dev->name = ts->name;
+ input_dev->phys = ts->phys;
+ input_dev->dev.parent = &spi->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(input_dev, ABS_X,
+ pdata->x_min ? : 0,
+ pdata->x_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ pdata->y_min ? : 0,
+ pdata->y_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ pdata->pressure_min, pdata->pressure_max, 0, 0);
+
+ ads7846_setup_spi_msg(ts, pdata);
+
+ ts->reg = regulator_get(&spi->dev, "vcc");
+ if (IS_ERR(ts->reg)) {
+ err = PTR_ERR(ts->reg);
+ dev_err(&spi->dev, "unable to get regulator: %d\n", err);
+ goto err_free_gpio;
+ }
+
+ err = regulator_enable(ts->reg);
+ if (err) {
+ dev_err(&spi->dev, "unable to enable regulator: %d\n", err);
+ goto err_put_regulator;
+ }
+
+ irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING;
+ irq_flags |= IRQF_ONESHOT;
+
+ err = request_threaded_irq(spi->irq, ads7846_hard_irq, ads7846_irq,
+ irq_flags, spi->dev.driver->name, ts);
+ if (err && !pdata->irq_flags) {
+ dev_info(&spi->dev,
+ "trying pin change workaround on irq %d\n", spi->irq);
+ irq_flags |= IRQF_TRIGGER_RISING;
+ err = request_threaded_irq(spi->irq,
+ ads7846_hard_irq, ads7846_irq,
+ irq_flags, spi->dev.driver->name, ts);
+ }
+
+ if (err) {
+ dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
+ goto err_disable_regulator;
+ }
+
+ err = ads784x_hwmon_register(spi, ts);
+ if (err)
+ goto err_free_irq;
+
+ dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq);
+
+ /*
+ * Take a first sample, leaving nPENIRQ active and vREF off; avoid
+ * the touchscreen, in case it's not connected.
+ */
+ if (ts->model == 7845)
+ ads7845_read12_ser(&spi->dev, PWRDOWN);
+ else
+ (void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux));
+
+ err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);
+ if (err)
+ goto err_remove_hwmon;
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_remove_attr_group;
+
+ device_init_wakeup(&spi->dev, pdata->wakeup);
+
+ /*
+ * If device does not carry platform data we must have allocated it
+ * when parsing DT data.
+ */
+ if (!dev_get_platdata(&spi->dev))
+ devm_kfree(&spi->dev, (void *)pdata);
+
+ return 0;
+
+ err_remove_attr_group:
+ sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
+ err_remove_hwmon:
+ ads784x_hwmon_unregister(spi, ts);
+ err_free_irq:
+ free_irq(spi->irq, ts);
+ err_disable_regulator:
+ regulator_disable(ts->reg);
+ err_put_regulator:
+ regulator_put(ts->reg);
+ err_free_gpio:
+ if (!ts->get_pendown_state)
+ gpio_free(ts->gpio_pendown);
+ err_cleanup_filter:
+ if (ts->filter_cleanup)
+ ts->filter_cleanup(ts->filter_data);
+ err_free_mem:
+ input_free_device(input_dev);
+ kfree(packet);
+ kfree(ts);
+ return err;
+}
+
+static int ads7846_remove(struct spi_device *spi)
+{
+ struct ads7846 *ts = spi_get_drvdata(spi);
+
+ device_init_wakeup(&spi->dev, false);
+
+ sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
+
+ ads7846_disable(ts);
+ free_irq(ts->spi->irq, ts);
+
+ input_unregister_device(ts->input);
+
+ ads784x_hwmon_unregister(spi, ts);
+
+ regulator_disable(ts->reg);
+ regulator_put(ts->reg);
+
+ if (!ts->get_pendown_state) {
+ /*
+ * If we are not using specialized pendown method we must
+ * have been relying on gpio we set up ourselves.
+ */
+ gpio_free(ts->gpio_pendown);
+ }
+
+ if (ts->filter_cleanup)
+ ts->filter_cleanup(ts->filter_data);
+
+ kfree(ts->packet);
+ kfree(ts);
+
+ dev_dbg(&spi->dev, "unregistered touchscreen\n");
+
+ return 0;
+}
+
+static struct spi_driver ads7846_driver = {
+ .driver = {
+ .name = "ads7846",
+ .owner = THIS_MODULE,
+ .pm = &ads7846_pm,
+ .of_match_table = of_match_ptr(ads7846_dt_ids),
+ },
+ .probe = ads7846_probe,
+ .remove = ads7846_remove,
+};
+
+module_spi_driver(ads7846_driver);
+
+MODULE_DESCRIPTION("ADS7846 TouchScreen Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:ads7846");
diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c
new file mode 100644
index 000000000..f0b954d46
--- /dev/null
+++ b/drivers/input/touchscreen/ar1021_i2c.c
@@ -0,0 +1,181 @@
+/*
+ * Microchip AR1021 driver for I2C
+ *
+ * Author: Christian Gmeiner <christian.gmeiner@gmail.com>
+ *
+ * License: GPLv2 as published by the FSF.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#define AR1021_TOCUH_PKG_SIZE 5
+
+#define AR1021_MAX_X 4095
+#define AR1021_MAX_Y 4095
+
+struct ar1021_i2c {
+ struct i2c_client *client;
+ struct input_dev *input;
+ u8 data[AR1021_TOCUH_PKG_SIZE];
+};
+
+static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id)
+{
+ struct ar1021_i2c *ar1021 = dev_id;
+ struct input_dev *input = ar1021->input;
+ u8 *data = ar1021->data;
+ unsigned int x, y, button;
+ int retval;
+
+ retval = i2c_master_recv(ar1021->client,
+ ar1021->data, sizeof(ar1021->data));
+ if (retval != sizeof(ar1021->data))
+ goto out;
+
+ /* sync bit set ? */
+ if ((data[0] & 0x80) == 0)
+ goto out;
+
+ button = data[0] & BIT(0);
+ x = ((data[2] & 0x1f) << 7) | (data[1] & 0x7f);
+ y = ((data[4] & 0x1f) << 7) | (data[3] & 0x7f);
+
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ input_report_key(input, BTN_TOUCH, button);
+ input_sync(input);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int ar1021_i2c_open(struct input_dev *dev)
+{
+ struct ar1021_i2c *ar1021 = input_get_drvdata(dev);
+ struct i2c_client *client = ar1021->client;
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static void ar1021_i2c_close(struct input_dev *dev)
+{
+ struct ar1021_i2c *ar1021 = input_get_drvdata(dev);
+ struct i2c_client *client = ar1021->client;
+
+ disable_irq(client->irq);
+}
+
+static int ar1021_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ar1021_i2c *ar1021;
+ struct input_dev *input;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "i2c_check_functionality error\n");
+ return -ENXIO;
+ }
+
+ ar1021 = devm_kzalloc(&client->dev, sizeof(*ar1021), GFP_KERNEL);
+ if (!ar1021)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(&client->dev);
+ if (!input)
+ return -ENOMEM;
+
+ ar1021->client = client;
+ ar1021->input = input;
+
+ input->name = "ar1021 I2C Touchscreen";
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &client->dev;
+ input->open = ar1021_i2c_open;
+ input->close = ar1021_i2c_close;
+
+ input_set_capability(input, EV_KEY, BTN_TOUCH);
+ input_set_abs_params(input, ABS_X, 0, AR1021_MAX_X, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, AR1021_MAX_Y, 0, 0);
+
+ input_set_drvdata(input, ar1021);
+
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, ar1021_i2c_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "ar1021_i2c", ar1021);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to enable IRQ, error: %d\n", error);
+ return error;
+ }
+
+ /* Disable the IRQ, we'll enable it in ar1021_i2c_open() */
+ disable_irq(client->irq);
+
+ error = input_register_device(ar1021->input);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to register input device, error: %d\n", error);
+ return error;
+ }
+
+ i2c_set_clientdata(client, ar1021);
+ return 0;
+}
+
+static int __maybe_unused ar1021_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ disable_irq(client->irq);
+
+ return 0;
+}
+
+static int __maybe_unused ar1021_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ar1021_i2c_pm, ar1021_i2c_suspend, ar1021_i2c_resume);
+
+static const struct i2c_device_id ar1021_i2c_id[] = {
+ { "MICROCHIP_AR1021_I2C", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id);
+
+static const struct of_device_id ar1021_i2c_of_match[] = {
+ { .compatible = "microchip,ar1021-i2c", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ar1021_i2c_of_match);
+
+static struct i2c_driver ar1021_i2c_driver = {
+ .driver = {
+ .name = "ar1021_i2c",
+ .owner = THIS_MODULE,
+ .pm = &ar1021_i2c_pm,
+ .of_match_table = ar1021_i2c_of_match,
+ },
+
+ .probe = ar1021_i2c_probe,
+ .id_table = ar1021_i2c_id,
+};
+module_i2c_driver(ar1021_i2c_driver);
+
+MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>");
+MODULE_DESCRIPTION("Microchip AR1021 I2C Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c
new file mode 100644
index 000000000..7ec0421c0
--- /dev/null
+++ b/drivers/input/touchscreen/atmel-wm97xx.c
@@ -0,0 +1,436 @@
+/*
+ * Atmel AT91 and AVR32 continuous touch screen driver for Wolfson WM97xx AC97
+ * codecs.
+ *
+ * Copyright (C) 2008 - 2009 Atmel Corporation
+ *
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/wm97xx.h>
+#include <linux/timer.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#define AC97C_ICA 0x10
+#define AC97C_CBRHR 0x30
+#define AC97C_CBSR 0x38
+#define AC97C_CBMR 0x3c
+#define AC97C_IER 0x54
+#define AC97C_IDR 0x58
+
+#define AC97C_RXRDY (1 << 4)
+#define AC97C_OVRUN (1 << 5)
+
+#define AC97C_CMR_SIZE_20 (0 << 16)
+#define AC97C_CMR_SIZE_18 (1 << 16)
+#define AC97C_CMR_SIZE_16 (2 << 16)
+#define AC97C_CMR_SIZE_10 (3 << 16)
+#define AC97C_CMR_CEM_LITTLE (1 << 18)
+#define AC97C_CMR_CEM_BIG (0 << 18)
+#define AC97C_CMR_CENA (1 << 21)
+
+#define AC97C_INT_CBEVT (1 << 4)
+
+#define AC97C_SR_CAEVT (1 << 3)
+
+#define AC97C_CH_MASK(slot) \
+ (0x7 << (3 * (slot - 3)))
+#define AC97C_CH_ASSIGN(slot, channel) \
+ (AC97C_CHANNEL_##channel << (3 * (slot - 3)))
+#define AC97C_CHANNEL_NONE 0x0
+#define AC97C_CHANNEL_B 0x2
+
+#define ac97c_writel(chip, reg, val) \
+ __raw_writel((val), (chip)->regs + AC97C_##reg)
+#define ac97c_readl(chip, reg) \
+ __raw_readl((chip)->regs + AC97C_##reg)
+
+#ifdef CONFIG_CPU_AT32AP700X
+#define ATMEL_WM97XX_AC97C_IOMEM (0xfff02800)
+#define ATMEL_WM97XX_AC97C_IRQ (29)
+#define ATMEL_WM97XX_GPIO_DEFAULT (32+16) /* Pin 16 on port B. */
+#else
+#error Unknown CPU, this driver only supports AT32AP700X CPUs.
+#endif
+
+struct continuous {
+ u16 id; /* codec id */
+ u8 code; /* continuous code */
+ u8 reads; /* number of coord reads per read cycle */
+ u32 speed; /* number of coords per second */
+};
+
+#define WM_READS(sp) ((sp / HZ) + 1)
+
+static const struct continuous cinfo[] = {
+ {WM9705_ID2, 0, WM_READS(94), 94},
+ {WM9705_ID2, 1, WM_READS(188), 188},
+ {WM9705_ID2, 2, WM_READS(375), 375},
+ {WM9705_ID2, 3, WM_READS(750), 750},
+ {WM9712_ID2, 0, WM_READS(94), 94},
+ {WM9712_ID2, 1, WM_READS(188), 188},
+ {WM9712_ID2, 2, WM_READS(375), 375},
+ {WM9712_ID2, 3, WM_READS(750), 750},
+ {WM9713_ID2, 0, WM_READS(94), 94},
+ {WM9713_ID2, 1, WM_READS(120), 120},
+ {WM9713_ID2, 2, WM_READS(154), 154},
+ {WM9713_ID2, 3, WM_READS(188), 188},
+};
+
+/* Continuous speed index. */
+static int sp_idx;
+
+/*
+ * Pen sampling frequency (Hz) in continuous mode.
+ */
+static int cont_rate = 188;
+module_param(cont_rate, int, 0);
+MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
+
+/*
+ * Pen down detection.
+ *
+ * This driver can either poll or use an interrupt to indicate a pen down
+ * event. If the irq request fails then it will fall back to polling mode.
+ */
+static int pen_int = 1;
+module_param(pen_int, int, 0);
+MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)");
+
+/*
+ * Pressure readback.
+ *
+ * Set to 1 to read back pen down pressure.
+ */
+static int pressure;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
+
+/*
+ * AC97 touch data slot.
+ *
+ * Touch screen readback data ac97 slot.
+ */
+static int ac97_touch_slot = 5;
+module_param(ac97_touch_slot, int, 0);
+MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
+
+/*
+ * GPIO line number.
+ *
+ * Set to GPIO number where the signal from the WM97xx device is hooked up.
+ */
+static int atmel_gpio_line = ATMEL_WM97XX_GPIO_DEFAULT;
+module_param(atmel_gpio_line, int, 0);
+MODULE_PARM_DESC(atmel_gpio_line, "GPIO line number connected to WM97xx");
+
+struct atmel_wm97xx {
+ struct wm97xx *wm;
+ struct timer_list pen_timer;
+ void __iomem *regs;
+ unsigned long ac97c_irq;
+ unsigned long gpio_pen;
+ unsigned long gpio_irq;
+ unsigned short x;
+ unsigned short y;
+};
+
+static irqreturn_t atmel_wm97xx_channel_b_interrupt(int irq, void *dev_id)
+{
+ struct atmel_wm97xx *atmel_wm97xx = dev_id;
+ struct wm97xx *wm = atmel_wm97xx->wm;
+ int status = ac97c_readl(atmel_wm97xx, CBSR);
+ irqreturn_t retval = IRQ_NONE;
+
+ if (status & AC97C_OVRUN) {
+ dev_dbg(&wm->touch_dev->dev, "AC97C overrun\n");
+ ac97c_readl(atmel_wm97xx, CBRHR);
+ retval = IRQ_HANDLED;
+ } else if (status & AC97C_RXRDY) {
+ u16 data;
+ u16 value;
+ u16 source;
+ u16 pen_down;
+
+ data = ac97c_readl(atmel_wm97xx, CBRHR);
+ value = data & 0x0fff;
+ source = data & WM97XX_ADCSEL_MASK;
+ pen_down = (data & WM97XX_PEN_DOWN) >> 8;
+
+ if (source == WM97XX_ADCSEL_X)
+ atmel_wm97xx->x = value;
+ if (source == WM97XX_ADCSEL_Y)
+ atmel_wm97xx->y = value;
+
+ if (!pressure && source == WM97XX_ADCSEL_Y) {
+ input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x);
+ input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y);
+ input_report_key(wm->input_dev, BTN_TOUCH, pen_down);
+ input_sync(wm->input_dev);
+ } else if (pressure && source == WM97XX_ADCSEL_PRES) {
+ input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x);
+ input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y);
+ input_report_abs(wm->input_dev, ABS_PRESSURE, value);
+ input_report_key(wm->input_dev, BTN_TOUCH, value);
+ input_sync(wm->input_dev);
+ }
+
+ retval = IRQ_HANDLED;
+ }
+
+ return retval;
+}
+
+static void atmel_wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+ struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev);
+ struct input_dev *input_dev = wm->input_dev;
+ int pen_down = gpio_get_value(atmel_wm97xx->gpio_pen);
+
+ if (pen_down != 0) {
+ mod_timer(&atmel_wm97xx->pen_timer,
+ jiffies + msecs_to_jiffies(1));
+ } else {
+ if (pressure)
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_sync(input_dev);
+ }
+}
+
+static void atmel_wm97xx_pen_timer(unsigned long data)
+{
+ atmel_wm97xx_acc_pen_up((struct wm97xx *)data);
+}
+
+static int atmel_wm97xx_acc_startup(struct wm97xx *wm)
+{
+ struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev);
+ int idx = 0;
+
+ if (wm->ac97 == NULL)
+ return -ENODEV;
+
+ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
+ if (wm->id != cinfo[idx].id)
+ continue;
+
+ sp_idx = idx;
+
+ if (cont_rate <= cinfo[idx].speed)
+ break;
+ }
+
+ wm->acc_rate = cinfo[sp_idx].code;
+ wm->acc_slot = ac97_touch_slot;
+ dev_info(&wm->touch_dev->dev, "atmel accelerated touchscreen driver, "
+ "%d samples/sec\n", cinfo[sp_idx].speed);
+
+ if (pen_int) {
+ unsigned long reg;
+
+ wm->pen_irq = atmel_wm97xx->gpio_irq;
+
+ switch (wm->id) {
+ case WM9712_ID2: /* Fall through. */
+ case WM9713_ID2:
+ /*
+ * Use GPIO 13 (PEN_DOWN) to assert GPIO line 3
+ * (PENDOWN).
+ */
+ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
+ WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_STICKY,
+ WM97XX_GPIO_WAKE);
+ wm97xx_config_gpio(wm, WM97XX_GPIO_3, WM97XX_GPIO_OUT,
+ WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_NOTSTICKY,
+ WM97XX_GPIO_NOWAKE);
+ case WM9705_ID2: /* Fall through. */
+ /*
+ * Enable touch data slot in AC97 controller channel B.
+ */
+ reg = ac97c_readl(atmel_wm97xx, ICA);
+ reg &= ~AC97C_CH_MASK(wm->acc_slot);
+ reg |= AC97C_CH_ASSIGN(wm->acc_slot, B);
+ ac97c_writel(atmel_wm97xx, ICA, reg);
+
+ /*
+ * Enable channel and interrupt for RXRDY and OVERRUN.
+ */
+ ac97c_writel(atmel_wm97xx, CBMR, AC97C_CMR_CENA
+ | AC97C_CMR_CEM_BIG
+ | AC97C_CMR_SIZE_16
+ | AC97C_OVRUN
+ | AC97C_RXRDY);
+ /* Dummy read to empty RXRHR. */
+ ac97c_readl(atmel_wm97xx, CBRHR);
+ /*
+ * Enable interrupt for channel B in the AC97
+ * controller.
+ */
+ ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT);
+ break;
+ default:
+ dev_err(&wm->touch_dev->dev, "pen down irq not "
+ "supported on this device\n");
+ pen_int = 0;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void atmel_wm97xx_acc_shutdown(struct wm97xx *wm)
+{
+ if (pen_int) {
+ struct atmel_wm97xx *atmel_wm97xx =
+ platform_get_drvdata(wm->touch_dev);
+ unsigned long ica;
+
+ switch (wm->id & 0xffff) {
+ case WM9705_ID2: /* Fall through. */
+ case WM9712_ID2: /* Fall through. */
+ case WM9713_ID2:
+ /* Disable slot and turn off channel B interrupts. */
+ ica = ac97c_readl(atmel_wm97xx, ICA);
+ ica &= ~AC97C_CH_MASK(wm->acc_slot);
+ ac97c_writel(atmel_wm97xx, ICA, ica);
+ ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT);
+ ac97c_writel(atmel_wm97xx, CBMR, 0);
+ wm->pen_irq = 0;
+ break;
+ default:
+ dev_err(&wm->touch_dev->dev, "unknown codec\n");
+ break;
+ }
+ }
+}
+
+static void atmel_wm97xx_irq_enable(struct wm97xx *wm, int enable)
+{
+ /* Intentionally left empty. */
+}
+
+static struct wm97xx_mach_ops atmel_mach_ops = {
+ .acc_enabled = 1,
+ .acc_pen_up = atmel_wm97xx_acc_pen_up,
+ .acc_startup = atmel_wm97xx_acc_startup,
+ .acc_shutdown = atmel_wm97xx_acc_shutdown,
+ .irq_enable = atmel_wm97xx_irq_enable,
+ .irq_gpio = WM97XX_GPIO_3,
+};
+
+static int __init atmel_wm97xx_probe(struct platform_device *pdev)
+{
+ struct wm97xx *wm = platform_get_drvdata(pdev);
+ struct atmel_wm97xx *atmel_wm97xx;
+ int ret;
+
+ atmel_wm97xx = kzalloc(sizeof(struct atmel_wm97xx), GFP_KERNEL);
+ if (!atmel_wm97xx) {
+ dev_dbg(&pdev->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+
+ atmel_wm97xx->wm = wm;
+ atmel_wm97xx->regs = (void *)ATMEL_WM97XX_AC97C_IOMEM;
+ atmel_wm97xx->ac97c_irq = ATMEL_WM97XX_AC97C_IRQ;
+ atmel_wm97xx->gpio_pen = atmel_gpio_line;
+ atmel_wm97xx->gpio_irq = gpio_to_irq(atmel_wm97xx->gpio_pen);
+
+ setup_timer(&atmel_wm97xx->pen_timer, atmel_wm97xx_pen_timer,
+ (unsigned long)wm);
+
+ ret = request_irq(atmel_wm97xx->ac97c_irq,
+ atmel_wm97xx_channel_b_interrupt,
+ IRQF_SHARED, "atmel-wm97xx-ch-b", atmel_wm97xx);
+ if (ret) {
+ dev_dbg(&pdev->dev, "could not request ac97c irq\n");
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, atmel_wm97xx);
+
+ ret = wm97xx_register_mach_ops(wm, &atmel_mach_ops);
+ if (ret)
+ goto err_irq;
+
+ return ret;
+
+err_irq:
+ free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx);
+err:
+ kfree(atmel_wm97xx);
+ return ret;
+}
+
+static int __exit atmel_wm97xx_remove(struct platform_device *pdev)
+{
+ struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
+ struct wm97xx *wm = atmel_wm97xx->wm;
+
+ ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT);
+ free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx);
+ del_timer_sync(&atmel_wm97xx->pen_timer);
+ wm97xx_unregister_mach_ops(wm);
+ kfree(atmel_wm97xx);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int atmel_wm97xx_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
+
+ ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT);
+ disable_irq(atmel_wm97xx->gpio_irq);
+ del_timer_sync(&atmel_wm97xx->pen_timer);
+
+ return 0;
+}
+
+static int atmel_wm97xx_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
+ struct wm97xx *wm = atmel_wm97xx->wm;
+
+ if (wm->input_dev->users) {
+ enable_irq(atmel_wm97xx->gpio_irq);
+ ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_wm97xx_pm_ops,
+ atmel_wm97xx_suspend, atmel_wm97xx_resume);
+
+static struct platform_driver atmel_wm97xx_driver = {
+ .remove = __exit_p(atmel_wm97xx_remove),
+ .driver = {
+ .name = "wm97xx-touch",
+ .pm = &atmel_wm97xx_pm_ops,
+ },
+};
+
+module_platform_driver_probe(atmel_wm97xx_driver, atmel_wm97xx_probe);
+
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
+MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
new file mode 100644
index 000000000..b406ed0af
--- /dev/null
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -0,0 +1,2683 @@
+/*
+ * Atmel maXTouch Touchscreen driver
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Copyright (C) 2011-2014 Atmel Corporation
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+/* Version */
+#define MXT_VER_20 20
+#define MXT_VER_21 21
+#define MXT_VER_22 22
+
+/* Firmware files */
+#define MXT_FW_NAME "/*(DEBLOBBED)*/"
+#define MXT_CFG_NAME "maxtouch.cfg"
+#define MXT_CFG_MAGIC "OBP_RAW V1"
+
+/* Registers */
+#define MXT_INFO 0x00
+#define MXT_FAMILY_ID 0x00
+#define MXT_VARIANT_ID 0x01
+#define MXT_VERSION 0x02
+#define MXT_BUILD 0x03
+#define MXT_MATRIX_X_SIZE 0x04
+#define MXT_MATRIX_Y_SIZE 0x05
+#define MXT_OBJECT_NUM 0x06
+#define MXT_OBJECT_START 0x07
+
+#define MXT_OBJECT_SIZE 6
+#define MXT_INFO_CHECKSUM_SIZE 3
+#define MXT_MAX_BLOCK_WRITE 256
+
+/* Object types */
+#define MXT_DEBUG_DIAGNOSTIC_T37 37
+#define MXT_GEN_MESSAGE_T5 5
+#define MXT_GEN_COMMAND_T6 6
+#define MXT_GEN_POWER_T7 7
+#define MXT_GEN_ACQUIRE_T8 8
+#define MXT_GEN_DATASOURCE_T53 53
+#define MXT_TOUCH_MULTI_T9 9
+#define MXT_TOUCH_KEYARRAY_T15 15
+#define MXT_TOUCH_PROXIMITY_T23 23
+#define MXT_TOUCH_PROXKEY_T52 52
+#define MXT_PROCI_GRIPFACE_T20 20
+#define MXT_PROCG_NOISE_T22 22
+#define MXT_PROCI_ONETOUCH_T24 24
+#define MXT_PROCI_TWOTOUCH_T27 27
+#define MXT_PROCI_GRIP_T40 40
+#define MXT_PROCI_PALM_T41 41
+#define MXT_PROCI_TOUCHSUPPRESSION_T42 42
+#define MXT_PROCI_STYLUS_T47 47
+#define MXT_PROCG_NOISESUPPRESSION_T48 48
+#define MXT_SPT_COMMSCONFIG_T18 18
+#define MXT_SPT_GPIOPWM_T19 19
+#define MXT_SPT_SELFTEST_T25 25
+#define MXT_SPT_CTECONFIG_T28 28
+#define MXT_SPT_USERDATA_T38 38
+#define MXT_SPT_DIGITIZER_T43 43
+#define MXT_SPT_MESSAGECOUNT_T44 44
+#define MXT_SPT_CTECONFIG_T46 46
+#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100
+
+/* MXT_GEN_MESSAGE_T5 object */
+#define MXT_RPTID_NOMSG 0xff
+
+/* MXT_GEN_COMMAND_T6 field */
+#define MXT_COMMAND_RESET 0
+#define MXT_COMMAND_BACKUPNV 1
+#define MXT_COMMAND_CALIBRATE 2
+#define MXT_COMMAND_REPORTALL 3
+#define MXT_COMMAND_DIAGNOSTIC 5
+
+/* Define for T6 status byte */
+#define MXT_T6_STATUS_RESET (1 << 7)
+#define MXT_T6_STATUS_OFL (1 << 6)
+#define MXT_T6_STATUS_SIGERR (1 << 5)
+#define MXT_T6_STATUS_CAL (1 << 4)
+#define MXT_T6_STATUS_CFGERR (1 << 3)
+#define MXT_T6_STATUS_COMSERR (1 << 2)
+
+/* MXT_GEN_POWER_T7 field */
+#define MXT_POWER_IDLEACQINT 0
+#define MXT_POWER_ACTVACQINT 1
+#define MXT_POWER_ACTV2IDLETO 2
+
+/* MXT_GEN_ACQUIRE_T8 field */
+#define MXT_ACQUIRE_CHRGTIME 0
+#define MXT_ACQUIRE_TCHDRIFT 2
+#define MXT_ACQUIRE_DRIFTST 3
+#define MXT_ACQUIRE_TCHAUTOCAL 4
+#define MXT_ACQUIRE_SYNC 5
+#define MXT_ACQUIRE_ATCHCALST 6
+#define MXT_ACQUIRE_ATCHCALSTHR 7
+
+/* MXT_TOUCH_MULTI_T9 field */
+#define MXT_TOUCH_CTRL 0
+#define MXT_T9_ORIENT 9
+#define MXT_T9_RANGE 18
+
+/* MXT_TOUCH_MULTI_T9 status */
+#define MXT_T9_UNGRIP (1 << 0)
+#define MXT_T9_SUPPRESS (1 << 1)
+#define MXT_T9_AMP (1 << 2)
+#define MXT_T9_VECTOR (1 << 3)
+#define MXT_T9_MOVE (1 << 4)
+#define MXT_T9_RELEASE (1 << 5)
+#define MXT_T9_PRESS (1 << 6)
+#define MXT_T9_DETECT (1 << 7)
+
+struct t9_range {
+ u16 x;
+ u16 y;
+} __packed;
+
+/* MXT_TOUCH_MULTI_T9 orient */
+#define MXT_T9_ORIENT_SWITCH (1 << 0)
+
+/* MXT_PROCI_GRIPFACE_T20 field */
+#define MXT_GRIPFACE_CTRL 0
+#define MXT_GRIPFACE_XLOGRIP 1
+#define MXT_GRIPFACE_XHIGRIP 2
+#define MXT_GRIPFACE_YLOGRIP 3
+#define MXT_GRIPFACE_YHIGRIP 4
+#define MXT_GRIPFACE_MAXTCHS 5
+#define MXT_GRIPFACE_SZTHR1 7
+#define MXT_GRIPFACE_SZTHR2 8
+#define MXT_GRIPFACE_SHPTHR1 9
+#define MXT_GRIPFACE_SHPTHR2 10
+#define MXT_GRIPFACE_SUPEXTTO 11
+
+/* MXT_PROCI_NOISE field */
+#define MXT_NOISE_CTRL 0
+#define MXT_NOISE_OUTFLEN 1
+#define MXT_NOISE_GCAFUL_LSB 3
+#define MXT_NOISE_GCAFUL_MSB 4
+#define MXT_NOISE_GCAFLL_LSB 5
+#define MXT_NOISE_GCAFLL_MSB 6
+#define MXT_NOISE_ACTVGCAFVALID 7
+#define MXT_NOISE_NOISETHR 8
+#define MXT_NOISE_FREQHOPSCALE 10
+#define MXT_NOISE_FREQ0 11
+#define MXT_NOISE_FREQ1 12
+#define MXT_NOISE_FREQ2 13
+#define MXT_NOISE_FREQ3 14
+#define MXT_NOISE_FREQ4 15
+#define MXT_NOISE_IDLEGCAFVALID 16
+
+/* MXT_SPT_COMMSCONFIG_T18 */
+#define MXT_COMMS_CTRL 0
+#define MXT_COMMS_CMD 1
+
+/* MXT_SPT_CTECONFIG_T28 field */
+#define MXT_CTE_CTRL 0
+#define MXT_CTE_CMD 1
+#define MXT_CTE_MODE 2
+#define MXT_CTE_IDLEGCAFDEPTH 3
+#define MXT_CTE_ACTVGCAFDEPTH 4
+#define MXT_CTE_VOLTAGE 5
+
+#define MXT_VOLTAGE_DEFAULT 2700000
+#define MXT_VOLTAGE_STEP 10000
+
+/* Define for MXT_GEN_COMMAND_T6 */
+#define MXT_BOOT_VALUE 0xa5
+#define MXT_RESET_VALUE 0x01
+#define MXT_BACKUP_VALUE 0x55
+
+/* T100 Multiple Touch Touchscreen */
+#define MXT_T100_CTRL 0
+#define MXT_T100_CFG1 1
+#define MXT_T100_TCHAUX 3
+#define MXT_T100_XRANGE 13
+#define MXT_T100_YRANGE 24
+
+#define MXT_T100_CFG_SWITCHXY BIT(5)
+
+#define MXT_T100_TCHAUX_VECT BIT(0)
+#define MXT_T100_TCHAUX_AMPL BIT(1)
+#define MXT_T100_TCHAUX_AREA BIT(2)
+
+#define MXT_T100_DETECT BIT(7)
+#define MXT_T100_TYPE_MASK 0x70
+
+enum t100_type {
+ MXT_T100_TYPE_FINGER = 1,
+ MXT_T100_TYPE_PASSIVE_STYLUS = 2,
+ MXT_T100_TYPE_HOVERING_FINGER = 4,
+ MXT_T100_TYPE_GLOVE = 5,
+ MXT_T100_TYPE_LARGE_TOUCH = 6,
+};
+
+#define MXT_DISTANCE_ACTIVE_TOUCH 0
+#define MXT_DISTANCE_HOVERING 1
+
+#define MXT_TOUCH_MAJOR_DEFAULT 1
+#define MXT_PRESSURE_DEFAULT 1
+
+/* Delay times */
+#define MXT_BACKUP_TIME 50 /* msec */
+#define MXT_RESET_TIME 200 /* msec */
+#define MXT_RESET_TIMEOUT 3000 /* msec */
+#define MXT_CRC_TIMEOUT 1000 /* msec */
+#define MXT_FW_RESET_TIME 3000 /* msec */
+#define MXT_FW_CHG_TIMEOUT 300 /* msec */
+
+/* Command to unlock bootloader */
+#define MXT_UNLOCK_CMD_MSB 0xaa
+#define MXT_UNLOCK_CMD_LSB 0xdc
+
+/* Bootloader mode status */
+#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */
+#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */
+#define MXT_FRAME_CRC_CHECK 0x02
+#define MXT_FRAME_CRC_FAIL 0x03
+#define MXT_FRAME_CRC_PASS 0x04
+#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */
+#define MXT_BOOT_STATUS_MASK 0x3f
+#define MXT_BOOT_EXTENDED_ID (1 << 5)
+#define MXT_BOOT_ID_MASK 0x1f
+
+/* Touchscreen absolute values */
+#define MXT_MAX_AREA 0xff
+
+#define MXT_PIXELS_PER_MM 20
+
+struct mxt_info {
+ u8 family_id;
+ u8 variant_id;
+ u8 version;
+ u8 build;
+ u8 matrix_xsize;
+ u8 matrix_ysize;
+ u8 object_num;
+};
+
+struct mxt_object {
+ u8 type;
+ u16 start_address;
+ u8 size_minus_one;
+ u8 instances_minus_one;
+ u8 num_report_ids;
+} __packed;
+
+/* Each client has this additional data */
+struct mxt_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ char phys[64]; /* device physical location */
+ const struct mxt_platform_data *pdata;
+ struct mxt_object *object_table;
+ struct mxt_info info;
+ unsigned int irq;
+ unsigned int max_x;
+ unsigned int max_y;
+ bool in_bootloader;
+ u16 mem_size;
+ u8 t100_aux_ampl;
+ u8 t100_aux_area;
+ u8 t100_aux_vect;
+ u8 max_reportid;
+ u32 config_crc;
+ u32 info_crc;
+ u8 bootloader_addr;
+ u8 *msg_buf;
+ u8 t6_status;
+ bool update_input;
+ u8 last_message_count;
+ u8 num_touchids;
+ u8 multitouch;
+
+ /* Cached parameters from object table */
+ u16 T5_address;
+ u8 T5_msg_size;
+ u8 T6_reportid;
+ u16 T6_address;
+ u16 T7_address;
+ u8 T9_reportid_min;
+ u8 T9_reportid_max;
+ u8 T19_reportid;
+ u16 T44_address;
+ u8 T100_reportid_min;
+ u8 T100_reportid_max;
+
+ /* for fw update in bootloader */
+ struct completion bl_completion;
+
+ /* for reset handling */
+ struct completion reset_completion;
+
+ /* for config update handling */
+ struct completion crc_completion;
+};
+
+static size_t mxt_obj_size(const struct mxt_object *obj)
+{
+ return obj->size_minus_one + 1;
+}
+
+static size_t mxt_obj_instances(const struct mxt_object *obj)
+{
+ return obj->instances_minus_one + 1;
+}
+
+static bool mxt_object_readable(unsigned int type)
+{
+ switch (type) {
+ case MXT_GEN_COMMAND_T6:
+ case MXT_GEN_POWER_T7:
+ case MXT_GEN_ACQUIRE_T8:
+ case MXT_GEN_DATASOURCE_T53:
+ case MXT_TOUCH_MULTI_T9:
+ case MXT_TOUCH_KEYARRAY_T15:
+ case MXT_TOUCH_PROXIMITY_T23:
+ case MXT_TOUCH_PROXKEY_T52:
+ case MXT_PROCI_GRIPFACE_T20:
+ case MXT_PROCG_NOISE_T22:
+ case MXT_PROCI_ONETOUCH_T24:
+ case MXT_PROCI_TWOTOUCH_T27:
+ case MXT_PROCI_GRIP_T40:
+ case MXT_PROCI_PALM_T41:
+ case MXT_PROCI_TOUCHSUPPRESSION_T42:
+ case MXT_PROCI_STYLUS_T47:
+ case MXT_PROCG_NOISESUPPRESSION_T48:
+ case MXT_SPT_COMMSCONFIG_T18:
+ case MXT_SPT_GPIOPWM_T19:
+ case MXT_SPT_SELFTEST_T25:
+ case MXT_SPT_CTECONFIG_T28:
+ case MXT_SPT_USERDATA_T38:
+ case MXT_SPT_DIGITIZER_T43:
+ case MXT_SPT_CTECONFIG_T46:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void mxt_dump_message(struct mxt_data *data, u8 *message)
+{
+ dev_dbg(&data->client->dev, "message: %*ph\n",
+ data->T5_msg_size, message);
+}
+
+static int mxt_wait_for_completion(struct mxt_data *data,
+ struct completion *comp,
+ unsigned int timeout_ms)
+{
+ struct device *dev = &data->client->dev;
+ unsigned long timeout = msecs_to_jiffies(timeout_ms);
+ long ret;
+
+ ret = wait_for_completion_interruptible_timeout(comp, timeout);
+ if (ret < 0) {
+ return ret;
+ } else if (ret == 0) {
+ dev_err(dev, "Wait for completion timed out.\n");
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static int mxt_bootloader_read(struct mxt_data *data,
+ u8 *val, unsigned int count)
+{
+ int ret;
+ struct i2c_msg msg;
+
+ msg.addr = data->bootloader_addr;
+ msg.flags = data->client->flags & I2C_M_TEN;
+ msg.flags |= I2C_M_RD;
+ msg.len = count;
+ msg.buf = val;
+
+ ret = i2c_transfer(data->client->adapter, &msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ ret = ret < 0 ? ret : -EIO;
+ dev_err(&data->client->dev, "%s: i2c recv failed (%d)\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static int mxt_bootloader_write(struct mxt_data *data,
+ const u8 * const val, unsigned int count)
+{
+ int ret;
+ struct i2c_msg msg;
+
+ msg.addr = data->bootloader_addr;
+ msg.flags = data->client->flags & I2C_M_TEN;
+ msg.len = count;
+ msg.buf = (u8 *)val;
+
+ ret = i2c_transfer(data->client->adapter, &msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ ret = ret < 0 ? ret : -EIO;
+ dev_err(&data->client->dev, "%s: i2c send failed (%d)\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
+{
+ u8 appmode = data->client->addr;
+ u8 bootloader;
+
+ switch (appmode) {
+ case 0x4a:
+ case 0x4b:
+ /* Chips after 1664S use different scheme */
+ if (retry || data->info.family_id >= 0xa2) {
+ bootloader = appmode - 0x24;
+ break;
+ }
+ /* Fall through for normal case */
+ case 0x4c:
+ case 0x4d:
+ case 0x5a:
+ case 0x5b:
+ bootloader = appmode - 0x26;
+ break;
+
+ default:
+ dev_err(&data->client->dev,
+ "Appmode i2c address 0x%02x not found\n",
+ appmode);
+ return -EINVAL;
+ }
+
+ data->bootloader_addr = bootloader;
+ return 0;
+}
+
+static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address)
+{
+ struct device *dev = &data->client->dev;
+ int error;
+ u8 val;
+ bool crc_failure;
+
+ error = mxt_lookup_bootloader_address(data, alt_address);
+ if (error)
+ return error;
+
+ error = mxt_bootloader_read(data, &val, 1);
+ if (error)
+ return error;
+
+ /* Check app crc fail mode */
+ crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL;
+
+ dev_err(dev, "Detected bootloader, status:%02X%s\n",
+ val, crc_failure ? ", APP_CRC_FAIL" : "");
+
+ return 0;
+}
+
+static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val)
+{
+ struct device *dev = &data->client->dev;
+ u8 buf[3];
+
+ if (val & MXT_BOOT_EXTENDED_ID) {
+ if (mxt_bootloader_read(data, &buf[0], 3) != 0) {
+ dev_err(dev, "%s: i2c failure\n", __func__);
+ return val;
+ }
+
+ dev_dbg(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]);
+
+ return buf[0];
+ } else {
+ dev_dbg(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK);
+
+ return val;
+ }
+}
+
+static int mxt_check_bootloader(struct mxt_data *data, unsigned int state,
+ bool wait)
+{
+ struct device *dev = &data->client->dev;
+ u8 val;
+ int ret;
+
+recheck:
+ if (wait) {
+ /*
+ * In application update mode, the interrupt
+ * line signals state transitions. We must wait for the
+ * CHG assertion before reading the status byte.
+ * Once the status byte has been read, the line is deasserted.
+ */
+ ret = mxt_wait_for_completion(data, &data->bl_completion,
+ MXT_FW_CHG_TIMEOUT);
+ if (ret) {
+ /*
+ * TODO: handle -ERESTARTSYS better by terminating
+ * fw update process before returning to userspace
+ * by writing length 0x000 to device (iff we are in
+ * WAITING_FRAME_DATA state).
+ */
+ dev_err(dev, "Update wait error %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = mxt_bootloader_read(data, &val, 1);
+ if (ret)
+ return ret;
+
+ if (state == MXT_WAITING_BOOTLOAD_CMD)
+ val = mxt_get_bootloader_version(data, val);
+
+ switch (state) {
+ case MXT_WAITING_BOOTLOAD_CMD:
+ case MXT_WAITING_FRAME_DATA:
+ case MXT_APP_CRC_FAIL:
+ val &= ~MXT_BOOT_STATUS_MASK;
+ break;
+ case MXT_FRAME_CRC_PASS:
+ if (val == MXT_FRAME_CRC_CHECK) {
+ goto recheck;
+ } else if (val == MXT_FRAME_CRC_FAIL) {
+ dev_err(dev, "Bootloader CRC fail\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (val != state) {
+ dev_err(dev, "Invalid bootloader state %02X != %02X\n",
+ val, state);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock)
+{
+ int ret;
+ u8 buf[2];
+
+ if (unlock) {
+ buf[0] = MXT_UNLOCK_CMD_LSB;
+ buf[1] = MXT_UNLOCK_CMD_MSB;
+ } else {
+ buf[0] = 0x01;
+ buf[1] = 0x01;
+ }
+
+ ret = mxt_bootloader_write(data, buf, 2);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int __mxt_read_reg(struct i2c_client *client,
+ u16 reg, u16 len, void *val)
+{
+ struct i2c_msg xfer[2];
+ u8 buf[2];
+ int ret;
+
+ buf[0] = reg & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+
+ /* Write register */
+ xfer[0].addr = client->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 2;
+ xfer[0].buf = buf;
+
+ /* Read data */
+ xfer[1].addr = client->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = len;
+ xfer[1].buf = val;
+
+ ret = i2c_transfer(client->adapter, xfer, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ if (ret >= 0)
+ ret = -EIO;
+ dev_err(&client->dev, "%s: i2c transfer failed (%d)\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
+ const void *val)
+{
+ u8 *buf;
+ size_t count;
+ int ret;
+
+ count = len + 2;
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = reg & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+ memcpy(&buf[2], val, len);
+
+ ret = i2c_master_send(client, buf, count);
+ if (ret == count) {
+ ret = 0;
+ } else {
+ if (ret >= 0)
+ ret = -EIO;
+ dev_err(&client->dev, "%s: i2c send failed (%d)\n",
+ __func__, ret);
+ }
+
+ kfree(buf);
+ return ret;
+}
+
+static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
+{
+ return __mxt_write_reg(client, reg, 1, &val);
+}
+
+static struct mxt_object *
+mxt_get_object(struct mxt_data *data, u8 type)
+{
+ struct mxt_object *object;
+ int i;
+
+ for (i = 0; i < data->info.object_num; i++) {
+ object = data->object_table + i;
+ if (object->type == type)
+ return object;
+ }
+
+ dev_warn(&data->client->dev, "Invalid object type T%u\n", type);
+ return NULL;
+}
+
+static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg)
+{
+ struct device *dev = &data->client->dev;
+ u8 status = msg[1];
+ u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16);
+
+ complete(&data->crc_completion);
+
+ if (crc != data->config_crc) {
+ data->config_crc = crc;
+ dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc);
+ }
+
+ /* Detect reset */
+ if (status & MXT_T6_STATUS_RESET)
+ complete(&data->reset_completion);
+
+ /* Output debug if status has changed */
+ if (status != data->t6_status)
+ dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n",
+ status,
+ status == 0 ? " OK" : "",
+ status & MXT_T6_STATUS_RESET ? " RESET" : "",
+ status & MXT_T6_STATUS_OFL ? " OFL" : "",
+ status & MXT_T6_STATUS_SIGERR ? " SIGERR" : "",
+ status & MXT_T6_STATUS_CAL ? " CAL" : "",
+ status & MXT_T6_STATUS_CFGERR ? " CFGERR" : "",
+ status & MXT_T6_STATUS_COMSERR ? " COMSERR" : "");
+
+ /* Save current status */
+ data->t6_status = status;
+}
+
+static int mxt_write_object(struct mxt_data *data,
+ u8 type, u8 offset, u8 val)
+{
+ struct mxt_object *object;
+ u16 reg;
+
+ object = mxt_get_object(data, type);
+ if (!object || offset >= mxt_obj_size(object))
+ return -EINVAL;
+
+ reg = object->start_address;
+ return mxt_write_reg(data->client, reg + offset, val);
+}
+
+static void mxt_input_button(struct mxt_data *data, u8 *message)
+{
+ struct input_dev *input = data->input_dev;
+ const struct mxt_platform_data *pdata = data->pdata;
+ bool button;
+ int i;
+
+ /* Active-low switch */
+ for (i = 0; i < pdata->t19_num_keys; i++) {
+ if (pdata->t19_keymap[i] == KEY_RESERVED)
+ continue;
+ button = !(message[1] & (1 << i));
+ input_report_key(input, pdata->t19_keymap[i], button);
+ }
+}
+
+static void mxt_input_sync(struct mxt_data *data)
+{
+ input_mt_report_pointer_emulation(data->input_dev,
+ data->pdata->t19_num_keys);
+ input_sync(data->input_dev);
+}
+
+static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
+{
+ struct device *dev = &data->client->dev;
+ struct input_dev *input_dev = data->input_dev;
+ int id;
+ u8 status;
+ int x;
+ int y;
+ int area;
+ int amplitude;
+
+ id = message[0] - data->T9_reportid_min;
+ status = message[1];
+ x = (message[2] << 4) | ((message[4] >> 4) & 0xf);
+ y = (message[3] << 4) | ((message[4] & 0xf));
+
+ /* Handle 10/12 bit switching */
+ if (data->max_x < 1024)
+ x >>= 2;
+ if (data->max_y < 1024)
+ y >>= 2;
+
+ area = message[5];
+ amplitude = message[6];
+
+ dev_dbg(dev,
+ "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n",
+ id,
+ (status & MXT_T9_DETECT) ? 'D' : '.',
+ (status & MXT_T9_PRESS) ? 'P' : '.',
+ (status & MXT_T9_RELEASE) ? 'R' : '.',
+ (status & MXT_T9_MOVE) ? 'M' : '.',
+ (status & MXT_T9_VECTOR) ? 'V' : '.',
+ (status & MXT_T9_AMP) ? 'A' : '.',
+ (status & MXT_T9_SUPPRESS) ? 'S' : '.',
+ (status & MXT_T9_UNGRIP) ? 'U' : '.',
+ x, y, area, amplitude);
+
+ input_mt_slot(input_dev, id);
+
+ if (status & MXT_T9_DETECT) {
+ /*
+ * Multiple bits may be set if the host is slow to read
+ * the status messages, indicating all the events that
+ * have happened.
+ */
+ if (status & MXT_T9_RELEASE) {
+ input_mt_report_slot_state(input_dev,
+ MT_TOOL_FINGER, 0);
+ mxt_input_sync(data);
+ }
+
+ /* Touch active */
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude);
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area);
+ } else {
+ /* Touch no longer active, close out slot */
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0);
+ }
+
+ data->update_input = true;
+}
+
+static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
+{
+ struct device *dev = &data->client->dev;
+ struct input_dev *input_dev = data->input_dev;
+ int id;
+ u8 status;
+ u8 type = 0;
+ u16 x;
+ u16 y;
+ int distance = 0;
+ int tool = 0;
+ u8 major = 0;
+ u8 pressure = 0;
+ u8 orientation = 0;
+
+ id = message[0] - data->T100_reportid_min - 2;
+
+ /* ignore SCRSTATUS events */
+ if (id < 0)
+ return;
+
+ status = message[1];
+ x = get_unaligned_le16(&message[2]);
+ y = get_unaligned_le16(&message[4]);
+
+ if (status & MXT_T100_DETECT) {
+ type = (status & MXT_T100_TYPE_MASK) >> 4;
+
+ switch (type) {
+ case MXT_T100_TYPE_HOVERING_FINGER:
+ tool = MT_TOOL_FINGER;
+ distance = MXT_DISTANCE_HOVERING;
+
+ if (data->t100_aux_vect)
+ orientation = message[data->t100_aux_vect];
+
+ break;
+
+ case MXT_T100_TYPE_FINGER:
+ case MXT_T100_TYPE_GLOVE:
+ tool = MT_TOOL_FINGER;
+ distance = MXT_DISTANCE_ACTIVE_TOUCH;
+
+ if (data->t100_aux_area)
+ major = message[data->t100_aux_area];
+
+ if (data->t100_aux_ampl)
+ pressure = message[data->t100_aux_ampl];
+
+ if (data->t100_aux_vect)
+ orientation = message[data->t100_aux_vect];
+
+ break;
+
+ case MXT_T100_TYPE_PASSIVE_STYLUS:
+ tool = MT_TOOL_PEN;
+
+ /*
+ * Passive stylus is reported with size zero so
+ * hardcode.
+ */
+ major = MXT_TOUCH_MAJOR_DEFAULT;
+
+ if (data->t100_aux_ampl)
+ pressure = message[data->t100_aux_ampl];
+
+ break;
+
+ case MXT_T100_TYPE_LARGE_TOUCH:
+ /* Ignore suppressed touch */
+ break;
+
+ default:
+ dev_dbg(dev, "Unexpected T100 type\n");
+ return;
+ }
+ }
+
+ /*
+ * Values reported should be non-zero if tool is touching the
+ * device
+ */
+ if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER)
+ pressure = MXT_PRESSURE_DEFAULT;
+
+ input_mt_slot(input_dev, id);
+
+ if (status & MXT_T100_DETECT) {
+ dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n",
+ id, type, x, y, major, pressure, orientation);
+
+ input_mt_report_slot_state(input_dev, tool, 1);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+ input_report_abs(input_dev, ABS_MT_DISTANCE, distance);
+ input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation);
+ } else {
+ dev_dbg(dev, "[%u] release\n", id);
+
+ /* close out slot */
+ input_mt_report_slot_state(input_dev, 0, 0);
+ }
+
+ data->update_input = true;
+}
+
+static int mxt_proc_message(struct mxt_data *data, u8 *message)
+{
+ u8 report_id = message[0];
+
+ if (report_id == MXT_RPTID_NOMSG)
+ return 0;
+
+ if (report_id == data->T6_reportid) {
+ mxt_proc_t6_messages(data, message);
+ } else if (!data->input_dev) {
+ /*
+ * Do not report events if input device
+ * is not yet registered.
+ */
+ mxt_dump_message(data, message);
+ } else if (report_id >= data->T9_reportid_min &&
+ report_id <= data->T9_reportid_max) {
+ mxt_proc_t9_message(data, message);
+ } else if (report_id >= data->T100_reportid_min &&
+ report_id <= data->T100_reportid_max) {
+ mxt_proc_t100_message(data, message);
+ } else if (report_id == data->T19_reportid) {
+ mxt_input_button(data, message);
+ data->update_input = true;
+ } else {
+ mxt_dump_message(data, message);
+ }
+
+ return 1;
+}
+
+static int mxt_read_and_process_messages(struct mxt_data *data, u8 count)
+{
+ struct device *dev = &data->client->dev;
+ int ret;
+ int i;
+ u8 num_valid = 0;
+
+ /* Safety check for msg_buf */
+ if (count > data->max_reportid)
+ return -EINVAL;
+
+ /* Process remaining messages if necessary */
+ ret = __mxt_read_reg(data->client, data->T5_address,
+ data->T5_msg_size * count, data->msg_buf);
+ if (ret) {
+ dev_err(dev, "Failed to read %u messages (%d)\n", count, ret);
+ return ret;
+ }
+
+ for (i = 0; i < count; i++) {
+ ret = mxt_proc_message(data,
+ data->msg_buf + data->T5_msg_size * i);
+
+ if (ret == 1)
+ num_valid++;
+ }
+
+ /* return number of messages read */
+ return num_valid;
+}
+
+static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int ret;
+ u8 count, num_left;
+
+ /* Read T44 and T5 together */
+ ret = __mxt_read_reg(data->client, data->T44_address,
+ data->T5_msg_size + 1, data->msg_buf);
+ if (ret) {
+ dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret);
+ return IRQ_NONE;
+ }
+
+ count = data->msg_buf[0];
+
+ if (count == 0) {
+ /*
+ * This condition is caused by the CHG line being configured
+ * in Mode 0. It results in unnecessary I2C operations but it
+ * is benign.
+ */
+ dev_dbg(dev, "Interrupt triggered but zero messages\n");
+ return IRQ_NONE;
+ } else if (count > data->max_reportid) {
+ dev_err(dev, "T44 count %d exceeded max report id\n", count);
+ count = data->max_reportid;
+ }
+
+ /* Process first message */
+ ret = mxt_proc_message(data, data->msg_buf + 1);
+ if (ret < 0) {
+ dev_warn(dev, "Unexpected invalid message\n");
+ return IRQ_NONE;
+ }
+
+ num_left = count - 1;
+
+ /* Process remaining messages if necessary */
+ if (num_left) {
+ ret = mxt_read_and_process_messages(data, num_left);
+ if (ret < 0)
+ goto end;
+ else if (ret != num_left)
+ dev_warn(dev, "Unexpected invalid message\n");
+ }
+
+end:
+ if (data->update_input) {
+ mxt_input_sync(data);
+ data->update_input = false;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mxt_process_messages_until_invalid(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int count, read;
+ u8 tries = 2;
+
+ count = data->max_reportid;
+
+ /* Read messages until we force an invalid */
+ do {
+ read = mxt_read_and_process_messages(data, count);
+ if (read < count)
+ return 0;
+ } while (--tries);
+
+ if (data->update_input) {
+ mxt_input_sync(data);
+ data->update_input = false;
+ }
+
+ dev_err(dev, "CHG pin isn't cleared\n");
+ return -EBUSY;
+}
+
+static irqreturn_t mxt_process_messages(struct mxt_data *data)
+{
+ int total_handled, num_handled;
+ u8 count = data->last_message_count;
+
+ if (count < 1 || count > data->max_reportid)
+ count = 1;
+
+ /* include final invalid message */
+ total_handled = mxt_read_and_process_messages(data, count + 1);
+ if (total_handled < 0)
+ return IRQ_NONE;
+ /* if there were invalid messages, then we are done */
+ else if (total_handled <= count)
+ goto update_count;
+
+ /* keep reading two msgs until one is invalid or reportid limit */
+ do {
+ num_handled = mxt_read_and_process_messages(data, 2);
+ if (num_handled < 0)
+ return IRQ_NONE;
+
+ total_handled += num_handled;
+
+ if (num_handled < 2)
+ break;
+ } while (total_handled < data->num_touchids);
+
+update_count:
+ data->last_message_count = total_handled;
+
+ if (data->update_input) {
+ mxt_input_sync(data);
+ data->update_input = false;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxt_interrupt(int irq, void *dev_id)
+{
+ struct mxt_data *data = dev_id;
+
+ if (data->in_bootloader) {
+ /* bootloader state transition completion */
+ complete(&data->bl_completion);
+ return IRQ_HANDLED;
+ }
+
+ if (!data->object_table)
+ return IRQ_HANDLED;
+
+ if (data->T44_address) {
+ return mxt_process_messages_t44(data);
+ } else {
+ return mxt_process_messages(data);
+ }
+}
+
+static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset,
+ u8 value, bool wait)
+{
+ u16 reg;
+ u8 command_register;
+ int timeout_counter = 0;
+ int ret;
+
+ reg = data->T6_address + cmd_offset;
+
+ ret = mxt_write_reg(data->client, reg, value);
+ if (ret)
+ return ret;
+
+ if (!wait)
+ return 0;
+
+ do {
+ msleep(20);
+ ret = __mxt_read_reg(data->client, reg, 1, &command_register);
+ if (ret)
+ return ret;
+ } while (command_register != 0 && timeout_counter++ <= 100);
+
+ if (timeout_counter > 100) {
+ dev_err(&data->client->dev, "Command failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mxt_soft_reset(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int ret = 0;
+
+ dev_info(dev, "Resetting chip\n");
+
+ reinit_completion(&data->reset_completion);
+
+ ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false);
+ if (ret)
+ return ret;
+
+ ret = mxt_wait_for_completion(data, &data->reset_completion,
+ MXT_RESET_TIMEOUT);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value)
+{
+ /*
+ * On failure, CRC is set to 0 and config will always be
+ * downloaded.
+ */
+ data->config_crc = 0;
+ reinit_completion(&data->crc_completion);
+
+ mxt_t6_command(data, cmd, value, true);
+
+ /*
+ * Wait for crc message. On failure, CRC is set to 0 and config will
+ * always be downloaded.
+ */
+ mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT);
+}
+
+static void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte)
+{
+ static const unsigned int crcpoly = 0x80001B;
+ u32 result;
+ u32 data_word;
+
+ data_word = (secondbyte << 8) | firstbyte;
+ result = ((*crc << 1) ^ data_word);
+
+ if (result & 0x1000000)
+ result ^= crcpoly;
+
+ *crc = result;
+}
+
+static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off)
+{
+ u32 crc = 0;
+ u8 *ptr = base + start_off;
+ u8 *last_val = base + end_off - 1;
+
+ if (end_off < start_off)
+ return -EINVAL;
+
+ while (ptr < last_val) {
+ mxt_calc_crc24(&crc, *ptr, *(ptr + 1));
+ ptr += 2;
+ }
+
+ /* if len is odd, fill the last byte with 0 */
+ if (ptr == last_val)
+ mxt_calc_crc24(&crc, *ptr, 0);
+
+ /* Mask to 24-bit */
+ crc &= 0x00FFFFFF;
+
+ return crc;
+}
+
+static int mxt_prepare_cfg_mem(struct mxt_data *data,
+ const struct firmware *cfg,
+ unsigned int data_pos,
+ unsigned int cfg_start_ofs,
+ u8 *config_mem,
+ size_t config_mem_size)
+{
+ struct device *dev = &data->client->dev;
+ struct mxt_object *object;
+ unsigned int type, instance, size, byte_offset;
+ int offset;
+ int ret;
+ int i;
+ u16 reg;
+ u8 val;
+
+ while (data_pos < cfg->size) {
+ /* Read type, instance, length */
+ ret = sscanf(cfg->data + data_pos, "%x %x %x%n",
+ &type, &instance, &size, &offset);
+ if (ret == 0) {
+ /* EOF */
+ break;
+ } else if (ret != 3) {
+ dev_err(dev, "Bad format: failed to parse object\n");
+ return -EINVAL;
+ }
+ data_pos += offset;
+
+ object = mxt_get_object(data, type);
+ if (!object) {
+ /* Skip object */
+ for (i = 0; i < size; i++) {
+ ret = sscanf(cfg->data + data_pos, "%hhx%n",
+ &val, &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format in T%d at %d\n",
+ type, i);
+ return -EINVAL;
+ }
+ data_pos += offset;
+ }
+ continue;
+ }
+
+ if (size > mxt_obj_size(object)) {
+ /*
+ * Either we are in fallback mode due to wrong
+ * config or config from a later fw version,
+ * or the file is corrupt or hand-edited.
+ */
+ dev_warn(dev, "Discarding %zu byte(s) in T%u\n",
+ size - mxt_obj_size(object), type);
+ } else if (mxt_obj_size(object) > size) {
+ /*
+ * If firmware is upgraded, new bytes may be added to
+ * end of objects. It is generally forward compatible
+ * to zero these bytes - previous behaviour will be
+ * retained. However this does invalidate the CRC and
+ * will force fallback mode until the configuration is
+ * updated. We warn here but do nothing else - the
+ * malloc has zeroed the entire configuration.
+ */
+ dev_warn(dev, "Zeroing %zu byte(s) in T%d\n",
+ mxt_obj_size(object) - size, type);
+ }
+
+ if (instance >= mxt_obj_instances(object)) {
+ dev_err(dev, "Object instances exceeded!\n");
+ return -EINVAL;
+ }
+
+ reg = object->start_address + mxt_obj_size(object) * instance;
+
+ for (i = 0; i < size; i++) {
+ ret = sscanf(cfg->data + data_pos, "%hhx%n",
+ &val,
+ &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format in T%d at %d\n",
+ type, i);
+ return -EINVAL;
+ }
+ data_pos += offset;
+
+ if (i > mxt_obj_size(object))
+ continue;
+
+ byte_offset = reg + i - cfg_start_ofs;
+
+ if (byte_offset >= 0 && byte_offset < config_mem_size) {
+ *(config_mem + byte_offset) = val;
+ } else {
+ dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n",
+ reg, object->type, byte_offset);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int mxt_upload_cfg_mem(struct mxt_data *data, unsigned int cfg_start,
+ u8 *config_mem, size_t config_mem_size)
+{
+ unsigned int byte_offset = 0;
+ int error;
+
+ /* Write configuration as blocks */
+ while (byte_offset < config_mem_size) {
+ unsigned int size = config_mem_size - byte_offset;
+
+ if (size > MXT_MAX_BLOCK_WRITE)
+ size = MXT_MAX_BLOCK_WRITE;
+
+ error = __mxt_write_reg(data->client,
+ cfg_start + byte_offset,
+ size, config_mem + byte_offset);
+ if (error) {
+ dev_err(&data->client->dev,
+ "Config write error, ret=%d\n", error);
+ return error;
+ }
+
+ byte_offset += size;
+ }
+
+ return 0;
+}
+
+/*
+ * mxt_update_cfg - download configuration to chip
+ *
+ * Atmel Raw Config File Format
+ *
+ * The first four lines of the raw config file contain:
+ * 1) Version
+ * 2) Chip ID Information (first 7 bytes of device memory)
+ * 3) Chip Information Block 24-bit CRC Checksum
+ * 4) Chip Configuration 24-bit CRC Checksum
+ *
+ * The rest of the file consists of one line per object instance:
+ * <TYPE> <INSTANCE> <SIZE> <CONTENTS>
+ *
+ * <TYPE> - 2-byte object type as hex
+ * <INSTANCE> - 2-byte object instance number as hex
+ * <SIZE> - 2-byte object size as hex
+ * <CONTENTS> - array of <SIZE> 1-byte hex values
+ */
+static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
+{
+ struct device *dev = &data->client->dev;
+ struct mxt_info cfg_info;
+ int ret;
+ int offset;
+ int data_pos;
+ int i;
+ int cfg_start_ofs;
+ u32 info_crc, config_crc, calculated_crc;
+ u8 *config_mem;
+ size_t config_mem_size;
+
+ mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1);
+
+ if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) {
+ dev_err(dev, "Unrecognised config file\n");
+ return -EINVAL;
+ }
+
+ data_pos = strlen(MXT_CFG_MAGIC);
+
+ /* Load information block and check */
+ for (i = 0; i < sizeof(struct mxt_info); i++) {
+ ret = sscanf(cfg->data + data_pos, "%hhx%n",
+ (unsigned char *)&cfg_info + i,
+ &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format\n");
+ return -EINVAL;
+ }
+
+ data_pos += offset;
+ }
+
+ if (cfg_info.family_id != data->info.family_id) {
+ dev_err(dev, "Family ID mismatch!\n");
+ return -EINVAL;
+ }
+
+ if (cfg_info.variant_id != data->info.variant_id) {
+ dev_err(dev, "Variant ID mismatch!\n");
+ return -EINVAL;
+ }
+
+ /* Read CRCs */
+ ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format: failed to parse Info CRC\n");
+ return -EINVAL;
+ }
+ data_pos += offset;
+
+ ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format: failed to parse Config CRC\n");
+ return -EINVAL;
+ }
+ data_pos += offset;
+
+ /*
+ * The Info Block CRC is calculated over mxt_info and the object
+ * table. If it does not match then we are trying to load the
+ * configuration from a different chip or firmware version, so
+ * the configuration CRC is invalid anyway.
+ */
+ if (info_crc == data->info_crc) {
+ if (config_crc == 0 || data->config_crc == 0) {
+ dev_info(dev, "CRC zero, attempting to apply config\n");
+ } else if (config_crc == data->config_crc) {
+ dev_dbg(dev, "Config CRC 0x%06X: OK\n",
+ data->config_crc);
+ return 0;
+ } else {
+ dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
+ data->config_crc, config_crc);
+ }
+ } else {
+ dev_warn(dev,
+ "Warning: Info CRC error - device=0x%06X file=0x%06X\n",
+ data->info_crc, info_crc);
+ }
+
+ /* Malloc memory to store configuration */
+ cfg_start_ofs = MXT_OBJECT_START +
+ data->info.object_num * sizeof(struct mxt_object) +
+ MXT_INFO_CHECKSUM_SIZE;
+ config_mem_size = data->mem_size - cfg_start_ofs;
+ config_mem = kzalloc(config_mem_size, GFP_KERNEL);
+ if (!config_mem) {
+ dev_err(dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ ret = mxt_prepare_cfg_mem(data, cfg, data_pos, cfg_start_ofs,
+ config_mem, config_mem_size);
+ if (ret)
+ goto release_mem;
+
+ /* Calculate crc of the received configs (not the raw config file) */
+ if (data->T7_address < cfg_start_ofs) {
+ dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n",
+ data->T7_address, cfg_start_ofs);
+ ret = 0;
+ goto release_mem;
+ }
+
+ calculated_crc = mxt_calculate_crc(config_mem,
+ data->T7_address - cfg_start_ofs,
+ config_mem_size);
+
+ if (config_crc > 0 && config_crc != calculated_crc)
+ dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n",
+ calculated_crc, config_crc);
+
+ ret = mxt_upload_cfg_mem(data, cfg_start_ofs,
+ config_mem, config_mem_size);
+ if (ret)
+ goto release_mem;
+
+ mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);
+
+ ret = mxt_soft_reset(data);
+ if (ret)
+ goto release_mem;
+
+ dev_info(dev, "Config successfully updated\n");
+
+release_mem:
+ kfree(config_mem);
+ return ret;
+}
+
+static int mxt_acquire_irq(struct mxt_data *data)
+{
+ int error;
+
+ enable_irq(data->irq);
+
+ error = mxt_process_messages_until_invalid(data);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static int mxt_get_info(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ struct mxt_info *info = &data->info;
+ int error;
+
+ /* Read 7-byte info block starting at address 0 */
+ error = __mxt_read_reg(client, MXT_INFO, sizeof(*info), info);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static void mxt_free_input_device(struct mxt_data *data)
+{
+ if (data->input_dev) {
+ input_unregister_device(data->input_dev);
+ data->input_dev = NULL;
+ }
+}
+
+static void mxt_free_object_table(struct mxt_data *data)
+{
+ kfree(data->object_table);
+ data->object_table = NULL;
+ kfree(data->msg_buf);
+ data->msg_buf = NULL;
+ data->T5_address = 0;
+ data->T5_msg_size = 0;
+ data->T6_reportid = 0;
+ data->T7_address = 0;
+ data->T9_reportid_min = 0;
+ data->T9_reportid_max = 0;
+ data->T19_reportid = 0;
+ data->T44_address = 0;
+ data->T100_reportid_min = 0;
+ data->T100_reportid_max = 0;
+ data->max_reportid = 0;
+}
+
+static int mxt_get_object_table(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ size_t table_size;
+ struct mxt_object *object_table;
+ int error;
+ int i;
+ u8 reportid;
+ u16 end_address;
+
+ table_size = data->info.object_num * sizeof(struct mxt_object);
+ object_table = kzalloc(table_size, GFP_KERNEL);
+ if (!object_table) {
+ dev_err(&data->client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ error = __mxt_read_reg(client, MXT_OBJECT_START, table_size,
+ object_table);
+ if (error) {
+ kfree(object_table);
+ return error;
+ }
+
+ /* Valid Report IDs start counting from 1 */
+ reportid = 1;
+ data->mem_size = 0;
+ for (i = 0; i < data->info.object_num; i++) {
+ struct mxt_object *object = object_table + i;
+ u8 min_id, max_id;
+
+ le16_to_cpus(&object->start_address);
+
+ if (object->num_report_ids) {
+ min_id = reportid;
+ reportid += object->num_report_ids *
+ mxt_obj_instances(object);
+ max_id = reportid - 1;
+ } else {
+ min_id = 0;
+ max_id = 0;
+ }
+
+ dev_dbg(&data->client->dev,
+ "T%u Start:%u Size:%zu Instances:%zu Report IDs:%u-%u\n",
+ object->type, object->start_address,
+ mxt_obj_size(object), mxt_obj_instances(object),
+ min_id, max_id);
+
+ switch (object->type) {
+ case MXT_GEN_MESSAGE_T5:
+ if (data->info.family_id == 0x80 &&
+ data->info.version < 0x20) {
+ /*
+ * On mXT224 firmware versions prior to V2.0
+ * read and discard unused CRC byte otherwise
+ * DMA reads are misaligned.
+ */
+ data->T5_msg_size = mxt_obj_size(object);
+ } else {
+ /* CRC not enabled, so skip last byte */
+ data->T5_msg_size = mxt_obj_size(object) - 1;
+ }
+ data->T5_address = object->start_address;
+ break;
+ case MXT_GEN_COMMAND_T6:
+ data->T6_reportid = min_id;
+ data->T6_address = object->start_address;
+ break;
+ case MXT_GEN_POWER_T7:
+ data->T7_address = object->start_address;
+ break;
+ case MXT_TOUCH_MULTI_T9:
+ data->multitouch = MXT_TOUCH_MULTI_T9;
+ data->T9_reportid_min = min_id;
+ data->T9_reportid_max = max_id;
+ data->num_touchids = object->num_report_ids
+ * mxt_obj_instances(object);
+ break;
+ case MXT_SPT_MESSAGECOUNT_T44:
+ data->T44_address = object->start_address;
+ break;
+ case MXT_SPT_GPIOPWM_T19:
+ data->T19_reportid = min_id;
+ break;
+ case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+ data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100;
+ data->T100_reportid_min = min_id;
+ data->T100_reportid_max = max_id;
+ /* first two report IDs reserved */
+ data->num_touchids = object->num_report_ids - 2;
+ break;
+ }
+
+ end_address = object->start_address
+ + mxt_obj_size(object) * mxt_obj_instances(object) - 1;
+
+ if (end_address >= data->mem_size)
+ data->mem_size = end_address + 1;
+ }
+
+ /* Store maximum reportid */
+ data->max_reportid = reportid;
+
+ /* If T44 exists, T5 position has to be directly after */
+ if (data->T44_address && (data->T5_address != data->T44_address + 1)) {
+ dev_err(&client->dev, "Invalid T44 position\n");
+ error = -EINVAL;
+ goto free_object_table;
+ }
+
+ data->msg_buf = kcalloc(data->max_reportid,
+ data->T5_msg_size, GFP_KERNEL);
+ if (!data->msg_buf) {
+ dev_err(&client->dev, "Failed to allocate message buffer\n");
+ error = -ENOMEM;
+ goto free_object_table;
+ }
+
+ data->object_table = object_table;
+
+ return 0;
+
+free_object_table:
+ mxt_free_object_table(data);
+ return error;
+}
+
+static int mxt_read_t9_resolution(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+ struct t9_range range;
+ unsigned char orient;
+ struct mxt_object *object;
+
+ object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
+ if (!object)
+ return -EINVAL;
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T9_RANGE,
+ sizeof(range), &range);
+ if (error)
+ return error;
+
+ le16_to_cpus(&range.x);
+ le16_to_cpus(&range.y);
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T9_ORIENT,
+ 1, &orient);
+ if (error)
+ return error;
+
+ /* Handle default values */
+ if (range.x == 0)
+ range.x = 1023;
+
+ if (range.y == 0)
+ range.y = 1023;
+
+ if (orient & MXT_T9_ORIENT_SWITCH) {
+ data->max_x = range.y;
+ data->max_y = range.x;
+ } else {
+ data->max_x = range.x;
+ data->max_y = range.y;
+ }
+
+ dev_dbg(&client->dev,
+ "Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+ return 0;
+}
+
+static int mxt_read_t100_config(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+ struct mxt_object *object;
+ u16 range_x, range_y;
+ u8 cfg, tchaux;
+ u8 aux;
+
+ object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100);
+ if (!object)
+ return -EINVAL;
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_XRANGE,
+ sizeof(range_x), &range_x);
+ if (error)
+ return error;
+
+ le16_to_cpus(&range_x);
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_YRANGE,
+ sizeof(range_y), &range_y);
+ if (error)
+ return error;
+
+ le16_to_cpus(&range_y);
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_CFG1,
+ 1, &cfg);
+ if (error)
+ return error;
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_TCHAUX,
+ 1, &tchaux);
+ if (error)
+ return error;
+
+ /* Handle default values */
+ if (range_x == 0)
+ range_x = 1023;
+
+ if (range_y == 0)
+ range_y = 1023;
+
+ if (cfg & MXT_T100_CFG_SWITCHXY) {
+ data->max_x = range_y;
+ data->max_y = range_x;
+ } else {
+ data->max_x = range_x;
+ data->max_y = range_y;
+ }
+
+ /* allocate aux bytes */
+ aux = 6;
+
+ if (tchaux & MXT_T100_TCHAUX_VECT)
+ data->t100_aux_vect = aux++;
+
+ if (tchaux & MXT_T100_TCHAUX_AMPL)
+ data->t100_aux_ampl = aux++;
+
+ if (tchaux & MXT_T100_TCHAUX_AREA)
+ data->t100_aux_area = aux++;
+
+ dev_dbg(&client->dev,
+ "T100 aux mappings vect:%u ampl:%u area:%u\n",
+ data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area);
+
+ dev_info(&client->dev,
+ "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+ return 0;
+}
+
+static int mxt_input_open(struct input_dev *dev);
+static void mxt_input_close(struct input_dev *dev);
+
+static void mxt_set_up_as_touchpad(struct input_dev *input_dev,
+ struct mxt_data *data)
+{
+ const struct mxt_platform_data *pdata = data->pdata;
+ int i;
+
+ input_dev->name = "Atmel maXTouch Touchpad";
+
+ __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+
+ input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+ MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+ MXT_PIXELS_PER_MM);
+
+ for (i = 0; i < pdata->t19_num_keys; i++)
+ if (pdata->t19_keymap[i] != KEY_RESERVED)
+ input_set_capability(input_dev, EV_KEY,
+ pdata->t19_keymap[i]);
+}
+
+static int mxt_initialize_input_device(struct mxt_data *data)
+{
+ const struct mxt_platform_data *pdata = data->pdata;
+ struct device *dev = &data->client->dev;
+ struct input_dev *input_dev;
+ int error;
+ unsigned int num_mt_slots;
+ unsigned int mt_flags = 0;
+
+ switch (data->multitouch) {
+ case MXT_TOUCH_MULTI_T9:
+ num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
+ error = mxt_read_t9_resolution(data);
+ if (error)
+ dev_warn(dev, "Failed to initialize T9 resolution\n");
+ break;
+
+ case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+ num_mt_slots = data->num_touchids;
+ error = mxt_read_t100_config(data);
+ if (error)
+ dev_warn(dev, "Failed to read T100 config\n");
+ break;
+
+ default:
+ dev_err(dev, "Invalid multitouch object\n");
+ return -EINVAL;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ input_dev->name = "Atmel maXTouch Touchscreen";
+ input_dev->phys = data->phys;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = dev;
+ input_dev->open = mxt_input_open;
+ input_dev->close = mxt_input_close;
+
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+
+ /* For single touch */
+ input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, data->max_y, 0, 0);
+
+ if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+ (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_ampl)) {
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
+ }
+
+ /* If device has buttons we assume it is a touchpad */
+ if (pdata->t19_num_keys) {
+ mxt_set_up_as_touchpad(input_dev, data);
+ mt_flags |= INPUT_MT_POINTER;
+ }
+
+ /* For multi touch */
+ error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags);
+ if (error) {
+ dev_err(dev, "Error %d initialising slots\n", error);
+ goto err_free_mem;
+ }
+
+ if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100) {
+ input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
+ 0, MT_TOOL_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_DISTANCE,
+ MXT_DISTANCE_ACTIVE_TOUCH,
+ MXT_DISTANCE_HOVERING,
+ 0, 0);
+ }
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, data->max_y, 0, 0);
+
+ if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+ (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_area)) {
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, MXT_MAX_AREA, 0, 0);
+ }
+
+ if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+ (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_ampl)) {
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, 255, 0, 0);
+ }
+
+ if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_vect) {
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+ 0, 255, 0, 0);
+ }
+
+ if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_ampl) {
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, 255, 0, 0);
+ }
+
+ if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_vect) {
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+ 0, 255, 0, 0);
+ }
+
+ input_set_drvdata(input_dev, data);
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(dev, "Error %d registering input device\n", error);
+ goto err_free_mem;
+ }
+
+ data->input_dev = input_dev;
+
+ return 0;
+
+err_free_mem:
+ input_free_device(input_dev);
+ return error;
+}
+
+static int mxt_configure_objects(struct mxt_data *data,
+ const struct firmware *cfg);
+
+static void mxt_config_cb(const struct firmware *cfg, void *ctx)
+{
+ mxt_configure_objects(ctx, cfg);
+ release_firmware(cfg);
+}
+
+static int mxt_initialize(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int recovery_attempts = 0;
+ int error;
+
+ while (1) {
+ error = mxt_get_info(data);
+ if (!error)
+ break;
+
+ /* Check bootloader state */
+ error = mxt_probe_bootloader(data, false);
+ if (error) {
+ dev_info(&client->dev, "Trying alternate bootloader address\n");
+ error = mxt_probe_bootloader(data, true);
+ if (error) {
+ /* Chip is not in appmode or bootloader mode */
+ return error;
+ }
+ }
+
+ /* OK, we are in bootloader, see if we can recover */
+ if (++recovery_attempts > 1) {
+ dev_err(&client->dev, "Could not recover from bootloader mode\n");
+ /*
+ * We can reflash from this state, so do not
+ * abort initialization.
+ */
+ data->in_bootloader = true;
+ return 0;
+ }
+
+ /* Attempt to exit bootloader into app mode */
+ mxt_send_bootloader_cmd(data, false);
+ msleep(MXT_FW_RESET_TIME);
+ }
+
+ /* Get object table information */
+ error = mxt_get_object_table(data);
+ if (error) {
+ dev_err(&client->dev, "Error %d reading object table\n", error);
+ return error;
+ }
+
+ error = mxt_acquire_irq(data);
+ if (error)
+ goto err_free_object_table;
+
+ error = reject_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME,
+ &client->dev, GFP_KERNEL, data,
+ mxt_config_cb);
+ if (error) {
+ dev_err(&client->dev, "Failed to invoke firmware loader: %d\n",
+ error);
+ goto err_free_object_table;
+ }
+
+ return 0;
+
+err_free_object_table:
+ mxt_free_object_table(data);
+ return error;
+}
+
+static int mxt_configure_objects(struct mxt_data *data,
+ const struct firmware *cfg)
+{
+ struct device *dev = &data->client->dev;
+ struct mxt_info *info = &data->info;
+ int error;
+
+ if (cfg) {
+ error = mxt_update_cfg(data, cfg);
+ if (error)
+ dev_warn(dev, "Error %d updating config\n", error);
+ }
+
+ if (data->multitouch) {
+ error = mxt_initialize_input_device(data);
+ if (error)
+ return error;
+ } else {
+ dev_warn(dev, "No touch object detected\n");
+ }
+
+ dev_info(dev,
+ "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
+ info->family_id, info->variant_id, info->version >> 4,
+ info->version & 0xf, info->build, info->object_num);
+
+ return 0;
+}
+
+/* Firmware Version is returned as Major.Minor.Build */
+static ssize_t mxt_fw_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ struct mxt_info *info = &data->info;
+ return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
+ info->version >> 4, info->version & 0xf, info->build);
+}
+
+/* Hardware Version is returned as FamilyID.VariantID */
+static ssize_t mxt_hw_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ struct mxt_info *info = &data->info;
+ return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
+ info->family_id, info->variant_id);
+}
+
+static ssize_t mxt_show_instance(char *buf, int count,
+ struct mxt_object *object, int instance,
+ const u8 *val)
+{
+ int i;
+
+ if (mxt_obj_instances(object) > 1)
+ count += scnprintf(buf + count, PAGE_SIZE - count,
+ "Instance %u\n", instance);
+
+ for (i = 0; i < mxt_obj_size(object); i++)
+ count += scnprintf(buf + count, PAGE_SIZE - count,
+ "\t[%2u]: %02x (%d)\n", i, val[i], val[i]);
+ count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+
+ return count;
+}
+
+static ssize_t mxt_object_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ struct mxt_object *object;
+ int count = 0;
+ int i, j;
+ int error;
+ u8 *obuf;
+
+ /* Pre-allocate buffer large enough to hold max sized object. */
+ obuf = kmalloc(256, GFP_KERNEL);
+ if (!obuf)
+ return -ENOMEM;
+
+ error = 0;
+ for (i = 0; i < data->info.object_num; i++) {
+ object = data->object_table + i;
+
+ if (!mxt_object_readable(object->type))
+ continue;
+
+ count += scnprintf(buf + count, PAGE_SIZE - count,
+ "T%u:\n", object->type);
+
+ for (j = 0; j < mxt_obj_instances(object); j++) {
+ u16 size = mxt_obj_size(object);
+ u16 addr = object->start_address + j * size;
+
+ error = __mxt_read_reg(data->client, addr, size, obuf);
+ if (error)
+ goto done;
+
+ count = mxt_show_instance(buf, count, object, j, obuf);
+ }
+ }
+
+done:
+ kfree(obuf);
+ return error ?: count;
+}
+
+static int mxt_check_firmware_format(struct device *dev,
+ const struct firmware *fw)
+{
+ unsigned int pos = 0;
+ char c;
+
+ while (pos < fw->size) {
+ c = *(fw->data + pos);
+
+ if (c < '0' || (c > '9' && c < 'A') || c > 'F')
+ return 0;
+
+ pos++;
+ }
+
+ /*
+ * To convert file try:
+ /*(DEBLOBBED)*/
+ dev_err(dev, "Aborting: firmware file must be in binary format\n");
+
+ return -EINVAL;
+}
+
+static int mxt_load_fw(struct device *dev, const char *fn)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ const struct firmware *fw = NULL;
+ unsigned int frame_size;
+ unsigned int pos = 0;
+ unsigned int retry = 0;
+ unsigned int frame = 0;
+ int ret;
+
+ ret = reject_firmware(&fw, fn, dev);
+ if (ret) {
+ dev_err(dev, "Unable to open firmware %s\n", fn);
+ return ret;
+ }
+
+ /* Check for incorrect enc file */
+ ret = mxt_check_firmware_format(dev, fw);
+ if (ret)
+ goto release_firmware;
+
+ if (!data->in_bootloader) {
+ /* Change to the bootloader mode */
+ data->in_bootloader = true;
+
+ ret = mxt_t6_command(data, MXT_COMMAND_RESET,
+ MXT_BOOT_VALUE, false);
+ if (ret)
+ goto release_firmware;
+
+ msleep(MXT_RESET_TIME);
+
+ /* Do not need to scan since we know family ID */
+ ret = mxt_lookup_bootloader_address(data, 0);
+ if (ret)
+ goto release_firmware;
+
+ mxt_free_input_device(data);
+ mxt_free_object_table(data);
+ } else {
+ enable_irq(data->irq);
+ }
+
+ reinit_completion(&data->bl_completion);
+
+ ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false);
+ if (ret) {
+ /* Bootloader may still be unlocked from previous attempt */
+ ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false);
+ if (ret)
+ goto disable_irq;
+ } else {
+ dev_info(dev, "Unlocking bootloader\n");
+
+ /* Unlock bootloader */
+ ret = mxt_send_bootloader_cmd(data, true);
+ if (ret)
+ goto disable_irq;
+ }
+
+ while (pos < fw->size) {
+ ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true);
+ if (ret)
+ goto disable_irq;
+
+ frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
+
+ /* Take account of CRC bytes */
+ frame_size += 2;
+
+ /* Write one frame to device */
+ ret = mxt_bootloader_write(data, fw->data + pos, frame_size);
+ if (ret)
+ goto disable_irq;
+
+ ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true);
+ if (ret) {
+ retry++;
+
+ /* Back off by 20ms per retry */
+ msleep(retry * 20);
+
+ if (retry > 20) {
+ dev_err(dev, "Retry count exceeded\n");
+ goto disable_irq;
+ }
+ } else {
+ retry = 0;
+ pos += frame_size;
+ frame++;
+ }
+
+ if (frame % 50 == 0)
+ dev_dbg(dev, "Sent %d frames, %d/%zd bytes\n",
+ frame, pos, fw->size);
+ }
+
+ /* Wait for flash. */
+ ret = mxt_wait_for_completion(data, &data->bl_completion,
+ MXT_FW_RESET_TIME);
+ if (ret)
+ goto disable_irq;
+
+ dev_dbg(dev, "Sent %d frames, %d bytes\n", frame, pos);
+
+ /*
+ * Wait for device to reset. Some bootloader versions do not assert
+ * the CHG line after bootloading has finished, so ignore potential
+ * errors.
+ */
+ mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME);
+
+ data->in_bootloader = false;
+
+disable_irq:
+ disable_irq(data->irq);
+release_firmware:
+ release_firmware(fw);
+ return ret;
+}
+
+static ssize_t mxt_update_fw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int error;
+
+ error = mxt_load_fw(dev, MXT_FW_NAME);
+ if (error) {
+ dev_err(dev, "The firmware update failed(%d)\n", error);
+ count = error;
+ } else {
+ dev_info(dev, "The firmware update succeeded\n");
+
+ error = mxt_initialize(data);
+ if (error)
+ return error;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL);
+static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store);
+
+static struct attribute *mxt_attrs[] = {
+ &dev_attr_fw_version.attr,
+ &dev_attr_hw_version.attr,
+ &dev_attr_object.attr,
+ &dev_attr_update_fw.attr,
+ NULL
+};
+
+static const struct attribute_group mxt_attr_group = {
+ .attrs = mxt_attrs,
+};
+
+static void mxt_start(struct mxt_data *data)
+{
+ /* Touch enable */
+ mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0x83);
+}
+
+static void mxt_stop(struct mxt_data *data)
+{
+ /* Touch disable */
+ mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0);
+}
+
+static int mxt_input_open(struct input_dev *dev)
+{
+ struct mxt_data *data = input_get_drvdata(dev);
+
+ mxt_start(data);
+
+ return 0;
+}
+
+static void mxt_input_close(struct input_dev *dev)
+{
+ struct mxt_data *data = input_get_drvdata(dev);
+
+ mxt_stop(data);
+}
+
+#ifdef CONFIG_OF
+static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
+{
+ struct mxt_platform_data *pdata;
+ u32 *keymap;
+ u32 keycode;
+ int proplen, i, ret;
+
+ if (!client->dev.of_node)
+ return ERR_PTR(-ENOENT);
+
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ if (of_find_property(client->dev.of_node, "linux,gpio-keymap",
+ &proplen)) {
+ pdata->t19_num_keys = proplen / sizeof(u32);
+
+ keymap = devm_kzalloc(&client->dev,
+ pdata->t19_num_keys * sizeof(keymap[0]),
+ GFP_KERNEL);
+ if (!keymap)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < pdata->t19_num_keys; i++) {
+ ret = of_property_read_u32_index(client->dev.of_node,
+ "linux,gpio-keymap", i, &keycode);
+ if (ret)
+ keycode = KEY_RESERVED;
+
+ keymap[i] = keycode;
+ }
+
+ pdata->t19_keymap = keymap;
+ }
+
+ return pdata;
+}
+#else
+static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
+{
+ return ERR_PTR(-ENOENT);
+}
+#endif
+
+#ifdef CONFIG_ACPI
+
+struct mxt_acpi_platform_data {
+ const char *hid;
+ struct mxt_platform_data pdata;
+};
+
+static unsigned int samus_touchpad_buttons[] = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ BTN_LEFT
+};
+
+static struct mxt_acpi_platform_data samus_platform_data[] = {
+ {
+ /* Touchpad */
+ .hid = "ATML0000",
+ .pdata = {
+ .t19_num_keys = ARRAY_SIZE(samus_touchpad_buttons),
+ .t19_keymap = samus_touchpad_buttons,
+ },
+ },
+ {
+ /* Touchscreen */
+ .hid = "ATML0001",
+ },
+ { }
+};
+
+static const struct dmi_system_id mxt_dmi_table[] = {
+ {
+ /* 2015 Google Pixel */
+ .ident = "Chromebook Pixel 2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Samus"),
+ },
+ .driver_data = samus_platform_data,
+ },
+ { }
+};
+
+static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
+{
+ struct acpi_device *adev;
+ const struct dmi_system_id *system_id;
+ const struct mxt_acpi_platform_data *acpi_pdata;
+
+ /*
+ * Ignore ACPI devices representing bootloader mode.
+ *
+ * This is a bit of a hack: Google Chromebook BIOS creates ACPI
+ * devices for both application and bootloader modes, but we are
+ * interested in application mode only (if device is in bootloader
+ * mode we'll end up switching into application anyway). So far
+ * application mode addresses were all above 0x40, so we'll use it
+ * as a threshold.
+ */
+ if (client->addr < 0x40)
+ return ERR_PTR(-ENXIO);
+
+ adev = ACPI_COMPANION(&client->dev);
+ if (!adev)
+ return ERR_PTR(-ENOENT);
+
+ system_id = dmi_first_match(mxt_dmi_table);
+ if (!system_id)
+ return ERR_PTR(-ENOENT);
+
+ acpi_pdata = system_id->driver_data;
+ if (!acpi_pdata)
+ return ERR_PTR(-ENOENT);
+
+ while (acpi_pdata->hid) {
+ if (!strcmp(acpi_device_hid(adev), acpi_pdata->hid))
+ return &acpi_pdata->pdata;
+
+ acpi_pdata++;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+#else
+static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
+{
+ return ERR_PTR(-ENOENT);
+}
+#endif
+
+static const struct mxt_platform_data *
+mxt_get_platform_data(struct i2c_client *client)
+{
+ const struct mxt_platform_data *pdata;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (pdata)
+ return pdata;
+
+ pdata = mxt_parse_dt(client);
+ if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
+ return pdata;
+
+ pdata = mxt_parse_acpi(client);
+ if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
+ return pdata;
+
+ dev_err(&client->dev, "No platform data specified\n");
+ return ERR_PTR(-EINVAL);
+}
+
+static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct mxt_data *data;
+ const struct mxt_platform_data *pdata;
+ int error;
+
+ pdata = mxt_get_platform_data(client);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+
+ data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
+ client->adapter->nr, client->addr);
+
+ data->client = client;
+ data->pdata = pdata;
+ data->irq = client->irq;
+ i2c_set_clientdata(client, data);
+
+ init_completion(&data->bl_completion);
+ init_completion(&data->reset_completion);
+ init_completion(&data->crc_completion);
+
+ error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
+ pdata->irqflags | IRQF_ONESHOT,
+ client->name, data);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_mem;
+ }
+
+ disable_irq(client->irq);
+
+ error = mxt_initialize(data);
+ if (error)
+ goto err_free_irq;
+
+ error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
+ if (error) {
+ dev_err(&client->dev, "Failure %d creating sysfs group\n",
+ error);
+ goto err_free_object;
+ }
+
+ return 0;
+
+err_free_object:
+ mxt_free_input_device(data);
+ mxt_free_object_table(data);
+err_free_irq:
+ free_irq(client->irq, data);
+err_free_mem:
+ kfree(data);
+ return error;
+}
+
+static int mxt_remove(struct i2c_client *client)
+{
+ struct mxt_data *data = i2c_get_clientdata(client);
+
+ sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
+ free_irq(data->irq, data);
+ mxt_free_input_device(data);
+ mxt_free_object_table(data);
+ kfree(data);
+
+ return 0;
+}
+
+static int __maybe_unused mxt_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mxt_data *data = i2c_get_clientdata(client);
+ struct input_dev *input_dev = data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ mxt_stop(data);
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static int __maybe_unused mxt_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mxt_data *data = i2c_get_clientdata(client);
+ struct input_dev *input_dev = data->input_dev;
+
+ mxt_soft_reset(data);
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ mxt_start(data);
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume);
+
+static const struct of_device_id mxt_of_match[] = {
+ { .compatible = "atmel,maxtouch", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mxt_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id mxt_acpi_id[] = {
+ { "ATML0000", 0 }, /* Touchpad */
+ { "ATML0001", 0 }, /* Touchscreen */
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, mxt_acpi_id);
+#endif
+
+static const struct i2c_device_id mxt_id[] = {
+ { "qt602240_ts", 0 },
+ { "atmel_mxt_ts", 0 },
+ { "atmel_mxt_tp", 0 },
+ { "mXT224", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mxt_id);
+
+static struct i2c_driver mxt_driver = {
+ .driver = {
+ .name = "atmel_mxt_ts",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mxt_of_match),
+ .acpi_match_table = ACPI_PTR(mxt_acpi_id),
+ .pm = &mxt_pm_ops,
+ },
+ .probe = mxt_probe,
+ .remove = mxt_remove,
+ .id_table = mxt_id,
+};
+
+module_i2c_driver(mxt_driver);
+
+/* Module information */
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c
new file mode 100644
index 000000000..40e02dd5b
--- /dev/null
+++ b/drivers/input/touchscreen/auo-pixcir-ts.c
@@ -0,0 +1,702 @@
+/*
+ * Driver for AUO in-cell touchscreens
+ *
+ * Copyright (c) 2011 Heiko Stuebner <heiko@sntech.de>
+ *
+ * loosely based on auo_touch.c from Dell Streak vendor-kernel
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/input/auo-pixcir-ts.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+/*
+ * Coordinate calculation:
+ * X1 = X1_LSB + X1_MSB*256
+ * Y1 = Y1_LSB + Y1_MSB*256
+ * X2 = X2_LSB + X2_MSB*256
+ * Y2 = Y2_LSB + Y2_MSB*256
+ */
+#define AUO_PIXCIR_REG_X1_LSB 0x00
+#define AUO_PIXCIR_REG_X1_MSB 0x01
+#define AUO_PIXCIR_REG_Y1_LSB 0x02
+#define AUO_PIXCIR_REG_Y1_MSB 0x03
+#define AUO_PIXCIR_REG_X2_LSB 0x04
+#define AUO_PIXCIR_REG_X2_MSB 0x05
+#define AUO_PIXCIR_REG_Y2_LSB 0x06
+#define AUO_PIXCIR_REG_Y2_MSB 0x07
+
+#define AUO_PIXCIR_REG_STRENGTH 0x0d
+#define AUO_PIXCIR_REG_STRENGTH_X1_LSB 0x0e
+#define AUO_PIXCIR_REG_STRENGTH_X1_MSB 0x0f
+
+#define AUO_PIXCIR_REG_RAW_DATA_X 0x2b
+#define AUO_PIXCIR_REG_RAW_DATA_Y 0x4f
+
+#define AUO_PIXCIR_REG_X_SENSITIVITY 0x6f
+#define AUO_PIXCIR_REG_Y_SENSITIVITY 0x70
+#define AUO_PIXCIR_REG_INT_SETTING 0x71
+#define AUO_PIXCIR_REG_INT_WIDTH 0x72
+#define AUO_PIXCIR_REG_POWER_MODE 0x73
+
+#define AUO_PIXCIR_REG_VERSION 0x77
+#define AUO_PIXCIR_REG_CALIBRATE 0x78
+
+#define AUO_PIXCIR_REG_TOUCHAREA_X1 0x1e
+#define AUO_PIXCIR_REG_TOUCHAREA_Y1 0x1f
+#define AUO_PIXCIR_REG_TOUCHAREA_X2 0x20
+#define AUO_PIXCIR_REG_TOUCHAREA_Y2 0x21
+
+#define AUO_PIXCIR_REG_EEPROM_CALIB_X 0x42
+#define AUO_PIXCIR_REG_EEPROM_CALIB_Y 0xad
+
+#define AUO_PIXCIR_INT_TPNUM_MASK 0xe0
+#define AUO_PIXCIR_INT_TPNUM_SHIFT 5
+#define AUO_PIXCIR_INT_RELEASE (1 << 4)
+#define AUO_PIXCIR_INT_ENABLE (1 << 3)
+#define AUO_PIXCIR_INT_POL_HIGH (1 << 2)
+#define AUO_PIXCIR_INT_MODE_MASK 0x03
+
+/*
+ * Power modes:
+ * active: scan speed 60Hz
+ * sleep: scan speed 10Hz can be auto-activated, wakeup on 1st touch
+ * deep sleep: scan speed 1Hz can only be entered or left manually.
+ */
+#define AUO_PIXCIR_POWER_ACTIVE 0x00
+#define AUO_PIXCIR_POWER_SLEEP 0x01
+#define AUO_PIXCIR_POWER_DEEP_SLEEP 0x02
+#define AUO_PIXCIR_POWER_MASK 0x03
+
+#define AUO_PIXCIR_POWER_ALLOW_SLEEP (1 << 2)
+#define AUO_PIXCIR_POWER_IDLE_TIME(ms) ((ms & 0xf) << 4)
+
+#define AUO_PIXCIR_CALIBRATE 0x03
+
+#define AUO_PIXCIR_EEPROM_CALIB_X_LEN 62
+#define AUO_PIXCIR_EEPROM_CALIB_Y_LEN 36
+
+#define AUO_PIXCIR_RAW_DATA_X_LEN 18
+#define AUO_PIXCIR_RAW_DATA_Y_LEN 11
+
+#define AUO_PIXCIR_STRENGTH_ENABLE (1 << 0)
+
+/* Touchscreen absolute values */
+#define AUO_PIXCIR_REPORT_POINTS 2
+#define AUO_PIXCIR_MAX_AREA 0xff
+#define AUO_PIXCIR_PENUP_TIMEOUT_MS 10
+
+struct auo_pixcir_ts {
+ struct i2c_client *client;
+ struct input_dev *input;
+ const struct auo_pixcir_ts_platdata *pdata;
+ char phys[32];
+
+ /* special handling for touch_indicate interupt mode */
+ bool touch_ind_mode;
+
+ wait_queue_head_t wait;
+ bool stopped;
+};
+
+struct auo_point_t {
+ int coord_x;
+ int coord_y;
+ int area_major;
+ int area_minor;
+ int orientation;
+};
+
+static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts,
+ struct auo_point_t *point)
+{
+ struct i2c_client *client = ts->client;
+ const struct auo_pixcir_ts_platdata *pdata = ts->pdata;
+ uint8_t raw_coord[8];
+ uint8_t raw_area[4];
+ int i, ret;
+
+ /* touch coordinates */
+ ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_X1_LSB,
+ 8, raw_coord);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to read coordinate, %d\n", ret);
+ return ret;
+ }
+
+ /* touch area */
+ ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_TOUCHAREA_X1,
+ 4, raw_area);
+ if (ret < 0) {
+ dev_err(&client->dev, "could not read touch area, %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
+ point[i].coord_x =
+ raw_coord[4 * i + 1] << 8 | raw_coord[4 * i];
+ point[i].coord_y =
+ raw_coord[4 * i + 3] << 8 | raw_coord[4 * i + 2];
+
+ if (point[i].coord_x > pdata->x_max ||
+ point[i].coord_y > pdata->y_max) {
+ dev_warn(&client->dev, "coordinates (%d,%d) invalid\n",
+ point[i].coord_x, point[i].coord_y);
+ point[i].coord_x = point[i].coord_y = 0;
+ }
+
+ /* determine touch major, minor and orientation */
+ point[i].area_major = max(raw_area[2 * i], raw_area[2 * i + 1]);
+ point[i].area_minor = min(raw_area[2 * i], raw_area[2 * i + 1]);
+ point[i].orientation = raw_area[2 * i] > raw_area[2 * i + 1];
+ }
+
+ return 0;
+}
+
+static irqreturn_t auo_pixcir_interrupt(int irq, void *dev_id)
+{
+ struct auo_pixcir_ts *ts = dev_id;
+ const struct auo_pixcir_ts_platdata *pdata = ts->pdata;
+ struct auo_point_t point[AUO_PIXCIR_REPORT_POINTS];
+ int i;
+ int ret;
+ int fingers = 0;
+ int abs = -1;
+
+ while (!ts->stopped) {
+
+ /* check for up event in touch touch_ind_mode */
+ if (ts->touch_ind_mode) {
+ if (gpio_get_value(pdata->gpio_int) == 0) {
+ input_mt_sync(ts->input);
+ input_report_key(ts->input, BTN_TOUCH, 0);
+ input_sync(ts->input);
+ break;
+ }
+ }
+
+ ret = auo_pixcir_collect_data(ts, point);
+ if (ret < 0) {
+ /* we want to loop only in touch_ind_mode */
+ if (!ts->touch_ind_mode)
+ break;
+
+ wait_event_timeout(ts->wait, ts->stopped,
+ msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS));
+ continue;
+ }
+
+ for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
+ if (point[i].coord_x > 0 || point[i].coord_y > 0) {
+ input_report_abs(ts->input, ABS_MT_POSITION_X,
+ point[i].coord_x);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y,
+ point[i].coord_y);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+ point[i].area_major);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+ point[i].area_minor);
+ input_report_abs(ts->input, ABS_MT_ORIENTATION,
+ point[i].orientation);
+ input_mt_sync(ts->input);
+
+ /* use first finger as source for singletouch */
+ if (fingers == 0)
+ abs = i;
+
+ /* number of touch points could also be queried
+ * via i2c but would require an additional call
+ */
+ fingers++;
+ }
+ }
+
+ input_report_key(ts->input, BTN_TOUCH, fingers > 0);
+
+ if (abs > -1) {
+ input_report_abs(ts->input, ABS_X, point[abs].coord_x);
+ input_report_abs(ts->input, ABS_Y, point[abs].coord_y);
+ }
+
+ input_sync(ts->input);
+
+ /* we want to loop only in touch_ind_mode */
+ if (!ts->touch_ind_mode)
+ break;
+
+ wait_event_timeout(ts->wait, ts->stopped,
+ msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS));
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Set the power mode of the device.
+ * Valid modes are
+ * - AUO_PIXCIR_POWER_ACTIVE
+ * - AUO_PIXCIR_POWER_SLEEP - automatically left on first touch
+ * - AUO_PIXCIR_POWER_DEEP_SLEEP
+ */
+static int auo_pixcir_power_mode(struct auo_pixcir_ts *ts, int mode)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_POWER_MODE);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to read reg %Xh, %d\n",
+ AUO_PIXCIR_REG_POWER_MODE, ret);
+ return ret;
+ }
+
+ ret &= ~AUO_PIXCIR_POWER_MASK;
+ ret |= mode;
+
+ ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_POWER_MODE, ret);
+ if (ret) {
+ dev_err(&client->dev, "unable to write reg %Xh, %d\n",
+ AUO_PIXCIR_REG_POWER_MODE, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int auo_pixcir_int_config(struct auo_pixcir_ts *ts,
+ int int_setting)
+{
+ struct i2c_client *client = ts->client;
+ const struct auo_pixcir_ts_platdata *pdata = ts->pdata;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to read reg %Xh, %d\n",
+ AUO_PIXCIR_REG_INT_SETTING, ret);
+ return ret;
+ }
+
+ ret &= ~AUO_PIXCIR_INT_MODE_MASK;
+ ret |= int_setting;
+ ret |= AUO_PIXCIR_INT_POL_HIGH; /* always use high for interrupts */
+
+ ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING,
+ ret);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to write reg %Xh, %d\n",
+ AUO_PIXCIR_REG_INT_SETTING, ret);
+ return ret;
+ }
+
+ ts->touch_ind_mode = pdata->int_setting == AUO_PIXCIR_INT_TOUCH_IND;
+
+ return 0;
+}
+
+/* control the generation of interrupts on the device side */
+static int auo_pixcir_int_toggle(struct auo_pixcir_ts *ts, bool enable)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to read reg %Xh, %d\n",
+ AUO_PIXCIR_REG_INT_SETTING, ret);
+ return ret;
+ }
+
+ if (enable)
+ ret |= AUO_PIXCIR_INT_ENABLE;
+ else
+ ret &= ~AUO_PIXCIR_INT_ENABLE;
+
+ ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING,
+ ret);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to write reg %Xh, %d\n",
+ AUO_PIXCIR_REG_INT_SETTING, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int auo_pixcir_start(struct auo_pixcir_ts *ts)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_ACTIVE);
+ if (ret < 0) {
+ dev_err(&client->dev, "could not set power mode, %d\n",
+ ret);
+ return ret;
+ }
+
+ ts->stopped = false;
+ mb();
+ enable_irq(client->irq);
+
+ ret = auo_pixcir_int_toggle(ts, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "could not enable interrupt, %d\n",
+ ret);
+ disable_irq(client->irq);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int auo_pixcir_stop(struct auo_pixcir_ts *ts)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ ret = auo_pixcir_int_toggle(ts, 0);
+ if (ret < 0) {
+ dev_err(&client->dev, "could not disable interrupt, %d\n",
+ ret);
+ return ret;
+ }
+
+ /* disable receiving of interrupts */
+ disable_irq(client->irq);
+ ts->stopped = true;
+ mb();
+ wake_up(&ts->wait);
+
+ return auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_DEEP_SLEEP);
+}
+
+static int auo_pixcir_input_open(struct input_dev *dev)
+{
+ struct auo_pixcir_ts *ts = input_get_drvdata(dev);
+ int ret;
+
+ ret = auo_pixcir_start(ts);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void auo_pixcir_input_close(struct input_dev *dev)
+{
+ struct auo_pixcir_ts *ts = input_get_drvdata(dev);
+
+ auo_pixcir_stop(ts);
+
+ return;
+}
+
+static int __maybe_unused auo_pixcir_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct auo_pixcir_ts *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+ int ret = 0;
+
+ mutex_lock(&input->mutex);
+
+ /* when configured as wakeup source, device should always wake system
+ * therefore start device if necessary
+ */
+ if (device_may_wakeup(&client->dev)) {
+ /* need to start device if not open, to be wakeup source */
+ if (!input->users) {
+ ret = auo_pixcir_start(ts);
+ if (ret)
+ goto unlock;
+ }
+
+ enable_irq_wake(client->irq);
+ ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_SLEEP);
+ } else if (input->users) {
+ ret = auo_pixcir_stop(ts);
+ }
+
+unlock:
+ mutex_unlock(&input->mutex);
+
+ return ret;
+}
+
+static int __maybe_unused auo_pixcir_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct auo_pixcir_ts *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+ int ret = 0;
+
+ mutex_lock(&input->mutex);
+
+ if (device_may_wakeup(&client->dev)) {
+ disable_irq_wake(client->irq);
+
+ /* need to stop device if it was not open on suspend */
+ if (!input->users) {
+ ret = auo_pixcir_stop(ts);
+ if (ret)
+ goto unlock;
+ }
+
+ /* device wakes automatically from SLEEP */
+ } else if (input->users) {
+ ret = auo_pixcir_start(ts);
+ }
+
+unlock:
+ mutex_unlock(&input->mutex);
+
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops,
+ auo_pixcir_suspend, auo_pixcir_resume);
+
+#ifdef CONFIG_OF
+static struct auo_pixcir_ts_platdata *auo_pixcir_parse_dt(struct device *dev)
+{
+ struct auo_pixcir_ts_platdata *pdata;
+ struct device_node *np = dev->of_node;
+
+ if (!np)
+ return ERR_PTR(-ENOENT);
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "failed to allocate platform data\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ pdata->gpio_int = of_get_gpio(np, 0);
+ if (!gpio_is_valid(pdata->gpio_int)) {
+ dev_err(dev, "failed to get interrupt gpio\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata->gpio_rst = of_get_gpio(np, 1);
+ if (!gpio_is_valid(pdata->gpio_rst)) {
+ dev_err(dev, "failed to get reset gpio\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(np, "x-size", &pdata->x_max)) {
+ dev_err(dev, "failed to get x-size property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(np, "y-size", &pdata->y_max)) {
+ dev_err(dev, "failed to get y-size property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* default to asserting the interrupt when the screen is touched */
+ pdata->int_setting = AUO_PIXCIR_INT_TOUCH_IND;
+
+ return pdata;
+}
+#else
+static struct auo_pixcir_ts_platdata *auo_pixcir_parse_dt(struct device *dev)
+{
+ return ERR_PTR(-EINVAL);
+}
+#endif
+
+static void auo_pixcir_reset(void *data)
+{
+ struct auo_pixcir_ts *ts = data;
+
+ gpio_set_value(ts->pdata->gpio_rst, 0);
+}
+
+static int auo_pixcir_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct auo_pixcir_ts_platdata *pdata;
+ struct auo_pixcir_ts *ts;
+ struct input_dev *input_dev;
+ int version;
+ int error;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata) {
+ pdata = auo_pixcir_parse_dt(&client->dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
+
+ ts = devm_kzalloc(&client->dev,
+ sizeof(struct auo_pixcir_ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!input_dev) {
+ dev_err(&client->dev, "could not allocate input device\n");
+ return -ENOMEM;
+ }
+
+ ts->pdata = pdata;
+ ts->client = client;
+ ts->input = input_dev;
+ ts->touch_ind_mode = 0;
+ ts->stopped = true;
+ init_waitqueue_head(&ts->wait);
+
+ snprintf(ts->phys, sizeof(ts->phys),
+ "%s/input0", dev_name(&client->dev));
+
+ input_dev->name = "AUO-Pixcir touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = BUS_I2C;
+
+ input_dev->open = auo_pixcir_input_open;
+ input_dev->close = auo_pixcir_input_close;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ /* For single touch */
+ input_set_abs_params(input_dev, ABS_X, 0, pdata->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, pdata->y_max, 0, 0);
+
+ /* For multi touch */
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+ pdata->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+ pdata->y_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
+ AUO_PIXCIR_MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0,
+ AUO_PIXCIR_MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+
+ input_set_drvdata(ts->input, ts);
+
+ error = devm_gpio_request_one(&client->dev, pdata->gpio_int,
+ GPIOF_DIR_IN, "auo_pixcir_ts_int");
+ if (error) {
+ dev_err(&client->dev, "request of gpio %d failed, %d\n",
+ pdata->gpio_int, error);
+ return error;
+ }
+
+ error = devm_gpio_request_one(&client->dev, pdata->gpio_rst,
+ GPIOF_DIR_OUT | GPIOF_INIT_HIGH,
+ "auo_pixcir_ts_rst");
+ if (error) {
+ dev_err(&client->dev, "request of gpio %d failed, %d\n",
+ pdata->gpio_rst, error);
+ return error;
+ }
+
+ error = devm_add_action(&client->dev, auo_pixcir_reset, ts);
+ if (error) {
+ auo_pixcir_reset(ts);
+ dev_err(&client->dev, "failed to register reset action, %d\n",
+ error);
+ return error;
+ }
+
+ msleep(200);
+
+ version = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_VERSION);
+ if (version < 0) {
+ error = version;
+ return error;
+ }
+
+ dev_info(&client->dev, "firmware version 0x%X\n", version);
+
+ error = auo_pixcir_int_config(ts, pdata->int_setting);
+ if (error)
+ return error;
+
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, auo_pixcir_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ input_dev->name, ts);
+ if (error) {
+ dev_err(&client->dev, "irq %d requested failed, %d\n",
+ client->irq, error);
+ return error;
+ }
+
+ /* stop device and put it into deep sleep until it is opened */
+ error = auo_pixcir_stop(ts);
+ if (error)
+ return error;
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&client->dev, "could not register input device, %d\n",
+ error);
+ return error;
+ }
+
+ i2c_set_clientdata(client, ts);
+
+ return 0;
+}
+
+static const struct i2c_device_id auo_pixcir_idtable[] = {
+ { "auo_pixcir_ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, auo_pixcir_idtable);
+
+#ifdef CONFIG_OF
+static const struct of_device_id auo_pixcir_ts_dt_idtable[] = {
+ { .compatible = "auo,auo_pixcir_ts" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, auo_pixcir_ts_dt_idtable);
+#endif
+
+static struct i2c_driver auo_pixcir_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "auo_pixcir_ts",
+ .pm = &auo_pixcir_pm_ops,
+ .of_match_table = of_match_ptr(auo_pixcir_ts_dt_idtable),
+ },
+ .probe = auo_pixcir_probe,
+ .id_table = auo_pixcir_idtable,
+};
+
+module_i2c_driver(auo_pixcir_driver);
+
+MODULE_DESCRIPTION("AUO-PIXCIR touchscreen driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
diff --git a/drivers/input/touchscreen/bcm_iproc_tsc.c b/drivers/input/touchscreen/bcm_iproc_tsc.c
new file mode 100644
index 000000000..ae460a5c9
--- /dev/null
+++ b/drivers/input/touchscreen/bcm_iproc_tsc.c
@@ -0,0 +1,522 @@
+/*
+* Copyright (C) 2015 Broadcom Corporation
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation version 2.
+*
+* This program is distributed "as is" WITHOUT ANY WARRANTY of any
+* kind, whether express or implied; without even the implied warranty
+* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/keyboard.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <asm/irq.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/serio.h>
+
+#define IPROC_TS_NAME "iproc-ts"
+
+#define PEN_DOWN_STATUS 1
+#define PEN_UP_STATUS 0
+
+#define X_MIN 0
+#define Y_MIN 0
+#define X_MAX 0xFFF
+#define Y_MAX 0xFFF
+
+/* Value given by controller for invalid coordinate. */
+#define INVALID_COORD 0xFFFFFFFF
+
+/* Register offsets */
+#define REGCTL1 0x00
+#define REGCTL2 0x04
+#define INTERRUPT_THRES 0x08
+#define INTERRUPT_MASK 0x0c
+
+#define INTERRUPT_STATUS 0x10
+#define CONTROLLER_STATUS 0x14
+#define FIFO_DATA 0x18
+#define FIFO_DATA_X_Y_MASK 0xFFFF
+#define ANALOG_CONTROL 0x1c
+
+#define AUX_DATA 0x20
+#define DEBOUNCE_CNTR_STAT 0x24
+#define SCAN_CNTR_STAT 0x28
+#define REM_CNTR_STAT 0x2c
+
+#define SETTLING_TIMER_STAT 0x30
+#define SPARE_REG 0x34
+#define SOFT_BYPASS_CONTROL 0x38
+#define SOFT_BYPASS_DATA 0x3c
+
+
+/* Bit values for INTERRUPT_MASK and INTERRUPT_STATUS regs */
+#define TS_PEN_INTR_MASK BIT(0)
+#define TS_FIFO_INTR_MASK BIT(2)
+
+/* Bit values for CONTROLLER_STATUS reg1 */
+#define TS_PEN_DOWN BIT(0)
+
+/* Shift values for control reg1 */
+#define SCANNING_PERIOD_SHIFT 24
+#define DEBOUNCE_TIMEOUT_SHIFT 16
+#define SETTLING_TIMEOUT_SHIFT 8
+#define TOUCH_TIMEOUT_SHIFT 0
+
+/* Shift values for coordinates from fifo */
+#define X_COORD_SHIFT 0
+#define Y_COORD_SHIFT 16
+
+/* Bit values for REGCTL2 */
+#define TS_CONTROLLER_EN_BIT BIT(16)
+#define TS_CONTROLLER_AVGDATA_SHIFT 8
+#define TS_CONTROLLER_AVGDATA_MASK (0x7 << TS_CONTROLLER_AVGDATA_SHIFT)
+#define TS_CONTROLLER_PWR_LDO BIT(5)
+#define TS_CONTROLLER_PWR_ADC BIT(4)
+#define TS_CONTROLLER_PWR_BGP BIT(3)
+#define TS_CONTROLLER_PWR_TS BIT(2)
+#define TS_WIRE_MODE_BIT BIT(1)
+
+#define dbg_reg(dev, priv, reg) \
+ dev_dbg(dev, "%20s= 0x%08x\n", #reg, readl((priv)->regs + reg))
+
+struct tsc_param {
+ /* Each step is 1024 us. Valid 1-256 */
+ u32 scanning_period;
+
+ /* Each step is 512 us. Valid 0-255 */
+ u32 debounce_timeout;
+
+ /*
+ * The settling duration (in ms) is the amount of time the tsc
+ * waits to allow the voltage to settle after turning on the
+ * drivers in detection mode. Valid values: 0-11
+ * 0 = 0.008 ms
+ * 1 = 0.01 ms
+ * 2 = 0.02 ms
+ * 3 = 0.04 ms
+ * 4 = 0.08 ms
+ * 5 = 0.16 ms
+ * 6 = 0.32 ms
+ * 7 = 0.64 ms
+ * 8 = 1.28 ms
+ * 9 = 2.56 ms
+ * 10 = 5.12 ms
+ * 11 = 10.24 ms
+ */
+ u32 settling_timeout;
+
+ /* touch timeout in sample counts */
+ u32 touch_timeout;
+
+ /*
+ * Number of data samples which are averaged before a final data point
+ * is placed into the FIFO
+ */
+ u32 average_data;
+
+ /* FIFO threshold */
+ u32 fifo_threshold;
+
+ /* Optional standard touchscreen properties. */
+ u32 max_x;
+ u32 max_y;
+ u32 fuzz_x;
+ u32 fuzz_y;
+ bool invert_x;
+ bool invert_y;
+};
+
+struct iproc_ts_priv {
+ struct platform_device *pdev;
+ struct input_dev *idev;
+
+ void __iomem *regs;
+ struct clk *tsc_clk;
+
+ int pen_status;
+ struct tsc_param cfg_params;
+};
+
+/*
+ * Set default values the same as hardware reset values
+ * except for fifo_threshold with is set to 1.
+ */
+static const struct tsc_param iproc_default_config = {
+ .scanning_period = 0x5, /* 1 to 256 */
+ .debounce_timeout = 0x28, /* 0 to 255 */
+ .settling_timeout = 0x7, /* 0 to 11 */
+ .touch_timeout = 0xa, /* 0 to 255 */
+ .average_data = 5, /* entry 5 = 32 pts */
+ .fifo_threshold = 1, /* 0 to 31 */
+ .max_x = X_MAX,
+ .max_y = Y_MAX,
+};
+
+static void ts_reg_dump(struct iproc_ts_priv *priv)
+{
+ struct device *dev = &priv->pdev->dev;
+
+ dbg_reg(dev, priv, REGCTL1);
+ dbg_reg(dev, priv, REGCTL2);
+ dbg_reg(dev, priv, INTERRUPT_THRES);
+ dbg_reg(dev, priv, INTERRUPT_MASK);
+ dbg_reg(dev, priv, INTERRUPT_STATUS);
+ dbg_reg(dev, priv, CONTROLLER_STATUS);
+ dbg_reg(dev, priv, FIFO_DATA);
+ dbg_reg(dev, priv, ANALOG_CONTROL);
+ dbg_reg(dev, priv, AUX_DATA);
+ dbg_reg(dev, priv, DEBOUNCE_CNTR_STAT);
+ dbg_reg(dev, priv, SCAN_CNTR_STAT);
+ dbg_reg(dev, priv, REM_CNTR_STAT);
+ dbg_reg(dev, priv, SETTLING_TIMER_STAT);
+ dbg_reg(dev, priv, SPARE_REG);
+ dbg_reg(dev, priv, SOFT_BYPASS_CONTROL);
+ dbg_reg(dev, priv, SOFT_BYPASS_DATA);
+}
+
+static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)
+{
+ struct platform_device *pdev = data;
+ struct iproc_ts_priv *priv = platform_get_drvdata(pdev);
+ u32 intr_status;
+ u32 raw_coordinate;
+ u16 x;
+ u16 y;
+ int i;
+ bool needs_sync = false;
+
+ intr_status = readl(priv->regs + INTERRUPT_STATUS);
+ intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
+ if (intr_status == 0)
+ return IRQ_NONE;
+
+ /* Clear all interrupt status bits, write-1-clear */
+ writel(intr_status, priv->regs + INTERRUPT_STATUS);
+
+ /* Pen up/down */
+ if (intr_status & TS_PEN_INTR_MASK) {
+ if (readl(priv->regs + CONTROLLER_STATUS) & TS_PEN_DOWN)
+ priv->pen_status = PEN_DOWN_STATUS;
+ else
+ priv->pen_status = PEN_UP_STATUS;
+
+ input_report_key(priv->idev, BTN_TOUCH, priv->pen_status);
+ needs_sync = true;
+
+ dev_dbg(&priv->pdev->dev,
+ "pen up-down (%d)\n", priv->pen_status);
+ }
+
+ /* coordinates in FIFO exceed the theshold */
+ if (intr_status & TS_FIFO_INTR_MASK) {
+ for (i = 0; i < priv->cfg_params.fifo_threshold; i++) {
+ raw_coordinate = readl(priv->regs + FIFO_DATA);
+ if (raw_coordinate == INVALID_COORD)
+ continue;
+
+ /*
+ * The x and y coordinate are 16 bits each
+ * with the x in the lower 16 bits and y in the
+ * upper 16 bits.
+ */
+ x = (raw_coordinate >> X_COORD_SHIFT) &
+ FIFO_DATA_X_Y_MASK;
+ y = (raw_coordinate >> Y_COORD_SHIFT) &
+ FIFO_DATA_X_Y_MASK;
+
+ /* We only want to retain the 12 msb of the 16 */
+ x = (x >> 4) & 0x0FFF;
+ y = (y >> 4) & 0x0FFF;
+
+ /* adjust x y according to lcd tsc mount angle */
+ if (priv->cfg_params.invert_x)
+ x = priv->cfg_params.max_x - x;
+
+ if (priv->cfg_params.invert_y)
+ y = priv->cfg_params.max_y - y;
+
+ input_report_abs(priv->idev, ABS_X, x);
+ input_report_abs(priv->idev, ABS_Y, y);
+ needs_sync = true;
+
+ dev_dbg(&priv->pdev->dev, "xy (0x%x 0x%x)\n", x, y);
+ }
+ }
+
+ if (needs_sync)
+ input_sync(priv->idev);
+
+ return IRQ_HANDLED;
+}
+
+static int iproc_ts_start(struct input_dev *idev)
+{
+ struct iproc_ts_priv *priv = input_get_drvdata(idev);
+ u32 val;
+ int error;
+
+ /* Enable clock */
+ error = clk_prepare_enable(priv->tsc_clk);
+ if (error) {
+ dev_err(&priv->pdev->dev, "%s clk_prepare_enable failed %d\n",
+ __func__, error);
+ return error;
+ }
+
+ /*
+ * Interrupt is generated when:
+ * FIFO reaches the int_th value, and pen event(up/down)
+ */
+ val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
+ writel(val, priv->regs + INTERRUPT_MASK);
+
+ writel(priv->cfg_params.fifo_threshold, priv->regs + INTERRUPT_THRES);
+
+ /* Initialize control reg1 */
+ val = 0;
+ val |= priv->cfg_params.scanning_period << SCANNING_PERIOD_SHIFT;
+ val |= priv->cfg_params.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT;
+ val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT;
+ val |= priv->cfg_params.touch_timeout << TOUCH_TIMEOUT_SHIFT;
+ writel(val, priv->regs + REGCTL1);
+
+ /* Try to clear all interrupt status */
+ val = readl(priv->regs + INTERRUPT_STATUS);
+ val |= TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK;
+ writel(val, priv->regs + INTERRUPT_STATUS);
+
+ /* Initialize control reg2 */
+ val = readl(priv->regs + REGCTL2);
+ val |= TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT;
+
+ val &= ~TS_CONTROLLER_AVGDATA_MASK;
+ val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT;
+
+ val &= ~(TS_CONTROLLER_PWR_LDO | /* PWR up LDO */
+ TS_CONTROLLER_PWR_ADC | /* PWR up ADC */
+ TS_CONTROLLER_PWR_BGP | /* PWR up BGP */
+ TS_CONTROLLER_PWR_TS); /* PWR up TS */
+
+ writel(val, priv->regs + REGCTL2);
+
+ ts_reg_dump(priv);
+
+ return 0;
+}
+
+static void iproc_ts_stop(struct input_dev *dev)
+{
+ u32 val;
+ struct iproc_ts_priv *priv = input_get_drvdata(dev);
+
+ writel(0, priv->regs + INTERRUPT_MASK); /* Disable all interrupts */
+
+ /* Only power down touch screen controller */
+ val = readl(priv->regs + REGCTL2);
+ val |= TS_CONTROLLER_PWR_TS;
+ writel(val, priv->regs + REGCTL2);
+
+ clk_disable(priv->tsc_clk);
+}
+
+static int iproc_get_tsc_config(struct device *dev, struct iproc_ts_priv *priv)
+{
+ struct device_node *np = dev->of_node;
+ u32 val;
+
+ priv->cfg_params = iproc_default_config;
+
+ if (!np)
+ return 0;
+
+ if (of_property_read_u32(np, "scanning_period", &val) >= 0) {
+ if (val < 1 || val > 256) {
+ dev_err(dev, "scanning_period (%u) must be [1-256]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.scanning_period = val;
+ }
+
+ if (of_property_read_u32(np, "debounce_timeout", &val) >= 0) {
+ if (val > 255) {
+ dev_err(dev, "debounce_timeout (%u) must be [0-255]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.debounce_timeout = val;
+ }
+
+ if (of_property_read_u32(np, "settling_timeout", &val) >= 0) {
+ if (val > 11) {
+ dev_err(dev, "settling_timeout (%u) must be [0-11]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.settling_timeout = val;
+ }
+
+ if (of_property_read_u32(np, "touch_timeout", &val) >= 0) {
+ if (val > 255) {
+ dev_err(dev, "touch_timeout (%u) must be [0-255]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.touch_timeout = val;
+ }
+
+ if (of_property_read_u32(np, "average_data", &val) >= 0) {
+ if (val > 8) {
+ dev_err(dev, "average_data (%u) must be [0-8]\n", val);
+ return -EINVAL;
+ }
+ priv->cfg_params.average_data = val;
+ }
+
+ if (of_property_read_u32(np, "fifo_threshold", &val) >= 0) {
+ if (val > 31) {
+ dev_err(dev, "fifo_threshold (%u)) must be [0-31]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.fifo_threshold = val;
+ }
+
+ /* Parse optional properties. */
+ of_property_read_u32(np, "touchscreen-size-x", &priv->cfg_params.max_x);
+ of_property_read_u32(np, "touchscreen-size-y", &priv->cfg_params.max_y);
+
+ of_property_read_u32(np, "touchscreen-fuzz-x",
+ &priv->cfg_params.fuzz_x);
+ of_property_read_u32(np, "touchscreen-fuzz-y",
+ &priv->cfg_params.fuzz_y);
+
+ priv->cfg_params.invert_x =
+ of_property_read_bool(np, "touchscreen-inverted-x");
+ priv->cfg_params.invert_y =
+ of_property_read_bool(np, "touchscreen-inverted-y");
+
+ return 0;
+}
+
+static int iproc_ts_probe(struct platform_device *pdev)
+{
+ struct iproc_ts_priv *priv;
+ struct input_dev *idev;
+ struct resource *res;
+ int irq;
+ int error;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* touchscreen controller memory mapped regs */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->regs)) {
+ error = PTR_ERR(priv->regs);
+ dev_err(&pdev->dev, "unable to map I/O memory: %d\n", error);
+ return error;
+ }
+
+ priv->tsc_clk = devm_clk_get(&pdev->dev, "tsc_clk");
+ if (IS_ERR(priv->tsc_clk)) {
+ error = PTR_ERR(priv->tsc_clk);
+ dev_err(&pdev->dev,
+ "failed getting clock tsc_clk: %d\n", error);
+ return error;
+ }
+
+ priv->pdev = pdev;
+ error = iproc_get_tsc_config(&pdev->dev, priv);
+ if (error) {
+ dev_err(&pdev->dev, "get_tsc_config failed: %d\n", error);
+ return error;
+ }
+
+ idev = devm_input_allocate_device(&pdev->dev);
+ if (!idev) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ priv->idev = idev;
+ priv->pen_status = PEN_UP_STATUS;
+
+ /* Set input device info */
+ idev->name = IPROC_TS_NAME;
+ idev->dev.parent = &pdev->dev;
+
+ idev->id.bustype = BUS_HOST;
+ idev->id.vendor = SERIO_UNKNOWN;
+ idev->id.product = 0;
+ idev->id.version = 0;
+
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ __set_bit(BTN_TOUCH, idev->keybit);
+
+ input_set_abs_params(idev, ABS_X, X_MIN, priv->cfg_params.max_x,
+ priv->cfg_params.fuzz_x, 0);
+ input_set_abs_params(idev, ABS_Y, Y_MIN, priv->cfg_params.max_y,
+ priv->cfg_params.fuzz_y, 0);
+
+ idev->open = iproc_ts_start;
+ idev->close = iproc_ts_stop;
+
+ input_set_drvdata(idev, priv);
+ platform_set_drvdata(pdev, priv);
+
+ /* get interrupt */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "platform_get_irq failed: %d\n", irq);
+ return irq;
+ }
+
+ error = devm_request_irq(&pdev->dev, irq,
+ iproc_touchscreen_interrupt,
+ IRQF_SHARED, IPROC_TS_NAME, pdev);
+ if (error)
+ return error;
+
+ error = input_register_device(priv->idev);
+ if (error) {
+ dev_err(&pdev->dev,
+ "failed to register input device: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id iproc_ts_of_match[] = {
+ {.compatible = "brcm,iproc-touchscreen", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, iproc_ts_of_match);
+
+static struct platform_driver iproc_ts_driver = {
+ .probe = iproc_ts_probe,
+ .driver = {
+ .name = IPROC_TS_NAME,
+ .of_match_table = of_match_ptr(iproc_ts_of_match),
+ },
+};
+
+module_platform_driver(iproc_ts_driver);
+
+MODULE_DESCRIPTION("IPROC Touchscreen driver");
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c
new file mode 100644
index 000000000..b9b5ddad6
--- /dev/null
+++ b/drivers/input/touchscreen/bu21013_ts.c
@@ -0,0 +1,733 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * License terms:GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/input.h>
+#include <linux/input/bu21013.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#define PEN_DOWN_INTR 0
+#define MAX_FINGERS 2
+#define RESET_DELAY 30
+#define PENUP_TIMEOUT (10)
+#define DELTA_MIN 16
+#define MASK_BITS 0x03
+#define SHIFT_8 8
+#define SHIFT_2 2
+#define LENGTH_OF_BUFFER 11
+#define I2C_RETRY_COUNT 5
+
+#define BU21013_SENSORS_BTN_0_7_REG 0x70
+#define BU21013_SENSORS_BTN_8_15_REG 0x71
+#define BU21013_SENSORS_BTN_16_23_REG 0x72
+#define BU21013_X1_POS_MSB_REG 0x73
+#define BU21013_X1_POS_LSB_REG 0x74
+#define BU21013_Y1_POS_MSB_REG 0x75
+#define BU21013_Y1_POS_LSB_REG 0x76
+#define BU21013_X2_POS_MSB_REG 0x77
+#define BU21013_X2_POS_LSB_REG 0x78
+#define BU21013_Y2_POS_MSB_REG 0x79
+#define BU21013_Y2_POS_LSB_REG 0x7A
+#define BU21013_INT_CLR_REG 0xE8
+#define BU21013_INT_MODE_REG 0xE9
+#define BU21013_GAIN_REG 0xEA
+#define BU21013_OFFSET_MODE_REG 0xEB
+#define BU21013_XY_EDGE_REG 0xEC
+#define BU21013_RESET_REG 0xED
+#define BU21013_CALIB_REG 0xEE
+#define BU21013_DONE_REG 0xEF
+#define BU21013_SENSOR_0_7_REG 0xF0
+#define BU21013_SENSOR_8_15_REG 0xF1
+#define BU21013_SENSOR_16_23_REG 0xF2
+#define BU21013_POS_MODE1_REG 0xF3
+#define BU21013_POS_MODE2_REG 0xF4
+#define BU21013_CLK_MODE_REG 0xF5
+#define BU21013_IDLE_REG 0xFA
+#define BU21013_FILTER_REG 0xFB
+#define BU21013_TH_ON_REG 0xFC
+#define BU21013_TH_OFF_REG 0xFD
+
+
+#define BU21013_RESET_ENABLE 0x01
+
+#define BU21013_SENSORS_EN_0_7 0x3F
+#define BU21013_SENSORS_EN_8_15 0xFC
+#define BU21013_SENSORS_EN_16_23 0x1F
+
+#define BU21013_POS_MODE1_0 0x02
+#define BU21013_POS_MODE1_1 0x04
+#define BU21013_POS_MODE1_2 0x08
+
+#define BU21013_POS_MODE2_ZERO 0x01
+#define BU21013_POS_MODE2_AVG1 0x02
+#define BU21013_POS_MODE2_AVG2 0x04
+#define BU21013_POS_MODE2_EN_XY 0x08
+#define BU21013_POS_MODE2_EN_RAW 0x10
+#define BU21013_POS_MODE2_MULTI 0x80
+
+#define BU21013_CLK_MODE_DIV 0x01
+#define BU21013_CLK_MODE_EXT 0x02
+#define BU21013_CLK_MODE_CALIB 0x80
+
+#define BU21013_IDLET_0 0x01
+#define BU21013_IDLET_1 0x02
+#define BU21013_IDLET_2 0x04
+#define BU21013_IDLET_3 0x08
+#define BU21013_IDLE_INTERMIT_EN 0x10
+
+#define BU21013_DELTA_0_6 0x7F
+#define BU21013_FILTER_EN 0x80
+
+#define BU21013_INT_MODE_LEVEL 0x00
+#define BU21013_INT_MODE_EDGE 0x01
+
+#define BU21013_GAIN_0 0x01
+#define BU21013_GAIN_1 0x02
+#define BU21013_GAIN_2 0x04
+
+#define BU21013_OFFSET_MODE_DEFAULT 0x00
+#define BU21013_OFFSET_MODE_MOVE 0x01
+#define BU21013_OFFSET_MODE_DISABLE 0x02
+
+#define BU21013_TH_ON_0 0x01
+#define BU21013_TH_ON_1 0x02
+#define BU21013_TH_ON_2 0x04
+#define BU21013_TH_ON_3 0x08
+#define BU21013_TH_ON_4 0x10
+#define BU21013_TH_ON_5 0x20
+#define BU21013_TH_ON_6 0x40
+#define BU21013_TH_ON_7 0x80
+#define BU21013_TH_ON_MAX 0xFF
+
+#define BU21013_TH_OFF_0 0x01
+#define BU21013_TH_OFF_1 0x02
+#define BU21013_TH_OFF_2 0x04
+#define BU21013_TH_OFF_3 0x08
+#define BU21013_TH_OFF_4 0x10
+#define BU21013_TH_OFF_5 0x20
+#define BU21013_TH_OFF_6 0x40
+#define BU21013_TH_OFF_7 0x80
+#define BU21013_TH_OFF_MAX 0xFF
+
+#define BU21013_X_EDGE_0 0x01
+#define BU21013_X_EDGE_1 0x02
+#define BU21013_X_EDGE_2 0x04
+#define BU21013_X_EDGE_3 0x08
+#define BU21013_Y_EDGE_0 0x10
+#define BU21013_Y_EDGE_1 0x20
+#define BU21013_Y_EDGE_2 0x40
+#define BU21013_Y_EDGE_3 0x80
+
+#define BU21013_DONE 0x01
+#define BU21013_NUMBER_OF_X_SENSORS (6)
+#define BU21013_NUMBER_OF_Y_SENSORS (11)
+
+#define DRIVER_TP "bu21013_tp"
+
+/**
+ * struct bu21013_ts_data - touch panel data structure
+ * @client: pointer to the i2c client
+ * @wait: variable to wait_queue_head_t structure
+ * @touch_stopped: touch stop flag
+ * @chip: pointer to the touch panel controller
+ * @in_dev: pointer to the input device structure
+ * @intr_pin: interrupt pin value
+ * @regulator: pointer to the Regulator used for touch screen
+ *
+ * Touch panel device data structure
+ */
+struct bu21013_ts_data {
+ struct i2c_client *client;
+ wait_queue_head_t wait;
+ const struct bu21013_platform_device *chip;
+ struct input_dev *in_dev;
+ struct regulator *regulator;
+ unsigned int irq;
+ unsigned int intr_pin;
+ bool touch_stopped;
+};
+
+/**
+ * bu21013_read_block_data(): read the touch co-ordinates
+ * @data: bu21013_ts_data structure pointer
+ * @buf: byte pointer
+ *
+ * Read the touch co-ordinates using i2c read block into buffer
+ * and returns integer.
+ */
+static int bu21013_read_block_data(struct bu21013_ts_data *data, u8 *buf)
+{
+ int ret, i;
+
+ for (i = 0; i < I2C_RETRY_COUNT; i++) {
+ ret = i2c_smbus_read_i2c_block_data
+ (data->client, BU21013_SENSORS_BTN_0_7_REG,
+ LENGTH_OF_BUFFER, buf);
+ if (ret == LENGTH_OF_BUFFER)
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/**
+ * bu21013_do_touch_report(): Get the touch co-ordinates
+ * @data: bu21013_ts_data structure pointer
+ *
+ * Get the touch co-ordinates from touch sensor registers and writes
+ * into device structure and returns integer.
+ */
+static int bu21013_do_touch_report(struct bu21013_ts_data *data)
+{
+ u8 buf[LENGTH_OF_BUFFER];
+ unsigned int pos_x[2], pos_y[2];
+ bool has_x_sensors, has_y_sensors;
+ int finger_down_count = 0;
+ int i;
+
+ if (data == NULL)
+ return -EINVAL;
+
+ if (bu21013_read_block_data(data, buf) < 0)
+ return -EINVAL;
+
+ has_x_sensors = hweight32(buf[0] & BU21013_SENSORS_EN_0_7);
+ has_y_sensors = hweight32(((buf[1] & BU21013_SENSORS_EN_8_15) |
+ ((buf[2] & BU21013_SENSORS_EN_16_23) << SHIFT_8)) >> SHIFT_2);
+ if (!has_x_sensors || !has_y_sensors)
+ return 0;
+
+ for (i = 0; i < MAX_FINGERS; i++) {
+ const u8 *p = &buf[4 * i + 3];
+ unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS);
+ unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS);
+ if (x == 0 || y == 0)
+ continue;
+ pos_x[finger_down_count] = x;
+ pos_y[finger_down_count] = y;
+ finger_down_count++;
+ }
+
+ if (finger_down_count) {
+ if (finger_down_count == 2 &&
+ (abs(pos_x[0] - pos_x[1]) < DELTA_MIN ||
+ abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) {
+ return 0;
+ }
+
+ for (i = 0; i < finger_down_count; i++) {
+ if (data->chip->x_flip)
+ pos_x[i] = data->chip->touch_x_max - pos_x[i];
+ if (data->chip->y_flip)
+ pos_y[i] = data->chip->touch_y_max - pos_y[i];
+
+ input_report_abs(data->in_dev,
+ ABS_MT_POSITION_X, pos_x[i]);
+ input_report_abs(data->in_dev,
+ ABS_MT_POSITION_Y, pos_y[i]);
+ input_mt_sync(data->in_dev);
+ }
+ } else
+ input_mt_sync(data->in_dev);
+
+ input_sync(data->in_dev);
+
+ return 0;
+}
+/**
+ * bu21013_gpio_irq() - gpio thread function for touch interrupt
+ * @irq: irq value
+ * @device_data: void pointer
+ *
+ * This gpio thread function for touch interrupt
+ * and returns irqreturn_t.
+ */
+static irqreturn_t bu21013_gpio_irq(int irq, void *device_data)
+{
+ struct bu21013_ts_data *data = device_data;
+ struct i2c_client *i2c = data->client;
+ int retval;
+
+ do {
+ retval = bu21013_do_touch_report(data);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "bu21013_do_touch_report failed\n");
+ return IRQ_NONE;
+ }
+
+ data->intr_pin = gpio_get_value(data->chip->touch_pin);
+ if (data->intr_pin == PEN_DOWN_INTR)
+ wait_event_timeout(data->wait, data->touch_stopped,
+ msecs_to_jiffies(2));
+ } while (!data->intr_pin && !data->touch_stopped);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * bu21013_init_chip() - power on sequence for the bu21013 controller
+ * @data: device structure pointer
+ *
+ * This function is used to power on
+ * the bu21013 controller and returns integer.
+ */
+static int bu21013_init_chip(struct bu21013_ts_data *data)
+{
+ int retval;
+ struct i2c_client *i2c = data->client;
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_RESET_REG,
+ BU21013_RESET_ENABLE);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_RESET reg write failed\n");
+ return retval;
+ }
+ msleep(RESET_DELAY);
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_0_7_REG,
+ BU21013_SENSORS_EN_0_7);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG,
+ BU21013_SENSORS_EN_8_15);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG,
+ BU21013_SENSORS_EN_16_23);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG,
+ (BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG,
+ (BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 |
+ BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW |
+ BU21013_POS_MODE2_MULTI));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n");
+ return retval;
+ }
+
+ if (data->chip->ext_clk)
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
+ (BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB));
+ else
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
+ (BU21013_CLK_MODE_DIV | BU21013_CLK_MODE_CALIB));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG,
+ (BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG,
+ BU21013_INT_MODE_LEVEL);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG,
+ (BU21013_DELTA_0_6 |
+ BU21013_FILTER_EN));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_FILTER reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_ON_REG,
+ BU21013_TH_ON_5);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG,
+ BU21013_TH_OFF_4 | BU21013_TH_OFF_3);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG,
+ (BU21013_GAIN_0 | BU21013_GAIN_1));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_GAIN reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_OFFSET_MODE_REG,
+ BU21013_OFFSET_MODE_DEFAULT);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG,
+ (BU21013_X_EDGE_0 | BU21013_X_EDGE_2 |
+ BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG,
+ BU21013_DONE);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_REG_DONE reg write failed\n");
+ return retval;
+ }
+
+ return 0;
+}
+
+/**
+ * bu21013_free_irq() - frees IRQ registered for touchscreen
+ * @bu21013_data: device structure pointer
+ *
+ * This function signals interrupt thread to stop processing and
+ * frees interrupt.
+ */
+static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data)
+{
+ bu21013_data->touch_stopped = true;
+ wake_up(&bu21013_data->wait);
+ free_irq(bu21013_data->irq, bu21013_data);
+}
+
+/**
+ * bu21013_cs_disable() - deconfigures the touch panel controller
+ * @bu21013_data: device structure pointer
+ *
+ * This function is used to deconfigure the chip selection
+ * for touch panel controller.
+ */
+static void bu21013_cs_disable(struct bu21013_ts_data *bu21013_data)
+{
+ int error;
+
+ error = gpio_direction_output(bu21013_data->chip->cs_pin, 0);
+ if (error < 0)
+ dev_warn(&bu21013_data->client->dev,
+ "%s: gpio direction failed, error: %d\n",
+ __func__, error);
+ else
+ gpio_set_value(bu21013_data->chip->cs_pin, 0);
+
+ gpio_free(bu21013_data->chip->cs_pin);
+}
+
+#ifdef CONFIG_OF
+static const struct bu21013_platform_device *
+bu21013_parse_dt(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct bu21013_platform_device *pdata;
+
+ if (!np) {
+ dev_err(dev, "no device tree or platform data\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->y_flip = pdata->x_flip = false;
+
+ pdata->x_flip = of_property_read_bool(np, "rohm,flip-x");
+ pdata->y_flip = of_property_read_bool(np, "rohm,flip-y");
+
+ of_property_read_u32(np, "rohm,touch-max-x", &pdata->touch_x_max);
+ of_property_read_u32(np, "rohm,touch-max-y", &pdata->touch_y_max);
+
+ pdata->touch_pin = of_get_named_gpio(np, "touch-gpio", 0);
+ pdata->cs_pin = of_get_named_gpio(np, "reset-gpio", 0);
+
+ pdata->ext_clk = false;
+
+ return pdata;
+}
+#else
+static inline const struct bu21013_platform_device *
+bu21013_parse_dt(struct device *dev)
+{
+ dev_err(dev, "no platform data available\n");
+ return ERR_PTR(-EINVAL);
+}
+#endif
+
+/**
+ * bu21013_probe() - initializes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ * @id: i2c device id pointer
+ *
+ * This function used to initializes the i2c-client touchscreen
+ * driver and returns integer.
+ */
+static int bu21013_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct bu21013_platform_device *pdata =
+ dev_get_platdata(&client->dev);
+ struct bu21013_ts_data *bu21013_data;
+ struct input_dev *in_dev;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "i2c smbus byte data not supported\n");
+ return -EIO;
+ }
+
+ if (!pdata) {
+ pdata = bu21013_parse_dt(&client->dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
+
+ if (!gpio_is_valid(pdata->touch_pin)) {
+ dev_err(&client->dev, "invalid touch_pin supplied\n");
+ return -EINVAL;
+ }
+
+ bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL);
+ in_dev = input_allocate_device();
+ if (!bu21013_data || !in_dev) {
+ dev_err(&client->dev, "device memory alloc failed\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ bu21013_data->in_dev = in_dev;
+ bu21013_data->chip = pdata;
+ bu21013_data->client = client;
+ bu21013_data->irq = gpio_to_irq(pdata->touch_pin);
+
+ bu21013_data->regulator = regulator_get(&client->dev, "avdd");
+ if (IS_ERR(bu21013_data->regulator)) {
+ dev_err(&client->dev, "regulator_get failed\n");
+ error = PTR_ERR(bu21013_data->regulator);
+ goto err_free_mem;
+ }
+
+ error = regulator_enable(bu21013_data->regulator);
+ if (error < 0) {
+ dev_err(&client->dev, "regulator enable failed\n");
+ goto err_put_regulator;
+ }
+
+ bu21013_data->touch_stopped = false;
+ init_waitqueue_head(&bu21013_data->wait);
+
+ /* configure the gpio pins */
+ error = gpio_request_one(pdata->cs_pin, GPIOF_OUT_INIT_HIGH,
+ "touchp_reset");
+ if (error < 0) {
+ dev_err(&client->dev, "Unable to request gpio reset_pin\n");
+ goto err_disable_regulator;
+ }
+
+ /* configure the touch panel controller */
+ error = bu21013_init_chip(bu21013_data);
+ if (error) {
+ dev_err(&client->dev, "error in bu21013 config\n");
+ goto err_cs_disable;
+ }
+
+ /* register the device to input subsystem */
+ in_dev->name = DRIVER_TP;
+ in_dev->id.bustype = BUS_I2C;
+ in_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_SYN, in_dev->evbit);
+ __set_bit(EV_KEY, in_dev->evbit);
+ __set_bit(EV_ABS, in_dev->evbit);
+
+ input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0,
+ pdata->touch_x_max, 0, 0);
+ input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0,
+ pdata->touch_y_max, 0, 0);
+ input_set_drvdata(in_dev, bu21013_data);
+
+ error = request_threaded_irq(bu21013_data->irq, NULL, bu21013_gpio_irq,
+ IRQF_TRIGGER_FALLING | IRQF_SHARED |
+ IRQF_ONESHOT,
+ DRIVER_TP, bu21013_data);
+ if (error) {
+ dev_err(&client->dev, "request irq %d failed\n",
+ bu21013_data->irq);
+ goto err_cs_disable;
+ }
+
+ error = input_register_device(in_dev);
+ if (error) {
+ dev_err(&client->dev, "failed to register input device\n");
+ goto err_free_irq;
+ }
+
+ device_init_wakeup(&client->dev, pdata->wakeup);
+ i2c_set_clientdata(client, bu21013_data);
+
+ return 0;
+
+err_free_irq:
+ bu21013_free_irq(bu21013_data);
+err_cs_disable:
+ bu21013_cs_disable(bu21013_data);
+err_disable_regulator:
+ regulator_disable(bu21013_data->regulator);
+err_put_regulator:
+ regulator_put(bu21013_data->regulator);
+err_free_mem:
+ input_free_device(in_dev);
+ kfree(bu21013_data);
+
+ return error;
+}
+/**
+ * bu21013_remove() - removes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ *
+ * This function uses to remove the i2c-client
+ * touchscreen driver and returns integer.
+ */
+static int bu21013_remove(struct i2c_client *client)
+{
+ struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client);
+
+ bu21013_free_irq(bu21013_data);
+
+ bu21013_cs_disable(bu21013_data);
+
+ input_unregister_device(bu21013_data->in_dev);
+
+ regulator_disable(bu21013_data->regulator);
+ regulator_put(bu21013_data->regulator);
+
+ kfree(bu21013_data);
+
+ device_init_wakeup(&client->dev, false);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ * bu21013_suspend() - suspend the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to suspend the
+ * touch panel controller and returns integer
+ */
+static int bu21013_suspend(struct device *dev)
+{
+ struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
+ struct i2c_client *client = bu21013_data->client;
+
+ bu21013_data->touch_stopped = true;
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(bu21013_data->irq);
+ else
+ disable_irq(bu21013_data->irq);
+
+ regulator_disable(bu21013_data->regulator);
+
+ return 0;
+}
+
+/**
+ * bu21013_resume() - resume the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to resume the touch panel
+ * controller and returns integer.
+ */
+static int bu21013_resume(struct device *dev)
+{
+ struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
+ struct i2c_client *client = bu21013_data->client;
+ int retval;
+
+ retval = regulator_enable(bu21013_data->regulator);
+ if (retval < 0) {
+ dev_err(&client->dev, "bu21013 regulator enable failed\n");
+ return retval;
+ }
+
+ retval = bu21013_init_chip(bu21013_data);
+ if (retval < 0) {
+ dev_err(&client->dev, "bu21013 controller config failed\n");
+ return retval;
+ }
+
+ bu21013_data->touch_stopped = false;
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(bu21013_data->irq);
+ else
+ enable_irq(bu21013_data->irq);
+
+ return 0;
+}
+
+static const struct dev_pm_ops bu21013_dev_pm_ops = {
+ .suspend = bu21013_suspend,
+ .resume = bu21013_resume,
+};
+#endif
+
+static const struct i2c_device_id bu21013_id[] = {
+ { DRIVER_TP, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bu21013_id);
+
+static struct i2c_driver bu21013_driver = {
+ .driver = {
+ .name = DRIVER_TP,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &bu21013_dev_pm_ops,
+#endif
+ },
+ .probe = bu21013_probe,
+ .remove = bu21013_remove,
+ .id_table = bu21013_id,
+};
+
+module_i2c_driver(bu21013_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Naveen Kumar G <naveen.gaddipati@stericsson.com>");
+MODULE_DESCRIPTION("bu21013 touch screen controller driver");
diff --git a/drivers/input/touchscreen/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c
new file mode 100644
index 000000000..32e9db0e0
--- /dev/null
+++ b/drivers/input/touchscreen/chipone_icn8318.c
@@ -0,0 +1,316 @@
+/*
+ * Driver for ChipOne icn8318 i2c touchscreen controller
+ *
+ * Copyright (c) 2015 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Red Hat authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#define ICN8318_REG_POWER 4
+#define ICN8318_REG_TOUCHDATA 16
+
+#define ICN8318_POWER_ACTIVE 0
+#define ICN8318_POWER_MONITOR 1
+#define ICN8318_POWER_HIBERNATE 2
+
+#define ICN8318_MAX_TOUCHES 5
+
+struct icn8318_touch {
+ __u8 slot;
+ __be16 x;
+ __be16 y;
+ __u8 pressure; /* Seems more like finger width then pressure really */
+ __u8 event;
+/* The difference between 2 and 3 is unclear */
+#define ICN8318_EVENT_NO_DATA 1 /* No finger seen yet since wakeup */
+#define ICN8318_EVENT_UPDATE1 2 /* New or updated coordinates */
+#define ICN8318_EVENT_UPDATE2 3 /* New or updated coordinates */
+#define ICN8318_EVENT_END 4 /* Finger lifted */
+} __packed;
+
+struct icn8318_touch_data {
+ __u8 softbutton;
+ __u8 touch_count;
+ struct icn8318_touch touches[ICN8318_MAX_TOUCHES];
+} __packed;
+
+struct icn8318_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct gpio_desc *wake_gpio;
+ u32 max_x;
+ u32 max_y;
+ bool invert_x;
+ bool invert_y;
+ bool swap_x_y;
+};
+
+static int icn8318_read_touch_data(struct i2c_client *client,
+ struct icn8318_touch_data *touch_data)
+{
+ u8 reg = ICN8318_REG_TOUCHDATA;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = client->addr,
+ .len = 1,
+ .buf = &reg
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(struct icn8318_touch_data),
+ .buf = (u8 *)touch_data
+ }
+ };
+
+ return i2c_transfer(client->adapter, msg, 2);
+}
+
+static inline bool icn8318_touch_active(u8 event)
+{
+ return (event == ICN8318_EVENT_UPDATE1) ||
+ (event == ICN8318_EVENT_UPDATE2);
+}
+
+static irqreturn_t icn8318_irq(int irq, void *dev_id)
+{
+ struct icn8318_data *data = dev_id;
+ struct device *dev = &data->client->dev;
+ struct icn8318_touch_data touch_data;
+ int i, ret, x, y;
+
+ ret = icn8318_read_touch_data(data->client, &touch_data);
+ if (ret < 0) {
+ dev_err(dev, "Error reading touch data: %d\n", ret);
+ return IRQ_HANDLED;
+ }
+
+ if (touch_data.softbutton) {
+ /*
+ * Other data is invalid when a softbutton is pressed.
+ * This needs some extra devicetree bindings to map the icn8318
+ * softbutton codes to evdev codes. Currently no known devices
+ * use this.
+ */
+ return IRQ_HANDLED;
+ }
+
+ if (touch_data.touch_count > ICN8318_MAX_TOUCHES) {
+ dev_warn(dev, "Too much touches %d > %d\n",
+ touch_data.touch_count, ICN8318_MAX_TOUCHES);
+ touch_data.touch_count = ICN8318_MAX_TOUCHES;
+ }
+
+ for (i = 0; i < touch_data.touch_count; i++) {
+ struct icn8318_touch *touch = &touch_data.touches[i];
+ bool act = icn8318_touch_active(touch->event);
+
+ input_mt_slot(data->input, touch->slot);
+ input_mt_report_slot_state(data->input, MT_TOOL_FINGER, act);
+ if (!act)
+ continue;
+
+ x = be16_to_cpu(touch->x);
+ y = be16_to_cpu(touch->y);
+
+ if (data->invert_x)
+ x = data->max_x - x;
+
+ if (data->invert_y)
+ y = data->max_y - y;
+
+ if (!data->swap_x_y) {
+ input_event(data->input, EV_ABS, ABS_MT_POSITION_X, x);
+ input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, y);
+ } else {
+ input_event(data->input, EV_ABS, ABS_MT_POSITION_X, y);
+ input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, x);
+ }
+ }
+
+ input_mt_sync_frame(data->input);
+ input_sync(data->input);
+
+ return IRQ_HANDLED;
+}
+
+static int icn8318_start(struct input_dev *dev)
+{
+ struct icn8318_data *data = input_get_drvdata(dev);
+
+ enable_irq(data->client->irq);
+ gpiod_set_value_cansleep(data->wake_gpio, 1);
+
+ return 0;
+}
+
+static void icn8318_stop(struct input_dev *dev)
+{
+ struct icn8318_data *data = input_get_drvdata(dev);
+
+ disable_irq(data->client->irq);
+ i2c_smbus_write_byte_data(data->client, ICN8318_REG_POWER,
+ ICN8318_POWER_HIBERNATE);
+ gpiod_set_value_cansleep(data->wake_gpio, 0);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int icn8318_suspend(struct device *dev)
+{
+ struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+ mutex_lock(&data->input->mutex);
+ if (data->input->users)
+ icn8318_stop(data->input);
+ mutex_unlock(&data->input->mutex);
+
+ return 0;
+}
+
+static int icn8318_resume(struct device *dev)
+{
+ struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+ mutex_lock(&data->input->mutex);
+ if (data->input->users)
+ icn8318_start(data->input);
+ mutex_unlock(&data->input->mutex);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(icn8318_pm_ops, icn8318_suspend, icn8318_resume);
+
+static int icn8318_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct device_node *np = dev->of_node;
+ struct icn8318_data *data;
+ struct input_dev *input;
+ u32 fuzz_x = 0, fuzz_y = 0;
+ int error;
+
+ if (!client->irq) {
+ dev_err(dev, "Error no irq specified\n");
+ return -EINVAL;
+ }
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->wake_gpio = devm_gpiod_get(dev, "wake", GPIOD_OUT_LOW);
+ if (IS_ERR(data->wake_gpio)) {
+ error = PTR_ERR(data->wake_gpio);
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "Error getting wake gpio: %d\n", error);
+ return error;
+ }
+
+ if (of_property_read_u32(np, "touchscreen-size-x", &data->max_x) ||
+ of_property_read_u32(np, "touchscreen-size-y", &data->max_y)) {
+ dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
+ return -EINVAL;
+ }
+
+ /* Optional */
+ of_property_read_u32(np, "touchscreen-fuzz-x", &fuzz_x);
+ of_property_read_u32(np, "touchscreen-fuzz-y", &fuzz_y);
+ data->invert_x = of_property_read_bool(np, "touchscreen-inverted-x");
+ data->invert_y = of_property_read_bool(np, "touchscreen-inverted-y");
+ data->swap_x_y = of_property_read_bool(np, "touchscreen-swapped-x-y");
+
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return -ENOMEM;
+
+ input->name = client->name;
+ input->id.bustype = BUS_I2C;
+ input->open = icn8318_start;
+ input->close = icn8318_stop;
+ input->dev.parent = dev;
+
+ if (!data->swap_x_y) {
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+ data->max_x, fuzz_x, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+ data->max_y, fuzz_y, 0);
+ } else {
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+ data->max_y, fuzz_y, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+ data->max_x, fuzz_x, 0);
+ }
+
+ error = input_mt_init_slots(input, ICN8318_MAX_TOUCHES,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+ if (error)
+ return error;
+
+ data->client = client;
+ data->input = input;
+ input_set_drvdata(input, data);
+
+ error = devm_request_threaded_irq(dev, client->irq, NULL, icn8318_irq,
+ IRQF_ONESHOT, client->name, data);
+ if (error) {
+ dev_err(dev, "Error requesting irq: %d\n", error);
+ return error;
+ }
+
+ /* Stop device till opened */
+ icn8318_stop(data->input);
+
+ error = input_register_device(input);
+ if (error)
+ return error;
+
+ i2c_set_clientdata(client, data);
+
+ return 0;
+}
+
+static const struct of_device_id icn8318_of_match[] = {
+ { .compatible = "chipone,icn8318" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, icn8318_of_match);
+
+/* This is useless for OF-enabled devices, but it is needed by I2C subsystem */
+static const struct i2c_device_id icn8318_i2c_id[] = {
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, icn8318_i2c_id);
+
+static struct i2c_driver icn8318_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "chipone_icn8318",
+ .pm = &icn8318_pm_ops,
+ .of_match_table = icn8318_of_match,
+ },
+ .probe = icn8318_probe,
+ .id_table = icn8318_i2c_id,
+};
+
+module_i2c_driver(icn8318_driver);
+
+MODULE_DESCRIPTION("ChipOne icn8318 I2C Touchscreen Driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c
new file mode 100644
index 000000000..f2119ee0e
--- /dev/null
+++ b/drivers/input/touchscreen/cy8ctmg110_ts.c
@@ -0,0 +1,363 @@
+/*
+ * Driver for cypress touch screen controller
+ *
+ * Copyright (c) 2009 Aava Mobile
+ *
+ * Some cleanups by Alan Cox <alan@linux.intel.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/input/cy8ctmg110_pdata.h>
+
+#define CY8CTMG110_DRIVER_NAME "cy8ctmg110"
+
+/* Touch coordinates */
+#define CY8CTMG110_X_MIN 0
+#define CY8CTMG110_Y_MIN 0
+#define CY8CTMG110_X_MAX 759
+#define CY8CTMG110_Y_MAX 465
+
+
+/* cy8ctmg110 register definitions */
+#define CY8CTMG110_TOUCH_WAKEUP_TIME 0
+#define CY8CTMG110_TOUCH_SLEEP_TIME 2
+#define CY8CTMG110_TOUCH_X1 3
+#define CY8CTMG110_TOUCH_Y1 5
+#define CY8CTMG110_TOUCH_X2 7
+#define CY8CTMG110_TOUCH_Y2 9
+#define CY8CTMG110_FINGERS 11
+#define CY8CTMG110_GESTURE 12
+#define CY8CTMG110_REG_MAX 13
+
+
+/*
+ * The touch driver structure.
+ */
+struct cy8ctmg110 {
+ struct input_dev *input;
+ char phys[32];
+ struct i2c_client *client;
+ int reset_pin;
+ int irq_pin;
+};
+
+/*
+ * cy8ctmg110_power is the routine that is called when touch hardware
+ * will powered off or on.
+ */
+static void cy8ctmg110_power(struct cy8ctmg110 *ts, bool poweron)
+{
+ if (ts->reset_pin)
+ gpio_direction_output(ts->reset_pin, 1 - poweron);
+}
+
+static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg,
+ unsigned char len, unsigned char *value)
+{
+ struct i2c_client *client = tsc->client;
+ int ret;
+ unsigned char i2c_data[6];
+
+ BUG_ON(len > 5);
+
+ i2c_data[0] = reg;
+ memcpy(i2c_data + 1, value, len);
+
+ ret = i2c_master_send(client, i2c_data, len + 1);
+ if (ret != len + 1) {
+ dev_err(&client->dev, "i2c write data cmd failed\n");
+ return ret < 0 ? ret : -EIO;
+ }
+
+ return 0;
+}
+
+static int cy8ctmg110_read_regs(struct cy8ctmg110 *tsc,
+ unsigned char *data, unsigned char len, unsigned char cmd)
+{
+ struct i2c_client *client = tsc->client;
+ int ret;
+ struct i2c_msg msg[2] = {
+ /* first write slave position to i2c devices */
+ {
+ .addr = client->addr,
+ .len = 1,
+ .buf = &cmd
+ },
+ /* Second read data from position */
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = data
+ }
+ };
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cy8ctmg110_touch_pos(struct cy8ctmg110 *tsc)
+{
+ struct input_dev *input = tsc->input;
+ unsigned char reg_p[CY8CTMG110_REG_MAX];
+ int x, y;
+
+ memset(reg_p, 0, CY8CTMG110_REG_MAX);
+
+ /* Reading coordinates */
+ if (cy8ctmg110_read_regs(tsc, reg_p, 9, CY8CTMG110_TOUCH_X1) != 0)
+ return -EIO;
+
+ y = reg_p[2] << 8 | reg_p[3];
+ x = reg_p[0] << 8 | reg_p[1];
+
+ /* Number of touch */
+ if (reg_p[8] == 0) {
+ input_report_key(input, BTN_TOUCH, 0);
+ } else {
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ }
+
+ input_sync(input);
+
+ return 0;
+}
+
+static int cy8ctmg110_set_sleepmode(struct cy8ctmg110 *ts, bool sleep)
+{
+ unsigned char reg_p[3];
+
+ if (sleep) {
+ reg_p[0] = 0x00;
+ reg_p[1] = 0xff;
+ reg_p[2] = 5;
+ } else {
+ reg_p[0] = 0x10;
+ reg_p[1] = 0xff;
+ reg_p[2] = 0;
+ }
+
+ return cy8ctmg110_write_regs(ts, CY8CTMG110_TOUCH_WAKEUP_TIME, 3, reg_p);
+}
+
+static irqreturn_t cy8ctmg110_irq_thread(int irq, void *dev_id)
+{
+ struct cy8ctmg110 *tsc = dev_id;
+
+ cy8ctmg110_touch_pos(tsc);
+
+ return IRQ_HANDLED;
+}
+
+static int cy8ctmg110_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct cy8ctmg110_pdata *pdata = dev_get_platdata(&client->dev);
+ struct cy8ctmg110 *ts;
+ struct input_dev *input_dev;
+ int err;
+
+ /* No pdata no way forward */
+ if (pdata == NULL) {
+ dev_err(&client->dev, "no pdata\n");
+ return -ENODEV;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -EIO;
+
+ ts = kzalloc(sizeof(struct cy8ctmg110), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ts->client = client;
+ ts->input = input_dev;
+ ts->reset_pin = pdata->reset_pin;
+ ts->irq_pin = pdata->irq_pin;
+
+ snprintf(ts->phys, sizeof(ts->phys),
+ "%s/input0", dev_name(&client->dev));
+
+ input_dev->name = CY8CTMG110_DRIVER_NAME " Touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X,
+ CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0);
+
+ if (ts->reset_pin) {
+ err = gpio_request(ts->reset_pin, NULL);
+ if (err) {
+ dev_err(&client->dev,
+ "Unable to request GPIO pin %d.\n",
+ ts->reset_pin);
+ goto err_free_mem;
+ }
+ }
+
+ cy8ctmg110_power(ts, true);
+ cy8ctmg110_set_sleepmode(ts, false);
+
+ err = gpio_request(ts->irq_pin, "touch_irq_key");
+ if (err < 0) {
+ dev_err(&client->dev,
+ "Failed to request GPIO %d, error %d\n",
+ ts->irq_pin, err);
+ goto err_shutoff_device;
+ }
+
+ err = gpio_direction_input(ts->irq_pin);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "Failed to configure input direction for GPIO %d, error %d\n",
+ ts->irq_pin, err);
+ goto err_free_irq_gpio;
+ }
+
+ client->irq = gpio_to_irq(ts->irq_pin);
+ if (client->irq < 0) {
+ err = client->irq;
+ dev_err(&client->dev,
+ "Unable to get irq number for GPIO %d, error %d\n",
+ ts->irq_pin, err);
+ goto err_free_irq_gpio;
+ }
+
+ err = request_threaded_irq(client->irq, NULL, cy8ctmg110_irq_thread,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "touch_reset_key", ts);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "irq %d busy? error %d\n", client->irq, err);
+ goto err_free_irq_gpio;
+ }
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_free_irq;
+
+ i2c_set_clientdata(client, ts);
+ device_init_wakeup(&client->dev, 1);
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, ts);
+err_free_irq_gpio:
+ gpio_free(ts->irq_pin);
+err_shutoff_device:
+ cy8ctmg110_set_sleepmode(ts, true);
+ cy8ctmg110_power(ts, false);
+ if (ts->reset_pin)
+ gpio_free(ts->reset_pin);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+ return err;
+}
+
+static int __maybe_unused cy8ctmg110_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cy8ctmg110 *ts = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+ else {
+ cy8ctmg110_set_sleepmode(ts, true);
+ cy8ctmg110_power(ts, false);
+ }
+ return 0;
+}
+
+static int __maybe_unused cy8ctmg110_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cy8ctmg110 *ts = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+ else {
+ cy8ctmg110_power(ts, true);
+ cy8ctmg110_set_sleepmode(ts, false);
+ }
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume);
+
+static int cy8ctmg110_remove(struct i2c_client *client)
+{
+ struct cy8ctmg110 *ts = i2c_get_clientdata(client);
+
+ cy8ctmg110_set_sleepmode(ts, true);
+ cy8ctmg110_power(ts, false);
+
+ free_irq(client->irq, ts);
+ input_unregister_device(ts->input);
+ gpio_free(ts->irq_pin);
+ if (ts->reset_pin)
+ gpio_free(ts->reset_pin);
+ kfree(ts);
+
+ return 0;
+}
+
+static const struct i2c_device_id cy8ctmg110_idtable[] = {
+ { CY8CTMG110_DRIVER_NAME, 1 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable);
+
+static struct i2c_driver cy8ctmg110_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = CY8CTMG110_DRIVER_NAME,
+ .pm = &cy8ctmg110_pm,
+ },
+ .id_table = cy8ctmg110_idtable,
+ .probe = cy8ctmg110_probe,
+ .remove = cy8ctmg110_remove,
+};
+
+module_i2c_driver(cy8ctmg110_driver);
+
+MODULE_AUTHOR("Samuli Konttila <samuli.konttila@aavamobile.com>");
+MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c
new file mode 100644
index 000000000..568a3d340
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_core.c
@@ -0,0 +1,2160 @@
+/*
+ * cyttsp4_core.c
+ * Cypress TrueTouch(TM) Standard Product V4 Core driver module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include "cyttsp4_core.h"
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+/* Timeout in ms. */
+#define CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT 500
+#define CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT 5000
+#define CY_CORE_MODE_CHANGE_TIMEOUT 1000
+#define CY_CORE_RESET_AND_WAIT_TIMEOUT 500
+#define CY_CORE_WAKEUP_TIMEOUT 500
+
+#define CY_CORE_STARTUP_RETRY_COUNT 3
+
+static const u8 ldr_exit[] = {
+ 0xFF, 0x01, 0x3B, 0x00, 0x00, 0x4F, 0x6D, 0x17
+};
+
+static const u8 ldr_err_app[] = {
+ 0x01, 0x02, 0x00, 0x00, 0x55, 0xDD, 0x17
+};
+
+static inline size_t merge_bytes(u8 high, u8 low)
+{
+ return (high << 8) + low;
+}
+
+#ifdef VERBOSE_DEBUG
+static void cyttsp4_pr_buf(struct device *dev, u8 *pr_buf, u8 *dptr, int size,
+ const char *data_name)
+{
+ int i, k;
+ const char fmt[] = "%02X ";
+ int max;
+
+ if (!size)
+ return;
+
+ max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED);
+
+ pr_buf[0] = 0;
+ for (i = k = 0; i < size && k < max; i++, k += 3)
+ scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, dptr[i]);
+
+ dev_vdbg(dev, "%s: %s[0..%d]=%s%s\n", __func__, data_name, size - 1,
+ pr_buf, size <= max ? "" : CY_PR_TRUNCATED);
+}
+#else
+#define cyttsp4_pr_buf(dev, pr_buf, dptr, size, data_name) do { } while (0)
+#endif
+
+static int cyttsp4_load_status_regs(struct cyttsp4 *cd)
+{
+ struct cyttsp4_sysinfo *si = &cd->sysinfo;
+ struct device *dev = cd->dev;
+ int rc;
+
+ rc = cyttsp4_adap_read(cd, CY_REG_BASE, si->si_ofs.mode_size,
+ si->xy_mode);
+ if (rc < 0)
+ dev_err(dev, "%s: fail read mode regs r=%d\n",
+ __func__, rc);
+ else
+ cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_mode,
+ si->si_ofs.mode_size, "xy_mode");
+
+ return rc;
+}
+
+static int cyttsp4_handshake(struct cyttsp4 *cd, u8 mode)
+{
+ u8 cmd = mode ^ CY_HST_TOGGLE;
+ int rc;
+
+ /*
+ * Mode change issued, handshaking now will cause endless mode change
+ * requests, for sync mode modechange will do same with handshake
+ * */
+ if (mode & CY_HST_MODE_CHANGE)
+ return 0;
+
+ rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd);
+ if (rc < 0)
+ dev_err(cd->dev, "%s: bus write fail on handshake (ret=%d)\n",
+ __func__, rc);
+
+ return rc;
+}
+
+static int cyttsp4_hw_soft_reset(struct cyttsp4 *cd)
+{
+ u8 cmd = CY_HST_RESET;
+ int rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: FAILED to execute SOFT reset\n",
+ __func__);
+ return rc;
+ }
+ return 0;
+}
+
+static int cyttsp4_hw_hard_reset(struct cyttsp4 *cd)
+{
+ if (cd->cpdata->xres) {
+ cd->cpdata->xres(cd->cpdata, cd->dev);
+ dev_dbg(cd->dev, "%s: execute HARD reset\n", __func__);
+ return 0;
+ }
+ dev_err(cd->dev, "%s: FAILED to execute HARD reset\n", __func__);
+ return -ENOSYS;
+}
+
+static int cyttsp4_hw_reset(struct cyttsp4 *cd)
+{
+ int rc = cyttsp4_hw_hard_reset(cd);
+ if (rc == -ENOSYS)
+ rc = cyttsp4_hw_soft_reset(cd);
+ return rc;
+}
+
+/*
+ * Gets number of bits for a touch filed as parameter,
+ * sets maximum value for field which is used as bit mask
+ * and returns number of bytes required for that field
+ */
+static int cyttsp4_bits_2_bytes(unsigned int nbits, size_t *max)
+{
+ *max = 1UL << nbits;
+ return (nbits + 7) / 8;
+}
+
+static int cyttsp4_si_data_offsets(struct cyttsp4 *cd)
+{
+ struct cyttsp4_sysinfo *si = &cd->sysinfo;
+ int rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(si->si_data),
+ &si->si_data);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail read sysinfo data offsets r=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ /* Print sysinfo data offsets */
+ cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)&si->si_data,
+ sizeof(si->si_data), "sysinfo_data_offsets");
+
+ /* convert sysinfo data offset bytes into integers */
+
+ si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh,
+ si->si_data.map_szl);
+ si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh,
+ si->si_data.map_szl);
+ si->si_ofs.cydata_ofs = merge_bytes(si->si_data.cydata_ofsh,
+ si->si_data.cydata_ofsl);
+ si->si_ofs.test_ofs = merge_bytes(si->si_data.test_ofsh,
+ si->si_data.test_ofsl);
+ si->si_ofs.pcfg_ofs = merge_bytes(si->si_data.pcfg_ofsh,
+ si->si_data.pcfg_ofsl);
+ si->si_ofs.opcfg_ofs = merge_bytes(si->si_data.opcfg_ofsh,
+ si->si_data.opcfg_ofsl);
+ si->si_ofs.ddata_ofs = merge_bytes(si->si_data.ddata_ofsh,
+ si->si_data.ddata_ofsl);
+ si->si_ofs.mdata_ofs = merge_bytes(si->si_data.mdata_ofsh,
+ si->si_data.mdata_ofsl);
+ return rc;
+}
+
+static int cyttsp4_si_get_cydata(struct cyttsp4 *cd)
+{
+ struct cyttsp4_sysinfo *si = &cd->sysinfo;
+ int read_offset;
+ int mfgid_sz, calc_mfgid_sz;
+ void *p;
+ int rc;
+
+ si->si_ofs.cydata_size = si->si_ofs.test_ofs - si->si_ofs.cydata_ofs;
+ dev_dbg(cd->dev, "%s: cydata size: %Zd\n", __func__,
+ si->si_ofs.cydata_size);
+
+ p = krealloc(si->si_ptrs.cydata, si->si_ofs.cydata_size, GFP_KERNEL);
+ if (p == NULL) {
+ dev_err(cd->dev, "%s: fail alloc cydata memory\n", __func__);
+ return -ENOMEM;
+ }
+ si->si_ptrs.cydata = p;
+
+ read_offset = si->si_ofs.cydata_ofs;
+
+ /* Read the CYDA registers up to MFGID field */
+ rc = cyttsp4_adap_read(cd, read_offset,
+ offsetof(struct cyttsp4_cydata, mfgid_sz)
+ + sizeof(si->si_ptrs.cydata->mfgid_sz),
+ si->si_ptrs.cydata);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail read cydata r=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ /* Check MFGID size */
+ mfgid_sz = si->si_ptrs.cydata->mfgid_sz;
+ calc_mfgid_sz = si->si_ofs.cydata_size - sizeof(struct cyttsp4_cydata);
+ if (mfgid_sz != calc_mfgid_sz) {
+ dev_err(cd->dev, "%s: mismatch in MFGID size, reported:%d calculated:%d\n",
+ __func__, mfgid_sz, calc_mfgid_sz);
+ return -EINVAL;
+ }
+
+ read_offset += offsetof(struct cyttsp4_cydata, mfgid_sz)
+ + sizeof(si->si_ptrs.cydata->mfgid_sz);
+
+ /* Read the CYDA registers for MFGID field */
+ rc = cyttsp4_adap_read(cd, read_offset, si->si_ptrs.cydata->mfgid_sz,
+ si->si_ptrs.cydata->mfg_id);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail read cydata r=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ read_offset += si->si_ptrs.cydata->mfgid_sz;
+
+ /* Read the rest of the CYDA registers */
+ rc = cyttsp4_adap_read(cd, read_offset,
+ sizeof(struct cyttsp4_cydata)
+ - offsetof(struct cyttsp4_cydata, cyito_idh),
+ &si->si_ptrs.cydata->cyito_idh);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail read cydata r=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.cydata,
+ si->si_ofs.cydata_size, "sysinfo_cydata");
+ return rc;
+}
+
+static int cyttsp4_si_get_test_data(struct cyttsp4 *cd)
+{
+ struct cyttsp4_sysinfo *si = &cd->sysinfo;
+ void *p;
+ int rc;
+
+ si->si_ofs.test_size = si->si_ofs.pcfg_ofs - si->si_ofs.test_ofs;
+
+ p = krealloc(si->si_ptrs.test, si->si_ofs.test_size, GFP_KERNEL);
+ if (p == NULL) {
+ dev_err(cd->dev, "%s: fail alloc test memory\n", __func__);
+ return -ENOMEM;
+ }
+ si->si_ptrs.test = p;
+
+ rc = cyttsp4_adap_read(cd, si->si_ofs.test_ofs, si->si_ofs.test_size,
+ si->si_ptrs.test);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail read test data r=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ cyttsp4_pr_buf(cd->dev, cd->pr_buf,
+ (u8 *)si->si_ptrs.test, si->si_ofs.test_size,
+ "sysinfo_test_data");
+ if (si->si_ptrs.test->post_codel &
+ CY_POST_CODEL_WDG_RST)
+ dev_info(cd->dev, "%s: %s codel=%02X\n",
+ __func__, "Reset was a WATCHDOG RESET",
+ si->si_ptrs.test->post_codel);
+
+ if (!(si->si_ptrs.test->post_codel &
+ CY_POST_CODEL_CFG_DATA_CRC_FAIL))
+ dev_info(cd->dev, "%s: %s codel=%02X\n", __func__,
+ "Config Data CRC FAIL",
+ si->si_ptrs.test->post_codel);
+
+ if (!(si->si_ptrs.test->post_codel &
+ CY_POST_CODEL_PANEL_TEST_FAIL))
+ dev_info(cd->dev, "%s: %s codel=%02X\n",
+ __func__, "PANEL TEST FAIL",
+ si->si_ptrs.test->post_codel);
+
+ dev_info(cd->dev, "%s: SCANNING is %s codel=%02X\n",
+ __func__, si->si_ptrs.test->post_codel & 0x08 ?
+ "ENABLED" : "DISABLED",
+ si->si_ptrs.test->post_codel);
+ return rc;
+}
+
+static int cyttsp4_si_get_pcfg_data(struct cyttsp4 *cd)
+{
+ struct cyttsp4_sysinfo *si = &cd->sysinfo;
+ void *p;
+ int rc;
+
+ si->si_ofs.pcfg_size = si->si_ofs.opcfg_ofs - si->si_ofs.pcfg_ofs;
+
+ p = krealloc(si->si_ptrs.pcfg, si->si_ofs.pcfg_size, GFP_KERNEL);
+ if (p == NULL) {
+ rc = -ENOMEM;
+ dev_err(cd->dev, "%s: fail alloc pcfg memory r=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ si->si_ptrs.pcfg = p;
+
+ rc = cyttsp4_adap_read(cd, si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size,
+ si->si_ptrs.pcfg);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail read pcfg data r=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ si->si_ofs.max_x = merge_bytes((si->si_ptrs.pcfg->res_xh
+ & CY_PCFG_RESOLUTION_X_MASK), si->si_ptrs.pcfg->res_xl);
+ si->si_ofs.x_origin = !!(si->si_ptrs.pcfg->res_xh
+ & CY_PCFG_ORIGIN_X_MASK);
+ si->si_ofs.max_y = merge_bytes((si->si_ptrs.pcfg->res_yh
+ & CY_PCFG_RESOLUTION_Y_MASK), si->si_ptrs.pcfg->res_yl);
+ si->si_ofs.y_origin = !!(si->si_ptrs.pcfg->res_yh
+ & CY_PCFG_ORIGIN_Y_MASK);
+ si->si_ofs.max_p = merge_bytes(si->si_ptrs.pcfg->max_zh,
+ si->si_ptrs.pcfg->max_zl);
+
+ cyttsp4_pr_buf(cd->dev, cd->pr_buf,
+ (u8 *)si->si_ptrs.pcfg,
+ si->si_ofs.pcfg_size, "sysinfo_pcfg_data");
+ return rc;
+}
+
+static int cyttsp4_si_get_opcfg_data(struct cyttsp4 *cd)
+{
+ struct cyttsp4_sysinfo *si = &cd->sysinfo;
+ struct cyttsp4_tch_abs_params *tch;
+ struct cyttsp4_tch_rec_params *tch_old, *tch_new;
+ enum cyttsp4_tch_abs abs;
+ int i;
+ void *p;
+ int rc;
+
+ si->si_ofs.opcfg_size = si->si_ofs.ddata_ofs - si->si_ofs.opcfg_ofs;
+
+ p = krealloc(si->si_ptrs.opcfg, si->si_ofs.opcfg_size, GFP_KERNEL);
+ if (p == NULL) {
+ dev_err(cd->dev, "%s: fail alloc opcfg memory\n", __func__);
+ rc = -ENOMEM;
+ goto cyttsp4_si_get_opcfg_data_exit;
+ }
+ si->si_ptrs.opcfg = p;
+
+ rc = cyttsp4_adap_read(cd, si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size,
+ si->si_ptrs.opcfg);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail read opcfg data r=%d\n",
+ __func__, rc);
+ goto cyttsp4_si_get_opcfg_data_exit;
+ }
+ si->si_ofs.cmd_ofs = si->si_ptrs.opcfg->cmd_ofs;
+ si->si_ofs.rep_ofs = si->si_ptrs.opcfg->rep_ofs;
+ si->si_ofs.rep_sz = (si->si_ptrs.opcfg->rep_szh * 256) +
+ si->si_ptrs.opcfg->rep_szl;
+ si->si_ofs.num_btns = si->si_ptrs.opcfg->num_btns;
+ si->si_ofs.num_btn_regs = (si->si_ofs.num_btns +
+ CY_NUM_BTN_PER_REG - 1) / CY_NUM_BTN_PER_REG;
+ si->si_ofs.tt_stat_ofs = si->si_ptrs.opcfg->tt_stat_ofs;
+ si->si_ofs.obj_cfg0 = si->si_ptrs.opcfg->obj_cfg0;
+ si->si_ofs.max_tchs = si->si_ptrs.opcfg->max_tchs &
+ CY_BYTE_OFS_MASK;
+ si->si_ofs.tch_rec_size = si->si_ptrs.opcfg->tch_rec_size &
+ CY_BYTE_OFS_MASK;
+
+ /* Get the old touch fields */
+ for (abs = CY_TCH_X; abs < CY_NUM_TCH_FIELDS; abs++) {
+ tch = &si->si_ofs.tch_abs[abs];
+ tch_old = &si->si_ptrs.opcfg->tch_rec_old[abs];
+
+ tch->ofs = tch_old->loc & CY_BYTE_OFS_MASK;
+ tch->size = cyttsp4_bits_2_bytes(tch_old->size,
+ &tch->max);
+ tch->bofs = (tch_old->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT;
+ }
+
+ /* button fields */
+ si->si_ofs.btn_rec_size = si->si_ptrs.opcfg->btn_rec_size;
+ si->si_ofs.btn_diff_ofs = si->si_ptrs.opcfg->btn_diff_ofs;
+ si->si_ofs.btn_diff_size = si->si_ptrs.opcfg->btn_diff_size;
+
+ if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) {
+ /* Get the extended touch fields */
+ for (i = 0; i < CY_NUM_EXT_TCH_FIELDS; abs++, i++) {
+ tch = &si->si_ofs.tch_abs[abs];
+ tch_new = &si->si_ptrs.opcfg->tch_rec_new[i];
+
+ tch->ofs = tch_new->loc & CY_BYTE_OFS_MASK;
+ tch->size = cyttsp4_bits_2_bytes(tch_new->size,
+ &tch->max);
+ tch->bofs = (tch_new->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT;
+ }
+ }
+
+ for (abs = 0; abs < CY_TCH_NUM_ABS; abs++) {
+ dev_dbg(cd->dev, "%s: tch_rec_%s\n", __func__,
+ cyttsp4_tch_abs_string[abs]);
+ dev_dbg(cd->dev, "%s: ofs =%2Zd\n", __func__,
+ si->si_ofs.tch_abs[abs].ofs);
+ dev_dbg(cd->dev, "%s: siz =%2Zd\n", __func__,
+ si->si_ofs.tch_abs[abs].size);
+ dev_dbg(cd->dev, "%s: max =%2Zd\n", __func__,
+ si->si_ofs.tch_abs[abs].max);
+ dev_dbg(cd->dev, "%s: bofs=%2Zd\n", __func__,
+ si->si_ofs.tch_abs[abs].bofs);
+ }
+
+ si->si_ofs.mode_size = si->si_ofs.tt_stat_ofs + 1;
+ si->si_ofs.data_size = si->si_ofs.max_tchs *
+ si->si_ptrs.opcfg->tch_rec_size;
+
+ cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.opcfg,
+ si->si_ofs.opcfg_size, "sysinfo_opcfg_data");
+
+cyttsp4_si_get_opcfg_data_exit:
+ return rc;
+}
+
+static int cyttsp4_si_get_ddata(struct cyttsp4 *cd)
+{
+ struct cyttsp4_sysinfo *si = &cd->sysinfo;
+ void *p;
+ int rc;
+
+ si->si_ofs.ddata_size = si->si_ofs.mdata_ofs - si->si_ofs.ddata_ofs;
+
+ p = krealloc(si->si_ptrs.ddata, si->si_ofs.ddata_size, GFP_KERNEL);
+ if (p == NULL) {
+ dev_err(cd->dev, "%s: fail alloc ddata memory\n", __func__);
+ return -ENOMEM;
+ }
+ si->si_ptrs.ddata = p;
+
+ rc = cyttsp4_adap_read(cd, si->si_ofs.ddata_ofs, si->si_ofs.ddata_size,
+ si->si_ptrs.ddata);
+ if (rc < 0)
+ dev_err(cd->dev, "%s: fail read ddata data r=%d\n",
+ __func__, rc);
+ else
+ cyttsp4_pr_buf(cd->dev, cd->pr_buf,
+ (u8 *)si->si_ptrs.ddata,
+ si->si_ofs.ddata_size, "sysinfo_ddata");
+ return rc;
+}
+
+static int cyttsp4_si_get_mdata(struct cyttsp4 *cd)
+{
+ struct cyttsp4_sysinfo *si = &cd->sysinfo;
+ void *p;
+ int rc;
+
+ si->si_ofs.mdata_size = si->si_ofs.map_sz - si->si_ofs.mdata_ofs;
+
+ p = krealloc(si->si_ptrs.mdata, si->si_ofs.mdata_size, GFP_KERNEL);
+ if (p == NULL) {
+ dev_err(cd->dev, "%s: fail alloc mdata memory\n", __func__);
+ return -ENOMEM;
+ }
+ si->si_ptrs.mdata = p;
+
+ rc = cyttsp4_adap_read(cd, si->si_ofs.mdata_ofs, si->si_ofs.mdata_size,
+ si->si_ptrs.mdata);
+ if (rc < 0)
+ dev_err(cd->dev, "%s: fail read mdata data r=%d\n",
+ __func__, rc);
+ else
+ cyttsp4_pr_buf(cd->dev, cd->pr_buf,
+ (u8 *)si->si_ptrs.mdata,
+ si->si_ofs.mdata_size, "sysinfo_mdata");
+ return rc;
+}
+
+static int cyttsp4_si_get_btn_data(struct cyttsp4 *cd)
+{
+ struct cyttsp4_sysinfo *si = &cd->sysinfo;
+ int btn;
+ int num_defined_keys;
+ u16 *key_table;
+ void *p;
+ int rc = 0;
+
+ if (si->si_ofs.num_btns) {
+ si->si_ofs.btn_keys_size = si->si_ofs.num_btns *
+ sizeof(struct cyttsp4_btn);
+
+ p = krealloc(si->btn, si->si_ofs.btn_keys_size,
+ GFP_KERNEL|__GFP_ZERO);
+ if (p == NULL) {
+ dev_err(cd->dev, "%s: %s\n", __func__,
+ "fail alloc btn_keys memory");
+ return -ENOMEM;
+ }
+ si->btn = p;
+
+ if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS] == NULL)
+ num_defined_keys = 0;
+ else if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS]->data == NULL)
+ num_defined_keys = 0;
+ else
+ num_defined_keys = cd->cpdata->sett
+ [CY_IC_GRPNUM_BTN_KEYS]->size;
+
+ for (btn = 0; btn < si->si_ofs.num_btns &&
+ btn < num_defined_keys; btn++) {
+ key_table = (u16 *)cd->cpdata->sett
+ [CY_IC_GRPNUM_BTN_KEYS]->data;
+ si->btn[btn].key_code = key_table[btn];
+ si->btn[btn].state = CY_BTN_RELEASED;
+ si->btn[btn].enabled = true;
+ }
+ for (; btn < si->si_ofs.num_btns; btn++) {
+ si->btn[btn].key_code = KEY_RESERVED;
+ si->btn[btn].state = CY_BTN_RELEASED;
+ si->btn[btn].enabled = true;
+ }
+
+ return rc;
+ }
+
+ si->si_ofs.btn_keys_size = 0;
+ kfree(si->btn);
+ si->btn = NULL;
+ return rc;
+}
+
+static int cyttsp4_si_get_op_data_ptrs(struct cyttsp4 *cd)
+{
+ struct cyttsp4_sysinfo *si = &cd->sysinfo;
+ void *p;
+
+ p = krealloc(si->xy_mode, si->si_ofs.mode_size, GFP_KERNEL|__GFP_ZERO);
+ if (p == NULL)
+ return -ENOMEM;
+ si->xy_mode = p;
+
+ p = krealloc(si->xy_data, si->si_ofs.data_size, GFP_KERNEL|__GFP_ZERO);
+ if (p == NULL)
+ return -ENOMEM;
+ si->xy_data = p;
+
+ p = krealloc(si->btn_rec_data,
+ si->si_ofs.btn_rec_size * si->si_ofs.num_btns,
+ GFP_KERNEL|__GFP_ZERO);
+ if (p == NULL)
+ return -ENOMEM;
+ si->btn_rec_data = p;
+
+ return 0;
+}
+
+static void cyttsp4_si_put_log_data(struct cyttsp4 *cd)
+{
+ struct cyttsp4_sysinfo *si = &cd->sysinfo;
+ dev_dbg(cd->dev, "%s: cydata_ofs =%4Zd siz=%4Zd\n", __func__,
+ si->si_ofs.cydata_ofs, si->si_ofs.cydata_size);
+ dev_dbg(cd->dev, "%s: test_ofs =%4Zd siz=%4Zd\n", __func__,
+ si->si_ofs.test_ofs, si->si_ofs.test_size);
+ dev_dbg(cd->dev, "%s: pcfg_ofs =%4Zd siz=%4Zd\n", __func__,
+ si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size);
+ dev_dbg(cd->dev, "%s: opcfg_ofs =%4Zd siz=%4Zd\n", __func__,
+ si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size);
+ dev_dbg(cd->dev, "%s: ddata_ofs =%4Zd siz=%4Zd\n", __func__,
+ si->si_ofs.ddata_ofs, si->si_ofs.ddata_size);
+ dev_dbg(cd->dev, "%s: mdata_ofs =%4Zd siz=%4Zd\n", __func__,
+ si->si_ofs.mdata_ofs, si->si_ofs.mdata_size);
+
+ dev_dbg(cd->dev, "%s: cmd_ofs =%4Zd\n", __func__,
+ si->si_ofs.cmd_ofs);
+ dev_dbg(cd->dev, "%s: rep_ofs =%4Zd\n", __func__,
+ si->si_ofs.rep_ofs);
+ dev_dbg(cd->dev, "%s: rep_sz =%4Zd\n", __func__,
+ si->si_ofs.rep_sz);
+ dev_dbg(cd->dev, "%s: num_btns =%4Zd\n", __func__,
+ si->si_ofs.num_btns);
+ dev_dbg(cd->dev, "%s: num_btn_regs =%4Zd\n", __func__,
+ si->si_ofs.num_btn_regs);
+ dev_dbg(cd->dev, "%s: tt_stat_ofs =%4Zd\n", __func__,
+ si->si_ofs.tt_stat_ofs);
+ dev_dbg(cd->dev, "%s: tch_rec_size =%4Zd\n", __func__,
+ si->si_ofs.tch_rec_size);
+ dev_dbg(cd->dev, "%s: max_tchs =%4Zd\n", __func__,
+ si->si_ofs.max_tchs);
+ dev_dbg(cd->dev, "%s: mode_size =%4Zd\n", __func__,
+ si->si_ofs.mode_size);
+ dev_dbg(cd->dev, "%s: data_size =%4Zd\n", __func__,
+ si->si_ofs.data_size);
+ dev_dbg(cd->dev, "%s: map_sz =%4Zd\n", __func__,
+ si->si_ofs.map_sz);
+
+ dev_dbg(cd->dev, "%s: btn_rec_size =%2Zd\n", __func__,
+ si->si_ofs.btn_rec_size);
+ dev_dbg(cd->dev, "%s: btn_diff_ofs =%2Zd\n", __func__,
+ si->si_ofs.btn_diff_ofs);
+ dev_dbg(cd->dev, "%s: btn_diff_size =%2Zd\n", __func__,
+ si->si_ofs.btn_diff_size);
+
+ dev_dbg(cd->dev, "%s: max_x = 0x%04ZX (%Zd)\n", __func__,
+ si->si_ofs.max_x, si->si_ofs.max_x);
+ dev_dbg(cd->dev, "%s: x_origin = %Zd (%s)\n", __func__,
+ si->si_ofs.x_origin,
+ si->si_ofs.x_origin == CY_NORMAL_ORIGIN ?
+ "left corner" : "right corner");
+ dev_dbg(cd->dev, "%s: max_y = 0x%04ZX (%Zd)\n", __func__,
+ si->si_ofs.max_y, si->si_ofs.max_y);
+ dev_dbg(cd->dev, "%s: y_origin = %Zd (%s)\n", __func__,
+ si->si_ofs.y_origin,
+ si->si_ofs.y_origin == CY_NORMAL_ORIGIN ?
+ "upper corner" : "lower corner");
+ dev_dbg(cd->dev, "%s: max_p = 0x%04ZX (%Zd)\n", __func__,
+ si->si_ofs.max_p, si->si_ofs.max_p);
+
+ dev_dbg(cd->dev, "%s: xy_mode=%p xy_data=%p\n", __func__,
+ si->xy_mode, si->xy_data);
+}
+
+static int cyttsp4_get_sysinfo_regs(struct cyttsp4 *cd)
+{
+ struct cyttsp4_sysinfo *si = &cd->sysinfo;
+ int rc;
+
+ rc = cyttsp4_si_data_offsets(cd);
+ if (rc < 0)
+ return rc;
+
+ rc = cyttsp4_si_get_cydata(cd);
+ if (rc < 0)
+ return rc;
+
+ rc = cyttsp4_si_get_test_data(cd);
+ if (rc < 0)
+ return rc;
+
+ rc = cyttsp4_si_get_pcfg_data(cd);
+ if (rc < 0)
+ return rc;
+
+ rc = cyttsp4_si_get_opcfg_data(cd);
+ if (rc < 0)
+ return rc;
+
+ rc = cyttsp4_si_get_ddata(cd);
+ if (rc < 0)
+ return rc;
+
+ rc = cyttsp4_si_get_mdata(cd);
+ if (rc < 0)
+ return rc;
+
+ rc = cyttsp4_si_get_btn_data(cd);
+ if (rc < 0)
+ return rc;
+
+ rc = cyttsp4_si_get_op_data_ptrs(cd);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: failed to get_op_data\n",
+ __func__);
+ return rc;
+ }
+
+ cyttsp4_si_put_log_data(cd);
+
+ /* provide flow control handshake */
+ rc = cyttsp4_handshake(cd, si->si_data.hst_mode);
+ if (rc < 0)
+ dev_err(cd->dev, "%s: handshake fail on sysinfo reg\n",
+ __func__);
+
+ si->ready = true;
+ return rc;
+}
+
+static void cyttsp4_queue_startup_(struct cyttsp4 *cd)
+{
+ if (cd->startup_state == STARTUP_NONE) {
+ cd->startup_state = STARTUP_QUEUED;
+ schedule_work(&cd->startup_work);
+ dev_dbg(cd->dev, "%s: cyttsp4_startup queued\n", __func__);
+ } else {
+ dev_dbg(cd->dev, "%s: startup_state = %d\n", __func__,
+ cd->startup_state);
+ }
+}
+
+static void cyttsp4_report_slot_liftoff(struct cyttsp4_mt_data *md,
+ int max_slots)
+{
+ int t;
+
+ if (md->num_prv_tch == 0)
+ return;
+
+ for (t = 0; t < max_slots; t++) {
+ input_mt_slot(md->input, t);
+ input_mt_report_slot_state(md->input,
+ MT_TOOL_FINGER, false);
+ }
+}
+
+static void cyttsp4_lift_all(struct cyttsp4_mt_data *md)
+{
+ if (!md->si)
+ return;
+
+ if (md->num_prv_tch != 0) {
+ cyttsp4_report_slot_liftoff(md,
+ md->si->si_ofs.tch_abs[CY_TCH_T].max);
+ input_sync(md->input);
+ md->num_prv_tch = 0;
+ }
+}
+
+static void cyttsp4_get_touch_axis(struct cyttsp4_mt_data *md,
+ int *axis, int size, int max, u8 *xy_data, int bofs)
+{
+ int nbyte;
+ int next;
+
+ for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
+ dev_vdbg(&md->input->dev,
+ "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p"
+ " xy_data[%d]=%02X(%d) bofs=%d\n",
+ __func__, *axis, *axis, size, max, xy_data, next,
+ xy_data[next], xy_data[next], bofs);
+ *axis = (*axis * 256) + (xy_data[next] >> bofs);
+ next++;
+ }
+
+ *axis &= max - 1;
+
+ dev_vdbg(&md->input->dev,
+ "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p"
+ " xy_data[%d]=%02X(%d)\n",
+ __func__, *axis, *axis, size, max, xy_data, next,
+ xy_data[next], xy_data[next]);
+}
+
+static void cyttsp4_get_touch(struct cyttsp4_mt_data *md,
+ struct cyttsp4_touch *touch, u8 *xy_data)
+{
+ struct device *dev = &md->input->dev;
+ struct cyttsp4_sysinfo *si = md->si;
+ enum cyttsp4_tch_abs abs;
+ int tmp;
+ bool flipped;
+
+ for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) {
+ cyttsp4_get_touch_axis(md, &touch->abs[abs],
+ si->si_ofs.tch_abs[abs].size,
+ si->si_ofs.tch_abs[abs].max,
+ xy_data + si->si_ofs.tch_abs[abs].ofs,
+ si->si_ofs.tch_abs[abs].bofs);
+ dev_vdbg(dev, "%s: get %s=%04X(%d)\n", __func__,
+ cyttsp4_tch_abs_string[abs],
+ touch->abs[abs], touch->abs[abs]);
+ }
+
+ if (md->pdata->flags & CY_FLAG_FLIP) {
+ tmp = touch->abs[CY_TCH_X];
+ touch->abs[CY_TCH_X] = touch->abs[CY_TCH_Y];
+ touch->abs[CY_TCH_Y] = tmp;
+ flipped = true;
+ } else
+ flipped = false;
+
+ if (md->pdata->flags & CY_FLAG_INV_X) {
+ if (flipped)
+ touch->abs[CY_TCH_X] = md->si->si_ofs.max_y -
+ touch->abs[CY_TCH_X];
+ else
+ touch->abs[CY_TCH_X] = md->si->si_ofs.max_x -
+ touch->abs[CY_TCH_X];
+ }
+ if (md->pdata->flags & CY_FLAG_INV_Y) {
+ if (flipped)
+ touch->abs[CY_TCH_Y] = md->si->si_ofs.max_x -
+ touch->abs[CY_TCH_Y];
+ else
+ touch->abs[CY_TCH_Y] = md->si->si_ofs.max_y -
+ touch->abs[CY_TCH_Y];
+ }
+
+ dev_vdbg(dev, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n",
+ __func__, flipped ? "true" : "false",
+ md->pdata->flags & CY_FLAG_INV_X ? "true" : "false",
+ md->pdata->flags & CY_FLAG_INV_Y ? "true" : "false",
+ touch->abs[CY_TCH_X], touch->abs[CY_TCH_X],
+ touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]);
+}
+
+static void cyttsp4_final_sync(struct input_dev *input, int max_slots, int *ids)
+{
+ int t;
+
+ for (t = 0; t < max_slots; t++) {
+ if (ids[t])
+ continue;
+ input_mt_slot(input, t);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+ }
+
+ input_sync(input);
+}
+
+static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch)
+{
+ struct device *dev = &md->input->dev;
+ struct cyttsp4_sysinfo *si = md->si;
+ struct cyttsp4_touch tch;
+ int sig;
+ int i, j, t = 0;
+ int ids[max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)];
+
+ memset(ids, 0, si->si_ofs.tch_abs[CY_TCH_T].max * sizeof(int));
+ for (i = 0; i < num_cur_tch; i++) {
+ cyttsp4_get_touch(md, &tch, si->xy_data +
+ (i * si->si_ofs.tch_rec_size));
+ if ((tch.abs[CY_TCH_T] < md->pdata->frmwrk->abs
+ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]) ||
+ (tch.abs[CY_TCH_T] > md->pdata->frmwrk->abs
+ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MAX_OST])) {
+ dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n",
+ __func__, i, tch.abs[CY_TCH_T],
+ md->pdata->frmwrk->abs[(CY_ABS_ID_OST *
+ CY_NUM_ABS_SET) + CY_MAX_OST]);
+ continue;
+ }
+
+ /* use 0 based track id's */
+ sig = md->pdata->frmwrk->abs
+ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + 0];
+ if (sig != CY_IGNORE_VALUE) {
+ t = tch.abs[CY_TCH_T] - md->pdata->frmwrk->abs
+ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST];
+ if (tch.abs[CY_TCH_E] == CY_EV_LIFTOFF) {
+ dev_dbg(dev, "%s: t=%d e=%d lift-off\n",
+ __func__, t, tch.abs[CY_TCH_E]);
+ goto cyttsp4_get_mt_touches_pr_tch;
+ }
+ input_mt_slot(md->input, t);
+ input_mt_report_slot_state(md->input, MT_TOOL_FINGER,
+ true);
+ ids[t] = true;
+ }
+
+ /* all devices: position and pressure fields */
+ for (j = 0; j <= CY_ABS_W_OST; j++) {
+ sig = md->pdata->frmwrk->abs[((CY_ABS_X_OST + j) *
+ CY_NUM_ABS_SET) + 0];
+ if (sig != CY_IGNORE_VALUE)
+ input_report_abs(md->input, sig,
+ tch.abs[CY_TCH_X + j]);
+ }
+ if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) {
+ /*
+ * TMA400 size and orientation fields:
+ * if pressure is non-zero and major touch
+ * signal is zero, then set major and minor touch
+ * signals to minimum non-zero value
+ */
+ if (tch.abs[CY_TCH_P] > 0 && tch.abs[CY_TCH_MAJ] == 0)
+ tch.abs[CY_TCH_MAJ] = tch.abs[CY_TCH_MIN] = 1;
+
+ /* Get the extended touch fields */
+ for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) {
+ sig = md->pdata->frmwrk->abs
+ [((CY_ABS_MAJ_OST + j) *
+ CY_NUM_ABS_SET) + 0];
+ if (sig != CY_IGNORE_VALUE)
+ input_report_abs(md->input, sig,
+ tch.abs[CY_TCH_MAJ + j]);
+ }
+ }
+
+cyttsp4_get_mt_touches_pr_tch:
+ if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE)
+ dev_dbg(dev,
+ "%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d\n",
+ __func__, t,
+ tch.abs[CY_TCH_X],
+ tch.abs[CY_TCH_Y],
+ tch.abs[CY_TCH_P],
+ tch.abs[CY_TCH_MAJ],
+ tch.abs[CY_TCH_MIN],
+ tch.abs[CY_TCH_OR],
+ tch.abs[CY_TCH_E]);
+ else
+ dev_dbg(dev,
+ "%s: t=%d x=%d y=%d z=%d e=%d\n", __func__,
+ t,
+ tch.abs[CY_TCH_X],
+ tch.abs[CY_TCH_Y],
+ tch.abs[CY_TCH_P],
+ tch.abs[CY_TCH_E]);
+ }
+
+ cyttsp4_final_sync(md->input, si->si_ofs.tch_abs[CY_TCH_T].max, ids);
+
+ md->num_prv_tch = num_cur_tch;
+
+ return;
+}
+
+/* read xy_data for all current touches */
+static int cyttsp4_xy_worker(struct cyttsp4 *cd)
+{
+ struct cyttsp4_mt_data *md = &cd->md;
+ struct device *dev = &md->input->dev;
+ struct cyttsp4_sysinfo *si = md->si;
+ u8 num_cur_tch;
+ u8 hst_mode;
+ u8 rep_len;
+ u8 rep_stat;
+ u8 tt_stat;
+ int rc = 0;
+
+ /*
+ * Get event data from cyttsp4 device.
+ * The event data includes all data
+ * for all active touches.
+ * Event data also includes button data
+ */
+ /*
+ * Use 2 reads:
+ * 1st read to get mode + button bytes + touch count (core)
+ * 2nd read (optional) to get touch 1 - touch n data
+ */
+ hst_mode = si->xy_mode[CY_REG_BASE];
+ rep_len = si->xy_mode[si->si_ofs.rep_ofs];
+ rep_stat = si->xy_mode[si->si_ofs.rep_ofs + 1];
+ tt_stat = si->xy_mode[si->si_ofs.tt_stat_ofs];
+ dev_vdbg(dev, "%s: %s%02X %s%d %s%02X %s%02X\n", __func__,
+ "hst_mode=", hst_mode, "rep_len=", rep_len,
+ "rep_stat=", rep_stat, "tt_stat=", tt_stat);
+
+ num_cur_tch = GET_NUM_TOUCHES(tt_stat);
+ dev_vdbg(dev, "%s: num_cur_tch=%d\n", __func__, num_cur_tch);
+
+ if (rep_len == 0 && num_cur_tch > 0) {
+ dev_err(dev, "%s: report length error rep_len=%d num_tch=%d\n",
+ __func__, rep_len, num_cur_tch);
+ goto cyttsp4_xy_worker_exit;
+ }
+
+ /* read touches */
+ if (num_cur_tch > 0) {
+ rc = cyttsp4_adap_read(cd, si->si_ofs.tt_stat_ofs + 1,
+ num_cur_tch * si->si_ofs.tch_rec_size,
+ si->xy_data);
+ if (rc < 0) {
+ dev_err(dev, "%s: read fail on touch regs r=%d\n",
+ __func__, rc);
+ goto cyttsp4_xy_worker_exit;
+ }
+ }
+
+ /* print xy data */
+ cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_data, num_cur_tch *
+ si->si_ofs.tch_rec_size, "xy_data");
+
+ /* check any error conditions */
+ if (IS_BAD_PKT(rep_stat)) {
+ dev_dbg(dev, "%s: Invalid buffer detected\n", __func__);
+ rc = 0;
+ goto cyttsp4_xy_worker_exit;
+ }
+
+ if (IS_LARGE_AREA(tt_stat))
+ dev_dbg(dev, "%s: Large area detected\n", __func__);
+
+ if (num_cur_tch > si->si_ofs.max_tchs) {
+ dev_err(dev, "%s: too many tch; set to max tch (n=%d c=%Zd)\n",
+ __func__, num_cur_tch, si->si_ofs.max_tchs);
+ num_cur_tch = si->si_ofs.max_tchs;
+ }
+
+ /* extract xy_data for all currently reported touches */
+ dev_vdbg(dev, "%s: extract data num_cur_tch=%d\n", __func__,
+ num_cur_tch);
+ if (num_cur_tch)
+ cyttsp4_get_mt_touches(md, num_cur_tch);
+ else
+ cyttsp4_lift_all(md);
+
+ rc = 0;
+
+cyttsp4_xy_worker_exit:
+ return rc;
+}
+
+static int cyttsp4_mt_attention(struct cyttsp4 *cd)
+{
+ struct device *dev = cd->dev;
+ struct cyttsp4_mt_data *md = &cd->md;
+ int rc = 0;
+
+ if (!md->si)
+ return 0;
+
+ mutex_lock(&md->report_lock);
+ if (!md->is_suspended) {
+ /* core handles handshake */
+ rc = cyttsp4_xy_worker(cd);
+ } else {
+ dev_vdbg(dev, "%s: Ignoring report while suspended\n",
+ __func__);
+ }
+ mutex_unlock(&md->report_lock);
+ if (rc < 0)
+ dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);
+
+ return rc;
+}
+
+static irqreturn_t cyttsp4_irq(int irq, void *handle)
+{
+ struct cyttsp4 *cd = handle;
+ struct device *dev = cd->dev;
+ enum cyttsp4_mode cur_mode;
+ u8 cmd_ofs = cd->sysinfo.si_ofs.cmd_ofs;
+ u8 mode[3];
+ int rc;
+
+ /*
+ * Check whether this IRQ should be ignored (external)
+ * This should be the very first thing to check since
+ * ignore_irq may be set for a very short period of time
+ */
+ if (atomic_read(&cd->ignore_irq)) {
+ dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__);
+ return IRQ_HANDLED;
+ }
+
+ dev_dbg(dev, "%s int:0x%x\n", __func__, cd->int_status);
+
+ mutex_lock(&cd->system_lock);
+
+ /* Just to debug */
+ if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING)
+ dev_vdbg(dev, "%s: Received IRQ while in sleep\n", __func__);
+
+ rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), mode);
+ if (rc) {
+ dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc);
+ goto cyttsp4_irq_exit;
+ }
+ dev_vdbg(dev, "%s mode[0-2]:0x%X 0x%X 0x%X\n", __func__,
+ mode[0], mode[1], mode[2]);
+
+ if (IS_BOOTLOADER(mode[0], mode[1])) {
+ cur_mode = CY_MODE_BOOTLOADER;
+ dev_vdbg(dev, "%s: bl running\n", __func__);
+ if (cd->mode == CY_MODE_BOOTLOADER) {
+ /* Signal bootloader heartbeat heard */
+ wake_up(&cd->wait_q);
+ goto cyttsp4_irq_exit;
+ }
+
+ /* switch to bootloader */
+ dev_dbg(dev, "%s: restart switch to bl m=%d -> m=%d\n",
+ __func__, cd->mode, cur_mode);
+
+ /* catch operation->bl glitch */
+ if (cd->mode != CY_MODE_UNKNOWN) {
+ /* Incase startup_state do not let startup_() */
+ cd->mode = CY_MODE_UNKNOWN;
+ cyttsp4_queue_startup_(cd);
+ goto cyttsp4_irq_exit;
+ }
+
+ /*
+ * do not wake thread on this switch since
+ * it is possible to get an early heartbeat
+ * prior to performing the reset
+ */
+ cd->mode = cur_mode;
+
+ goto cyttsp4_irq_exit;
+ }
+
+ switch (mode[0] & CY_HST_MODE) {
+ case CY_HST_OPERATE:
+ cur_mode = CY_MODE_OPERATIONAL;
+ dev_vdbg(dev, "%s: operational\n", __func__);
+ break;
+ case CY_HST_CAT:
+ cur_mode = CY_MODE_CAT;
+ dev_vdbg(dev, "%s: CaT\n", __func__);
+ break;
+ case CY_HST_SYSINFO:
+ cur_mode = CY_MODE_SYSINFO;
+ dev_vdbg(dev, "%s: sysinfo\n", __func__);
+ break;
+ default:
+ cur_mode = CY_MODE_UNKNOWN;
+ dev_err(dev, "%s: unknown HST mode 0x%02X\n", __func__,
+ mode[0]);
+ break;
+ }
+
+ /* Check whether this IRQ should be ignored (internal) */
+ if (cd->int_status & CY_INT_IGNORE) {
+ dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__);
+ goto cyttsp4_irq_exit;
+ }
+
+ /* Check for wake up interrupt */
+ if (cd->int_status & CY_INT_AWAKE) {
+ cd->int_status &= ~CY_INT_AWAKE;
+ wake_up(&cd->wait_q);
+ dev_vdbg(dev, "%s: Received wake up interrupt\n", __func__);
+ goto cyttsp4_irq_handshake;
+ }
+
+ /* Expecting mode change interrupt */
+ if ((cd->int_status & CY_INT_MODE_CHANGE)
+ && (mode[0] & CY_HST_MODE_CHANGE) == 0) {
+ cd->int_status &= ~CY_INT_MODE_CHANGE;
+ dev_dbg(dev, "%s: finish mode switch m=%d -> m=%d\n",
+ __func__, cd->mode, cur_mode);
+ cd->mode = cur_mode;
+ wake_up(&cd->wait_q);
+ goto cyttsp4_irq_handshake;
+ }
+
+ /* compare current core mode to current device mode */
+ dev_vdbg(dev, "%s: cd->mode=%d cur_mode=%d\n",
+ __func__, cd->mode, cur_mode);
+ if ((mode[0] & CY_HST_MODE_CHANGE) == 0 && cd->mode != cur_mode) {
+ /* Unexpected mode change occurred */
+ dev_err(dev, "%s %d->%d 0x%x\n", __func__, cd->mode,
+ cur_mode, cd->int_status);
+ dev_dbg(dev, "%s: Unexpected mode change, startup\n",
+ __func__);
+ cyttsp4_queue_startup_(cd);
+ goto cyttsp4_irq_exit;
+ }
+
+ /* Expecting command complete interrupt */
+ dev_vdbg(dev, "%s: command byte:0x%x\n", __func__, mode[cmd_ofs]);
+ if ((cd->int_status & CY_INT_EXEC_CMD)
+ && mode[cmd_ofs] & CY_CMD_COMPLETE) {
+ cd->int_status &= ~CY_INT_EXEC_CMD;
+ dev_vdbg(dev, "%s: Received command complete interrupt\n",
+ __func__);
+ wake_up(&cd->wait_q);
+ /*
+ * It is possible to receive a single interrupt for
+ * command complete and touch/button status report.
+ * Continue processing for a possible status report.
+ */
+ }
+
+ /* This should be status report, read status regs */
+ if (cd->mode == CY_MODE_OPERATIONAL) {
+ dev_vdbg(dev, "%s: Read status registers\n", __func__);
+ rc = cyttsp4_load_status_regs(cd);
+ if (rc < 0)
+ dev_err(dev, "%s: fail read mode regs r=%d\n",
+ __func__, rc);
+ }
+
+ cyttsp4_mt_attention(cd);
+
+cyttsp4_irq_handshake:
+ /* handshake the event */
+ dev_vdbg(dev, "%s: Handshake mode=0x%02X r=%d\n",
+ __func__, mode[0], rc);
+ rc = cyttsp4_handshake(cd, mode[0]);
+ if (rc < 0)
+ dev_err(dev, "%s: Fail handshake mode=0x%02X r=%d\n",
+ __func__, mode[0], rc);
+
+ /*
+ * a non-zero udelay period is required for using
+ * IRQF_TRIGGER_LOW in order to delay until the
+ * device completes isr deassert
+ */
+ udelay(cd->cpdata->level_irq_udelay);
+
+cyttsp4_irq_exit:
+ mutex_unlock(&cd->system_lock);
+ return IRQ_HANDLED;
+}
+
+static void cyttsp4_start_wd_timer(struct cyttsp4 *cd)
+{
+ if (!CY_WATCHDOG_TIMEOUT)
+ return;
+
+ mod_timer(&cd->watchdog_timer, jiffies +
+ msecs_to_jiffies(CY_WATCHDOG_TIMEOUT));
+}
+
+static void cyttsp4_stop_wd_timer(struct cyttsp4 *cd)
+{
+ if (!CY_WATCHDOG_TIMEOUT)
+ return;
+
+ /*
+ * Ensure we wait until the watchdog timer
+ * running on a different CPU finishes
+ */
+ del_timer_sync(&cd->watchdog_timer);
+ cancel_work_sync(&cd->watchdog_work);
+ del_timer_sync(&cd->watchdog_timer);
+}
+
+static void cyttsp4_watchdog_timer(unsigned long handle)
+{
+ struct cyttsp4 *cd = (struct cyttsp4 *)handle;
+
+ dev_vdbg(cd->dev, "%s: Watchdog timer triggered\n", __func__);
+
+ schedule_work(&cd->watchdog_work);
+
+ return;
+}
+
+static int cyttsp4_request_exclusive(struct cyttsp4 *cd, void *ownptr,
+ int timeout_ms)
+{
+ int t = msecs_to_jiffies(timeout_ms);
+ bool with_timeout = (timeout_ms != 0);
+
+ mutex_lock(&cd->system_lock);
+ if (!cd->exclusive_dev && cd->exclusive_waits == 0) {
+ cd->exclusive_dev = ownptr;
+ goto exit;
+ }
+
+ cd->exclusive_waits++;
+wait:
+ mutex_unlock(&cd->system_lock);
+ if (with_timeout) {
+ t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t);
+ if (IS_TMO(t)) {
+ dev_err(cd->dev, "%s: tmo waiting exclusive access\n",
+ __func__);
+ mutex_lock(&cd->system_lock);
+ cd->exclusive_waits--;
+ mutex_unlock(&cd->system_lock);
+ return -ETIME;
+ }
+ } else {
+ wait_event(cd->wait_q, !cd->exclusive_dev);
+ }
+ mutex_lock(&cd->system_lock);
+ if (cd->exclusive_dev)
+ goto wait;
+ cd->exclusive_dev = ownptr;
+ cd->exclusive_waits--;
+exit:
+ mutex_unlock(&cd->system_lock);
+
+ return 0;
+}
+
+/*
+ * returns error if was not owned
+ */
+static int cyttsp4_release_exclusive(struct cyttsp4 *cd, void *ownptr)
+{
+ mutex_lock(&cd->system_lock);
+ if (cd->exclusive_dev != ownptr) {
+ mutex_unlock(&cd->system_lock);
+ return -EINVAL;
+ }
+
+ dev_vdbg(cd->dev, "%s: exclusive_dev %p freed\n",
+ __func__, cd->exclusive_dev);
+ cd->exclusive_dev = NULL;
+ wake_up(&cd->wait_q);
+ mutex_unlock(&cd->system_lock);
+ return 0;
+}
+
+static int cyttsp4_wait_bl_heartbeat(struct cyttsp4 *cd)
+{
+ long t;
+ int rc = 0;
+
+ /* wait heartbeat */
+ dev_vdbg(cd->dev, "%s: wait heartbeat...\n", __func__);
+ t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_BOOTLOADER,
+ msecs_to_jiffies(CY_CORE_RESET_AND_WAIT_TIMEOUT));
+ if (IS_TMO(t)) {
+ dev_err(cd->dev, "%s: tmo waiting bl heartbeat cd->mode=%d\n",
+ __func__, cd->mode);
+ rc = -ETIME;
+ }
+
+ return rc;
+}
+
+static int cyttsp4_wait_sysinfo_mode(struct cyttsp4 *cd)
+{
+ long t;
+
+ dev_vdbg(cd->dev, "%s: wait sysinfo...\n", __func__);
+
+ t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_SYSINFO,
+ msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT));
+ if (IS_TMO(t)) {
+ dev_err(cd->dev, "%s: tmo waiting exit bl cd->mode=%d\n",
+ __func__, cd->mode);
+ mutex_lock(&cd->system_lock);
+ cd->int_status &= ~CY_INT_MODE_CHANGE;
+ mutex_unlock(&cd->system_lock);
+ return -ETIME;
+ }
+
+ return 0;
+}
+
+static int cyttsp4_reset_and_wait(struct cyttsp4 *cd)
+{
+ int rc;
+
+ /* reset hardware */
+ mutex_lock(&cd->system_lock);
+ dev_dbg(cd->dev, "%s: reset hw...\n", __func__);
+ rc = cyttsp4_hw_reset(cd);
+ cd->mode = CY_MODE_UNKNOWN;
+ mutex_unlock(&cd->system_lock);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s:Fail hw reset r=%d\n", __func__, rc);
+ return rc;
+ }
+
+ return cyttsp4_wait_bl_heartbeat(cd);
+}
+
+/*
+ * returns err if refused or timeout; block until mode change complete
+ * bit is set (mode change interrupt)
+ */
+static int cyttsp4_set_mode(struct cyttsp4 *cd, int new_mode)
+{
+ u8 new_dev_mode;
+ u8 mode;
+ long t;
+ int rc;
+
+ switch (new_mode) {
+ case CY_MODE_OPERATIONAL:
+ new_dev_mode = CY_HST_OPERATE;
+ break;
+ case CY_MODE_SYSINFO:
+ new_dev_mode = CY_HST_SYSINFO;
+ break;
+ case CY_MODE_CAT:
+ new_dev_mode = CY_HST_CAT;
+ break;
+ default:
+ dev_err(cd->dev, "%s: invalid mode: %02X(%d)\n",
+ __func__, new_mode, new_mode);
+ return -EINVAL;
+ }
+
+ /* change mode */
+ dev_dbg(cd->dev, "%s: %s=%p new_dev_mode=%02X new_mode=%d\n",
+ __func__, "have exclusive", cd->exclusive_dev,
+ new_dev_mode, new_mode);
+
+ mutex_lock(&cd->system_lock);
+ rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode);
+ if (rc < 0) {
+ mutex_unlock(&cd->system_lock);
+ dev_err(cd->dev, "%s: Fail read mode r=%d\n",
+ __func__, rc);
+ goto exit;
+ }
+
+ /* Clear device mode bits and set to new mode */
+ mode &= ~CY_HST_MODE;
+ mode |= new_dev_mode | CY_HST_MODE_CHANGE;
+
+ cd->int_status |= CY_INT_MODE_CHANGE;
+ rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode), &mode);
+ mutex_unlock(&cd->system_lock);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: Fail write mode change r=%d\n",
+ __func__, rc);
+ goto exit;
+ }
+
+ /* wait for mode change done interrupt */
+ t = wait_event_timeout(cd->wait_q,
+ (cd->int_status & CY_INT_MODE_CHANGE) == 0,
+ msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT));
+ dev_dbg(cd->dev, "%s: back from wait t=%ld cd->mode=%d\n",
+ __func__, t, cd->mode);
+
+ if (IS_TMO(t)) {
+ dev_err(cd->dev, "%s: %s\n", __func__,
+ "tmo waiting mode change");
+ mutex_lock(&cd->system_lock);
+ cd->int_status &= ~CY_INT_MODE_CHANGE;
+ mutex_unlock(&cd->system_lock);
+ rc = -EINVAL;
+ }
+
+exit:
+ return rc;
+}
+
+static void cyttsp4_watchdog_work(struct work_struct *work)
+{
+ struct cyttsp4 *cd =
+ container_of(work, struct cyttsp4, watchdog_work);
+ u8 *mode;
+ int retval;
+
+ mutex_lock(&cd->system_lock);
+ retval = cyttsp4_load_status_regs(cd);
+ if (retval < 0) {
+ dev_err(cd->dev,
+ "%s: failed to access device in watchdog timer r=%d\n",
+ __func__, retval);
+ cyttsp4_queue_startup_(cd);
+ goto cyttsp4_timer_watchdog_exit_error;
+ }
+ mode = &cd->sysinfo.xy_mode[CY_REG_BASE];
+ if (IS_BOOTLOADER(mode[0], mode[1])) {
+ dev_err(cd->dev,
+ "%s: device found in bootloader mode when operational mode\n",
+ __func__);
+ cyttsp4_queue_startup_(cd);
+ goto cyttsp4_timer_watchdog_exit_error;
+ }
+
+ cyttsp4_start_wd_timer(cd);
+cyttsp4_timer_watchdog_exit_error:
+ mutex_unlock(&cd->system_lock);
+ return;
+}
+
+static int cyttsp4_core_sleep_(struct cyttsp4 *cd)
+{
+ enum cyttsp4_sleep_state ss = SS_SLEEP_ON;
+ enum cyttsp4_int_state int_status = CY_INT_IGNORE;
+ int rc = 0;
+ u8 mode[2];
+
+ /* Already in sleep mode? */
+ mutex_lock(&cd->system_lock);
+ if (cd->sleep_state == SS_SLEEP_ON) {
+ mutex_unlock(&cd->system_lock);
+ return 0;
+ }
+ cd->sleep_state = SS_SLEEPING;
+ mutex_unlock(&cd->system_lock);
+
+ cyttsp4_stop_wd_timer(cd);
+
+ /* Wait until currently running IRQ handler exits and disable IRQ */
+ disable_irq(cd->irq);
+
+ dev_vdbg(cd->dev, "%s: write DEEP SLEEP...\n", __func__);
+ mutex_lock(&cd->system_lock);
+ rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode);
+ if (rc) {
+ mutex_unlock(&cd->system_lock);
+ dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc);
+ goto error;
+ }
+
+ if (IS_BOOTLOADER(mode[0], mode[1])) {
+ mutex_unlock(&cd->system_lock);
+ dev_err(cd->dev, "%s: Device in BOOTLADER mode.\n", __func__);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ mode[0] |= CY_HST_SLEEP;
+ rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode[0]), &mode[0]);
+ mutex_unlock(&cd->system_lock);
+ if (rc) {
+ dev_err(cd->dev, "%s: Fail write adapter r=%d\n", __func__, rc);
+ goto error;
+ }
+ dev_vdbg(cd->dev, "%s: write DEEP SLEEP succeeded\n", __func__);
+
+ if (cd->cpdata->power) {
+ dev_dbg(cd->dev, "%s: Power down HW\n", __func__);
+ rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, &cd->ignore_irq);
+ } else {
+ dev_dbg(cd->dev, "%s: No power function\n", __func__);
+ rc = 0;
+ }
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: HW Power down fails r=%d\n",
+ __func__, rc);
+ goto error;
+ }
+
+ /* Give time to FW to sleep */
+ msleep(50);
+
+ goto exit;
+
+error:
+ ss = SS_SLEEP_OFF;
+ int_status = CY_INT_NONE;
+ cyttsp4_start_wd_timer(cd);
+
+exit:
+ mutex_lock(&cd->system_lock);
+ cd->sleep_state = ss;
+ cd->int_status |= int_status;
+ mutex_unlock(&cd->system_lock);
+ enable_irq(cd->irq);
+ return rc;
+}
+
+static int cyttsp4_startup_(struct cyttsp4 *cd)
+{
+ int retry = CY_CORE_STARTUP_RETRY_COUNT;
+ int rc;
+
+ cyttsp4_stop_wd_timer(cd);
+
+reset:
+ if (retry != CY_CORE_STARTUP_RETRY_COUNT)
+ dev_dbg(cd->dev, "%s: Retry %d\n", __func__,
+ CY_CORE_STARTUP_RETRY_COUNT - retry);
+
+ /* reset hardware and wait for heartbeat */
+ rc = cyttsp4_reset_and_wait(cd);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: Error on h/w reset r=%d\n", __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+
+ /* exit bl into sysinfo mode */
+ dev_vdbg(cd->dev, "%s: write exit ldr...\n", __func__);
+ mutex_lock(&cd->system_lock);
+ cd->int_status &= ~CY_INT_IGNORE;
+ cd->int_status |= CY_INT_MODE_CHANGE;
+
+ rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(ldr_exit),
+ (u8 *)ldr_exit);
+ mutex_unlock(&cd->system_lock);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: Fail write r=%d\n", __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+
+ rc = cyttsp4_wait_sysinfo_mode(cd);
+ if (rc < 0) {
+ u8 buf[sizeof(ldr_err_app)];
+ int rc1;
+
+ /* Check for invalid/corrupted touch application */
+ rc1 = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(ldr_err_app),
+ buf);
+ if (rc1) {
+ dev_err(cd->dev, "%s: Fail read r=%d\n", __func__, rc1);
+ } else if (!memcmp(buf, ldr_err_app, sizeof(ldr_err_app))) {
+ dev_err(cd->dev, "%s: Error launching touch application\n",
+ __func__);
+ mutex_lock(&cd->system_lock);
+ cd->invalid_touch_app = true;
+ mutex_unlock(&cd->system_lock);
+ goto exit_no_wd;
+ }
+
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+
+ mutex_lock(&cd->system_lock);
+ cd->invalid_touch_app = false;
+ mutex_unlock(&cd->system_lock);
+
+ /* read sysinfo data */
+ dev_vdbg(cd->dev, "%s: get sysinfo regs..\n", __func__);
+ rc = cyttsp4_get_sysinfo_regs(cd);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: failed to get sysinfo regs rc=%d\n",
+ __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+
+ rc = cyttsp4_set_mode(cd, CY_MODE_OPERATIONAL);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: failed to set mode to operational rc=%d\n",
+ __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+
+ cyttsp4_lift_all(&cd->md);
+
+ /* restore to sleep if was suspended */
+ mutex_lock(&cd->system_lock);
+ if (cd->sleep_state == SS_SLEEP_ON) {
+ cd->sleep_state = SS_SLEEP_OFF;
+ mutex_unlock(&cd->system_lock);
+ cyttsp4_core_sleep_(cd);
+ goto exit_no_wd;
+ }
+ mutex_unlock(&cd->system_lock);
+
+exit:
+ cyttsp4_start_wd_timer(cd);
+exit_no_wd:
+ return rc;
+}
+
+static int cyttsp4_startup(struct cyttsp4 *cd)
+{
+ int rc;
+
+ mutex_lock(&cd->system_lock);
+ cd->startup_state = STARTUP_RUNNING;
+ mutex_unlock(&cd->system_lock);
+
+ rc = cyttsp4_request_exclusive(cd, cd->dev,
+ CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ goto exit;
+ }
+
+ rc = cyttsp4_startup_(cd);
+
+ if (cyttsp4_release_exclusive(cd, cd->dev) < 0)
+ /* Don't return fail code, mode is already changed. */
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+ else
+ dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__);
+
+exit:
+ mutex_lock(&cd->system_lock);
+ cd->startup_state = STARTUP_NONE;
+ mutex_unlock(&cd->system_lock);
+
+ /* Wake the waiters for end of startup */
+ wake_up(&cd->wait_q);
+
+ return rc;
+}
+
+static void cyttsp4_startup_work_function(struct work_struct *work)
+{
+ struct cyttsp4 *cd = container_of(work, struct cyttsp4, startup_work);
+ int rc;
+
+ rc = cyttsp4_startup(cd);
+ if (rc < 0)
+ dev_err(cd->dev, "%s: Fail queued startup r=%d\n",
+ __func__, rc);
+}
+
+static void cyttsp4_free_si_ptrs(struct cyttsp4 *cd)
+{
+ struct cyttsp4_sysinfo *si = &cd->sysinfo;
+
+ if (!si)
+ return;
+
+ kfree(si->si_ptrs.cydata);
+ kfree(si->si_ptrs.test);
+ kfree(si->si_ptrs.pcfg);
+ kfree(si->si_ptrs.opcfg);
+ kfree(si->si_ptrs.ddata);
+ kfree(si->si_ptrs.mdata);
+ kfree(si->btn);
+ kfree(si->xy_mode);
+ kfree(si->xy_data);
+ kfree(si->btn_rec_data);
+}
+
+#ifdef CONFIG_PM
+static int cyttsp4_core_sleep(struct cyttsp4 *cd)
+{
+ int rc;
+
+ rc = cyttsp4_request_exclusive(cd, cd->dev,
+ CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return 0;
+ }
+
+ rc = cyttsp4_core_sleep_(cd);
+
+ if (cyttsp4_release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+ else
+ dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int cyttsp4_core_wake_(struct cyttsp4 *cd)
+{
+ struct device *dev = cd->dev;
+ int rc;
+ u8 mode;
+ int t;
+
+ /* Already woken? */
+ mutex_lock(&cd->system_lock);
+ if (cd->sleep_state == SS_SLEEP_OFF) {
+ mutex_unlock(&cd->system_lock);
+ return 0;
+ }
+ cd->int_status &= ~CY_INT_IGNORE;
+ cd->int_status |= CY_INT_AWAKE;
+ cd->sleep_state = SS_WAKING;
+
+ if (cd->cpdata->power) {
+ dev_dbg(dev, "%s: Power up HW\n", __func__);
+ rc = cd->cpdata->power(cd->cpdata, 1, dev, &cd->ignore_irq);
+ } else {
+ dev_dbg(dev, "%s: No power function\n", __func__);
+ rc = -ENOSYS;
+ }
+ if (rc < 0) {
+ dev_err(dev, "%s: HW Power up fails r=%d\n",
+ __func__, rc);
+
+ /* Initiate a read transaction to wake up */
+ cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode);
+ } else
+ dev_vdbg(cd->dev, "%s: HW power up succeeds\n",
+ __func__);
+ mutex_unlock(&cd->system_lock);
+
+ t = wait_event_timeout(cd->wait_q,
+ (cd->int_status & CY_INT_AWAKE) == 0,
+ msecs_to_jiffies(CY_CORE_WAKEUP_TIMEOUT));
+ if (IS_TMO(t)) {
+ dev_err(dev, "%s: TMO waiting for wakeup\n", __func__);
+ mutex_lock(&cd->system_lock);
+ cd->int_status &= ~CY_INT_AWAKE;
+ /* Try starting up */
+ cyttsp4_queue_startup_(cd);
+ mutex_unlock(&cd->system_lock);
+ }
+
+ mutex_lock(&cd->system_lock);
+ cd->sleep_state = SS_SLEEP_OFF;
+ mutex_unlock(&cd->system_lock);
+
+ cyttsp4_start_wd_timer(cd);
+
+ return 0;
+}
+
+static int cyttsp4_core_wake(struct cyttsp4 *cd)
+{
+ int rc;
+
+ rc = cyttsp4_request_exclusive(cd, cd->dev,
+ CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return 0;
+ }
+
+ rc = cyttsp4_core_wake_(cd);
+
+ if (cyttsp4_release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+ else
+ dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int cyttsp4_core_suspend(struct device *dev)
+{
+ struct cyttsp4 *cd = dev_get_drvdata(dev);
+ struct cyttsp4_mt_data *md = &cd->md;
+ int rc;
+
+ md->is_suspended = true;
+
+ rc = cyttsp4_core_sleep(cd);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on sleep\n", __func__);
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static int cyttsp4_core_resume(struct device *dev)
+{
+ struct cyttsp4 *cd = dev_get_drvdata(dev);
+ struct cyttsp4_mt_data *md = &cd->md;
+ int rc;
+
+ md->is_suspended = false;
+
+ rc = cyttsp4_core_wake(cd);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on wake\n", __func__);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+#endif
+
+const struct dev_pm_ops cyttsp4_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume)
+ SET_RUNTIME_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(cyttsp4_pm_ops);
+
+static int cyttsp4_mt_open(struct input_dev *input)
+{
+ pm_runtime_get(input->dev.parent);
+ return 0;
+}
+
+static void cyttsp4_mt_close(struct input_dev *input)
+{
+ struct cyttsp4_mt_data *md = input_get_drvdata(input);
+ mutex_lock(&md->report_lock);
+ if (!md->is_suspended)
+ pm_runtime_put(input->dev.parent);
+ mutex_unlock(&md->report_lock);
+}
+
+
+static int cyttsp4_setup_input_device(struct cyttsp4 *cd)
+{
+ struct device *dev = cd->dev;
+ struct cyttsp4_mt_data *md = &cd->md;
+ int signal = CY_IGNORE_VALUE;
+ int max_x, max_y, max_p, min, max;
+ int max_x_tmp, max_y_tmp;
+ int i;
+ int rc;
+
+ dev_vdbg(dev, "%s: Initialize event signals\n", __func__);
+ __set_bit(EV_ABS, md->input->evbit);
+ __set_bit(EV_REL, md->input->evbit);
+ __set_bit(EV_KEY, md->input->evbit);
+
+ max_x_tmp = md->si->si_ofs.max_x;
+ max_y_tmp = md->si->si_ofs.max_y;
+
+ /* get maximum values from the sysinfo data */
+ if (md->pdata->flags & CY_FLAG_FLIP) {
+ max_x = max_y_tmp - 1;
+ max_y = max_x_tmp - 1;
+ } else {
+ max_x = max_x_tmp - 1;
+ max_y = max_y_tmp - 1;
+ }
+ max_p = md->si->si_ofs.max_p;
+
+ /* set event signal capabilities */
+ for (i = 0; i < (md->pdata->frmwrk->size / CY_NUM_ABS_SET); i++) {
+ signal = md->pdata->frmwrk->abs
+ [(i * CY_NUM_ABS_SET) + CY_SIGNAL_OST];
+ if (signal != CY_IGNORE_VALUE) {
+ __set_bit(signal, md->input->absbit);
+ min = md->pdata->frmwrk->abs
+ [(i * CY_NUM_ABS_SET) + CY_MIN_OST];
+ max = md->pdata->frmwrk->abs
+ [(i * CY_NUM_ABS_SET) + CY_MAX_OST];
+ if (i == CY_ABS_ID_OST) {
+ /* shift track ids down to start at 0 */
+ max = max - min;
+ min = min - min;
+ } else if (i == CY_ABS_X_OST)
+ max = max_x;
+ else if (i == CY_ABS_Y_OST)
+ max = max_y;
+ else if (i == CY_ABS_P_OST)
+ max = max_p;
+ input_set_abs_params(md->input, signal, min, max,
+ md->pdata->frmwrk->abs
+ [(i * CY_NUM_ABS_SET) + CY_FUZZ_OST],
+ md->pdata->frmwrk->abs
+ [(i * CY_NUM_ABS_SET) + CY_FLAT_OST]);
+ dev_dbg(dev, "%s: register signal=%02X min=%d max=%d\n",
+ __func__, signal, min, max);
+ if ((i == CY_ABS_ID_OST) &&
+ (md->si->si_ofs.tch_rec_size <
+ CY_TMA4XX_TCH_REC_SIZE))
+ break;
+ }
+ }
+
+ input_mt_init_slots(md->input, md->si->si_ofs.tch_abs[CY_TCH_T].max,
+ INPUT_MT_DIRECT);
+ rc = input_register_device(md->input);
+ if (rc < 0)
+ dev_err(dev, "%s: Error, failed register input device r=%d\n",
+ __func__, rc);
+ return rc;
+}
+
+static int cyttsp4_mt_probe(struct cyttsp4 *cd)
+{
+ struct device *dev = cd->dev;
+ struct cyttsp4_mt_data *md = &cd->md;
+ struct cyttsp4_mt_platform_data *pdata = cd->pdata->mt_pdata;
+ int rc = 0;
+
+ mutex_init(&md->report_lock);
+ md->pdata = pdata;
+ /* Create the input device and register it. */
+ dev_vdbg(dev, "%s: Create the input device and register it\n",
+ __func__);
+ md->input = input_allocate_device();
+ if (md->input == NULL) {
+ dev_err(dev, "%s: Error, failed to allocate input device\n",
+ __func__);
+ rc = -ENOSYS;
+ goto error_alloc_failed;
+ }
+
+ md->input->name = pdata->inp_dev_name;
+ scnprintf(md->phys, sizeof(md->phys)-1, "%s", dev_name(dev));
+ md->input->phys = md->phys;
+ md->input->id.bustype = cd->bus_ops->bustype;
+ md->input->dev.parent = dev;
+ md->input->open = cyttsp4_mt_open;
+ md->input->close = cyttsp4_mt_close;
+ input_set_drvdata(md->input, md);
+
+ /* get sysinfo */
+ md->si = &cd->sysinfo;
+ if (!md->si) {
+ dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
+ __func__, md->si);
+ goto error_get_sysinfo;
+ }
+
+ rc = cyttsp4_setup_input_device(cd);
+ if (rc)
+ goto error_init_input;
+
+ return 0;
+
+error_init_input:
+ input_free_device(md->input);
+error_get_sysinfo:
+ input_set_drvdata(md->input, NULL);
+error_alloc_failed:
+ dev_err(dev, "%s failed.\n", __func__);
+ return rc;
+}
+
+struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops,
+ struct device *dev, u16 irq, size_t xfer_buf_size)
+{
+ struct cyttsp4 *cd;
+ struct cyttsp4_platform_data *pdata = dev_get_platdata(dev);
+ unsigned long irq_flags;
+ int rc = 0;
+
+ if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) {
+ dev_err(dev, "%s: Missing platform data\n", __func__);
+ rc = -ENODEV;
+ goto error_no_pdata;
+ }
+
+ cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+ if (!cd) {
+ dev_err(dev, "%s: Error, kzalloc\n", __func__);
+ rc = -ENOMEM;
+ goto error_alloc_data;
+ }
+
+ cd->xfer_buf = kzalloc(xfer_buf_size, GFP_KERNEL);
+ if (!cd->xfer_buf) {
+ dev_err(dev, "%s: Error, kzalloc\n", __func__);
+ rc = -ENOMEM;
+ goto error_free_cd;
+ }
+
+ /* Initialize device info */
+ cd->dev = dev;
+ cd->pdata = pdata;
+ cd->cpdata = pdata->core_pdata;
+ cd->bus_ops = ops;
+
+ /* Initialize mutexes and spinlocks */
+ mutex_init(&cd->system_lock);
+ mutex_init(&cd->adap_lock);
+
+ /* Initialize wait queue */
+ init_waitqueue_head(&cd->wait_q);
+
+ /* Initialize works */
+ INIT_WORK(&cd->startup_work, cyttsp4_startup_work_function);
+ INIT_WORK(&cd->watchdog_work, cyttsp4_watchdog_work);
+
+ /* Initialize IRQ */
+ cd->irq = gpio_to_irq(cd->cpdata->irq_gpio);
+ if (cd->irq < 0) {
+ rc = -EINVAL;
+ goto error_free_xfer;
+ }
+
+ dev_set_drvdata(dev, cd);
+
+ /* Call platform init function */
+ if (cd->cpdata->init) {
+ dev_dbg(cd->dev, "%s: Init HW\n", __func__);
+ rc = cd->cpdata->init(cd->cpdata, 1, cd->dev);
+ } else {
+ dev_dbg(cd->dev, "%s: No HW INIT function\n", __func__);
+ rc = 0;
+ }
+ if (rc < 0)
+ dev_err(cd->dev, "%s: HW Init fail r=%d\n", __func__, rc);
+
+ dev_dbg(dev, "%s: initialize threaded irq=%d\n", __func__, cd->irq);
+ if (cd->cpdata->level_irq_udelay > 0)
+ /* use level triggered interrupts */
+ irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT;
+ else
+ /* use edge triggered interrupts */
+ irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+
+ rc = request_threaded_irq(cd->irq, NULL, cyttsp4_irq, irq_flags,
+ dev_name(dev), cd);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error, could not request irq\n", __func__);
+ goto error_request_irq;
+ }
+
+ /* Setup watchdog timer */
+ setup_timer(&cd->watchdog_timer, cyttsp4_watchdog_timer,
+ (unsigned long)cd);
+
+ /*
+ * call startup directly to ensure that the device
+ * is tested before leaving the probe
+ */
+ rc = cyttsp4_startup(cd);
+
+ /* Do not fail probe if startup fails but the device is detected */
+ if (rc < 0 && cd->mode == CY_MODE_UNKNOWN) {
+ dev_err(cd->dev, "%s: Fail initial startup r=%d\n",
+ __func__, rc);
+ goto error_startup;
+ }
+
+ rc = cyttsp4_mt_probe(cd);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error, fail mt probe\n", __func__);
+ goto error_startup;
+ }
+
+ pm_runtime_enable(dev);
+
+ return cd;
+
+error_startup:
+ cancel_work_sync(&cd->startup_work);
+ cyttsp4_stop_wd_timer(cd);
+ pm_runtime_disable(dev);
+ cyttsp4_free_si_ptrs(cd);
+ free_irq(cd->irq, cd);
+error_request_irq:
+ if (cd->cpdata->init)
+ cd->cpdata->init(cd->cpdata, 0, dev);
+error_free_xfer:
+ kfree(cd->xfer_buf);
+error_free_cd:
+ kfree(cd);
+error_alloc_data:
+error_no_pdata:
+ dev_err(dev, "%s failed.\n", __func__);
+ return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(cyttsp4_probe);
+
+static void cyttsp4_mt_release(struct cyttsp4_mt_data *md)
+{
+ input_unregister_device(md->input);
+ input_set_drvdata(md->input, NULL);
+}
+
+int cyttsp4_remove(struct cyttsp4 *cd)
+{
+ struct device *dev = cd->dev;
+
+ cyttsp4_mt_release(&cd->md);
+
+ /*
+ * Suspend the device before freeing the startup_work and stopping
+ * the watchdog since sleep function restarts watchdog on failure
+ */
+ pm_runtime_suspend(dev);
+ pm_runtime_disable(dev);
+
+ cancel_work_sync(&cd->startup_work);
+
+ cyttsp4_stop_wd_timer(cd);
+
+ free_irq(cd->irq, cd);
+ if (cd->cpdata->init)
+ cd->cpdata->init(cd->cpdata, 0, dev);
+ cyttsp4_free_si_ptrs(cd);
+ kfree(cd);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen core driver");
+MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/cyttsp4_core.h b/drivers/input/touchscreen/cyttsp4_core.h
new file mode 100644
index 000000000..8e0d4d490
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_core.h
@@ -0,0 +1,472 @@
+/*
+ * cyttsp4_core.h
+ * Cypress TrueTouch(TM) Standard Product V4 Core driver module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#ifndef _LINUX_CYTTSP4_CORE_H
+#define _LINUX_CYTTSP4_CORE_H
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/platform_data/cyttsp4.h>
+
+#define CY_REG_BASE 0x00
+
+#define CY_POST_CODEL_WDG_RST 0x01
+#define CY_POST_CODEL_CFG_DATA_CRC_FAIL 0x02
+#define CY_POST_CODEL_PANEL_TEST_FAIL 0x04
+
+#define CY_NUM_BTN_PER_REG 4
+
+/* touch record system information offset masks and shifts */
+#define CY_BYTE_OFS_MASK 0x1F
+#define CY_BOFS_MASK 0xE0
+#define CY_BOFS_SHIFT 5
+
+#define CY_TMA1036_TCH_REC_SIZE 6
+#define CY_TMA4XX_TCH_REC_SIZE 9
+#define CY_TMA1036_MAX_TCH 0x0E
+#define CY_TMA4XX_MAX_TCH 0x1E
+
+#define CY_NORMAL_ORIGIN 0 /* upper, left corner */
+#define CY_INVERT_ORIGIN 1 /* lower, right corner */
+
+/* helpers */
+#define GET_NUM_TOUCHES(x) ((x) & 0x1F)
+#define IS_LARGE_AREA(x) ((x) & 0x20)
+#define IS_BAD_PKT(x) ((x) & 0x20)
+#define IS_BOOTLOADER(hst_mode, reset_detect) \
+ ((hst_mode) & 0x01 || (reset_detect) != 0)
+#define IS_TMO(t) ((t) == 0)
+
+
+enum cyttsp_cmd_bits {
+ CY_CMD_COMPLETE = (1 << 6),
+};
+
+/* Timeout in ms. */
+#define CY_WATCHDOG_TIMEOUT 1000
+
+#define CY_MAX_PRINT_SIZE 512
+#ifdef VERBOSE_DEBUG
+#define CY_MAX_PRBUF_SIZE PIPE_BUF
+#define CY_PR_TRUNCATED " truncated..."
+#endif
+
+enum cyttsp4_ic_grpnum {
+ CY_IC_GRPNUM_RESERVED,
+ CY_IC_GRPNUM_CMD_REGS,
+ CY_IC_GRPNUM_TCH_REP,
+ CY_IC_GRPNUM_DATA_REC,
+ CY_IC_GRPNUM_TEST_REC,
+ CY_IC_GRPNUM_PCFG_REC,
+ CY_IC_GRPNUM_TCH_PARM_VAL,
+ CY_IC_GRPNUM_TCH_PARM_SIZE,
+ CY_IC_GRPNUM_RESERVED1,
+ CY_IC_GRPNUM_RESERVED2,
+ CY_IC_GRPNUM_OPCFG_REC,
+ CY_IC_GRPNUM_DDATA_REC,
+ CY_IC_GRPNUM_MDATA_REC,
+ CY_IC_GRPNUM_TEST_REGS,
+ CY_IC_GRPNUM_BTN_KEYS,
+ CY_IC_GRPNUM_TTHE_REGS,
+ CY_IC_GRPNUM_NUM
+};
+
+enum cyttsp4_int_state {
+ CY_INT_NONE,
+ CY_INT_IGNORE = (1 << 0),
+ CY_INT_MODE_CHANGE = (1 << 1),
+ CY_INT_EXEC_CMD = (1 << 2),
+ CY_INT_AWAKE = (1 << 3),
+};
+
+enum cyttsp4_mode {
+ CY_MODE_UNKNOWN,
+ CY_MODE_BOOTLOADER = (1 << 1),
+ CY_MODE_OPERATIONAL = (1 << 2),
+ CY_MODE_SYSINFO = (1 << 3),
+ CY_MODE_CAT = (1 << 4),
+ CY_MODE_STARTUP = (1 << 5),
+ CY_MODE_LOADER = (1 << 6),
+ CY_MODE_CHANGE_MODE = (1 << 7),
+ CY_MODE_CHANGED = (1 << 8),
+ CY_MODE_CMD_COMPLETE = (1 << 9),
+};
+
+enum cyttsp4_sleep_state {
+ SS_SLEEP_OFF,
+ SS_SLEEP_ON,
+ SS_SLEEPING,
+ SS_WAKING,
+};
+
+enum cyttsp4_startup_state {
+ STARTUP_NONE,
+ STARTUP_QUEUED,
+ STARTUP_RUNNING,
+};
+
+#define CY_NUM_REVCTRL 8
+struct cyttsp4_cydata {
+ u8 ttpidh;
+ u8 ttpidl;
+ u8 fw_ver_major;
+ u8 fw_ver_minor;
+ u8 revctrl[CY_NUM_REVCTRL];
+ u8 blver_major;
+ u8 blver_minor;
+ u8 jtag_si_id3;
+ u8 jtag_si_id2;
+ u8 jtag_si_id1;
+ u8 jtag_si_id0;
+ u8 mfgid_sz;
+ u8 cyito_idh;
+ u8 cyito_idl;
+ u8 cyito_verh;
+ u8 cyito_verl;
+ u8 ttsp_ver_major;
+ u8 ttsp_ver_minor;
+ u8 device_info;
+ u8 mfg_id[];
+} __packed;
+
+struct cyttsp4_test {
+ u8 post_codeh;
+ u8 post_codel;
+} __packed;
+
+struct cyttsp4_pcfg {
+ u8 electrodes_x;
+ u8 electrodes_y;
+ u8 len_xh;
+ u8 len_xl;
+ u8 len_yh;
+ u8 len_yl;
+ u8 res_xh;
+ u8 res_xl;
+ u8 res_yh;
+ u8 res_yl;
+ u8 max_zh;
+ u8 max_zl;
+ u8 panel_info0;
+} __packed;
+
+struct cyttsp4_tch_rec_params {
+ u8 loc;
+ u8 size;
+} __packed;
+
+#define CY_NUM_TCH_FIELDS 7
+#define CY_NUM_EXT_TCH_FIELDS 3
+struct cyttsp4_opcfg {
+ u8 cmd_ofs;
+ u8 rep_ofs;
+ u8 rep_szh;
+ u8 rep_szl;
+ u8 num_btns;
+ u8 tt_stat_ofs;
+ u8 obj_cfg0;
+ u8 max_tchs;
+ u8 tch_rec_size;
+ struct cyttsp4_tch_rec_params tch_rec_old[CY_NUM_TCH_FIELDS];
+ u8 btn_rec_size; /* btn record size (in bytes) */
+ u8 btn_diff_ofs; /* btn data loc, diff counts */
+ u8 btn_diff_size; /* btn size of diff counts (in bits) */
+ struct cyttsp4_tch_rec_params tch_rec_new[CY_NUM_EXT_TCH_FIELDS];
+} __packed;
+
+struct cyttsp4_sysinfo_ptr {
+ struct cyttsp4_cydata *cydata;
+ struct cyttsp4_test *test;
+ struct cyttsp4_pcfg *pcfg;
+ struct cyttsp4_opcfg *opcfg;
+ struct cyttsp4_ddata *ddata;
+ struct cyttsp4_mdata *mdata;
+} __packed;
+
+struct cyttsp4_sysinfo_data {
+ u8 hst_mode;
+ u8 reserved;
+ u8 map_szh;
+ u8 map_szl;
+ u8 cydata_ofsh;
+ u8 cydata_ofsl;
+ u8 test_ofsh;
+ u8 test_ofsl;
+ u8 pcfg_ofsh;
+ u8 pcfg_ofsl;
+ u8 opcfg_ofsh;
+ u8 opcfg_ofsl;
+ u8 ddata_ofsh;
+ u8 ddata_ofsl;
+ u8 mdata_ofsh;
+ u8 mdata_ofsl;
+} __packed;
+
+enum cyttsp4_tch_abs { /* for ordering within the extracted touch data array */
+ CY_TCH_X, /* X */
+ CY_TCH_Y, /* Y */
+ CY_TCH_P, /* P (Z) */
+ CY_TCH_T, /* TOUCH ID */
+ CY_TCH_E, /* EVENT ID */
+ CY_TCH_O, /* OBJECT ID */
+ CY_TCH_W, /* SIZE */
+ CY_TCH_MAJ, /* TOUCH_MAJOR */
+ CY_TCH_MIN, /* TOUCH_MINOR */
+ CY_TCH_OR, /* ORIENTATION */
+ CY_TCH_NUM_ABS
+};
+
+static const char * const cyttsp4_tch_abs_string[] = {
+ [CY_TCH_X] = "X",
+ [CY_TCH_Y] = "Y",
+ [CY_TCH_P] = "P",
+ [CY_TCH_T] = "T",
+ [CY_TCH_E] = "E",
+ [CY_TCH_O] = "O",
+ [CY_TCH_W] = "W",
+ [CY_TCH_MAJ] = "MAJ",
+ [CY_TCH_MIN] = "MIN",
+ [CY_TCH_OR] = "OR",
+ [CY_TCH_NUM_ABS] = "INVALID"
+};
+
+struct cyttsp4_touch {
+ int abs[CY_TCH_NUM_ABS];
+};
+
+struct cyttsp4_tch_abs_params {
+ size_t ofs; /* abs byte offset */
+ size_t size; /* size in bits */
+ size_t max; /* max value */
+ size_t bofs; /* bit offset */
+};
+
+struct cyttsp4_sysinfo_ofs {
+ size_t chip_type;
+ size_t cmd_ofs;
+ size_t rep_ofs;
+ size_t rep_sz;
+ size_t num_btns;
+ size_t num_btn_regs; /* ceil(num_btns/4) */
+ size_t tt_stat_ofs;
+ size_t tch_rec_size;
+ size_t obj_cfg0;
+ size_t max_tchs;
+ size_t mode_size;
+ size_t data_size;
+ size_t map_sz;
+ size_t max_x;
+ size_t x_origin; /* left or right corner */
+ size_t max_y;
+ size_t y_origin; /* upper or lower corner */
+ size_t max_p;
+ size_t cydata_ofs;
+ size_t test_ofs;
+ size_t pcfg_ofs;
+ size_t opcfg_ofs;
+ size_t ddata_ofs;
+ size_t mdata_ofs;
+ size_t cydata_size;
+ size_t test_size;
+ size_t pcfg_size;
+ size_t opcfg_size;
+ size_t ddata_size;
+ size_t mdata_size;
+ size_t btn_keys_size;
+ struct cyttsp4_tch_abs_params tch_abs[CY_TCH_NUM_ABS];
+ size_t btn_rec_size; /* btn record size (in bytes) */
+ size_t btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */
+ size_t btn_diff_size;/* btn size of diff counts (in bits) */
+};
+
+enum cyttsp4_btn_state {
+ CY_BTN_RELEASED,
+ CY_BTN_PRESSED,
+ CY_BTN_NUM_STATE
+};
+
+struct cyttsp4_btn {
+ bool enabled;
+ int state; /* CY_BTN_PRESSED, CY_BTN_RELEASED */
+ int key_code;
+};
+
+struct cyttsp4_sysinfo {
+ bool ready;
+ struct cyttsp4_sysinfo_data si_data;
+ struct cyttsp4_sysinfo_ptr si_ptrs;
+ struct cyttsp4_sysinfo_ofs si_ofs;
+ struct cyttsp4_btn *btn; /* button states */
+ u8 *btn_rec_data; /* button diff count data */
+ u8 *xy_mode; /* operational mode and status regs */
+ u8 *xy_data; /* operational touch regs */
+};
+
+struct cyttsp4_mt_data {
+ struct cyttsp4_mt_platform_data *pdata;
+ struct cyttsp4_sysinfo *si;
+ struct input_dev *input;
+ struct mutex report_lock;
+ bool is_suspended;
+ char phys[NAME_MAX];
+ int num_prv_tch;
+};
+
+struct cyttsp4 {
+ struct device *dev;
+ struct mutex system_lock;
+ struct mutex adap_lock;
+ enum cyttsp4_mode mode;
+ enum cyttsp4_sleep_state sleep_state;
+ enum cyttsp4_startup_state startup_state;
+ int int_status;
+ wait_queue_head_t wait_q;
+ int irq;
+ struct work_struct startup_work;
+ struct work_struct watchdog_work;
+ struct timer_list watchdog_timer;
+ struct cyttsp4_sysinfo sysinfo;
+ void *exclusive_dev;
+ int exclusive_waits;
+ atomic_t ignore_irq;
+ bool invalid_touch_app;
+ struct cyttsp4_mt_data md;
+ struct cyttsp4_platform_data *pdata;
+ struct cyttsp4_core_platform_data *cpdata;
+ const struct cyttsp4_bus_ops *bus_ops;
+ u8 *xfer_buf;
+#ifdef VERBOSE_DEBUG
+ u8 pr_buf[CY_MAX_PRBUF_SIZE];
+#endif
+};
+
+struct cyttsp4_bus_ops {
+ u16 bustype;
+ int (*write)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length,
+ const void *values);
+ int (*read)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length,
+ void *values);
+};
+
+enum cyttsp4_hst_mode_bits {
+ CY_HST_TOGGLE = (1 << 7),
+ CY_HST_MODE_CHANGE = (1 << 3),
+ CY_HST_MODE = (7 << 4),
+ CY_HST_OPERATE = (0 << 4),
+ CY_HST_SYSINFO = (1 << 4),
+ CY_HST_CAT = (2 << 4),
+ CY_HST_LOWPOW = (1 << 2),
+ CY_HST_SLEEP = (1 << 1),
+ CY_HST_RESET = (1 << 0),
+};
+
+/* abs settings */
+#define CY_IGNORE_VALUE 0xFFFF
+
+/* abs signal capabilities offsets in the frameworks array */
+enum cyttsp4_sig_caps {
+ CY_SIGNAL_OST,
+ CY_MIN_OST,
+ CY_MAX_OST,
+ CY_FUZZ_OST,
+ CY_FLAT_OST,
+ CY_NUM_ABS_SET /* number of signal capability fields */
+};
+
+/* abs axis signal offsets in the framworks array */
+enum cyttsp4_sig_ost {
+ CY_ABS_X_OST,
+ CY_ABS_Y_OST,
+ CY_ABS_P_OST,
+ CY_ABS_W_OST,
+ CY_ABS_ID_OST,
+ CY_ABS_MAJ_OST,
+ CY_ABS_MIN_OST,
+ CY_ABS_OR_OST,
+ CY_NUM_ABS_OST /* number of abs signals */
+};
+
+enum cyttsp4_flags {
+ CY_FLAG_NONE = 0x00,
+ CY_FLAG_HOVER = 0x04,
+ CY_FLAG_FLIP = 0x08,
+ CY_FLAG_INV_X = 0x10,
+ CY_FLAG_INV_Y = 0x20,
+ CY_FLAG_VKEYS = 0x40,
+};
+
+enum cyttsp4_object_id {
+ CY_OBJ_STANDARD_FINGER,
+ CY_OBJ_LARGE_OBJECT,
+ CY_OBJ_STYLUS,
+ CY_OBJ_HOVER,
+};
+
+enum cyttsp4_event_id {
+ CY_EV_NO_EVENT,
+ CY_EV_TOUCHDOWN,
+ CY_EV_MOVE, /* significant displacement (> act dist) */
+ CY_EV_LIFTOFF, /* record reports last position */
+};
+
+/* x-axis resolution of panel in pixels */
+#define CY_PCFG_RESOLUTION_X_MASK 0x7F
+
+/* y-axis resolution of panel in pixels */
+#define CY_PCFG_RESOLUTION_Y_MASK 0x7F
+
+/* x-axis, 0:origin is on left side of panel, 1: right */
+#define CY_PCFG_ORIGIN_X_MASK 0x80
+
+/* y-axis, 0:origin is on top side of panel, 1: bottom */
+#define CY_PCFG_ORIGIN_Y_MASK 0x80
+
+static inline int cyttsp4_adap_read(struct cyttsp4 *ts, u16 addr, int size,
+ void *buf)
+{
+ return ts->bus_ops->read(ts->dev, ts->xfer_buf, addr, size, buf);
+}
+
+static inline int cyttsp4_adap_write(struct cyttsp4 *ts, u16 addr, int size,
+ const void *buf)
+{
+ return ts->bus_ops->write(ts->dev, ts->xfer_buf, addr, size, buf);
+}
+
+extern struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops,
+ struct device *dev, u16 irq, size_t xfer_buf_size);
+extern int cyttsp4_remove(struct cyttsp4 *ts);
+int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
+ u8 length, const void *values);
+int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
+ u8 length, void *values);
+extern const struct dev_pm_ops cyttsp4_pm_ops;
+
+#endif /* _LINUX_CYTTSP4_CORE_H */
diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c
new file mode 100644
index 000000000..8e2012c79
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_i2c.c
@@ -0,0 +1,90 @@
+/*
+ * cyttsp_i2c.c
+ * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ * Copyright (C) 2013 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include "cyttsp4_core.h"
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+
+#define CYTTSP4_I2C_DATA_SIZE (3 * 256)
+
+static const struct cyttsp4_bus_ops cyttsp4_i2c_bus_ops = {
+ .bustype = BUS_I2C,
+ .write = cyttsp_i2c_write_block_data,
+ .read = cyttsp_i2c_read_block_data,
+};
+
+static int cyttsp4_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cyttsp4 *ts;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "I2C functionality not Supported\n");
+ return -EIO;
+ }
+
+ ts = cyttsp4_probe(&cyttsp4_i2c_bus_ops, &client->dev, client->irq,
+ CYTTSP4_I2C_DATA_SIZE);
+
+ if (IS_ERR(ts))
+ return PTR_ERR(ts);
+
+ return 0;
+}
+
+static int cyttsp4_i2c_remove(struct i2c_client *client)
+{
+ struct cyttsp4 *ts = i2c_get_clientdata(client);
+
+ cyttsp4_remove(ts);
+
+ return 0;
+}
+
+static const struct i2c_device_id cyttsp4_i2c_id[] = {
+ { CYTTSP4_I2C_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cyttsp4_i2c_id);
+
+static struct i2c_driver cyttsp4_i2c_driver = {
+ .driver = {
+ .name = CYTTSP4_I2C_NAME,
+ .owner = THIS_MODULE,
+ .pm = &cyttsp4_pm_ops,
+ },
+ .probe = cyttsp4_i2c_probe,
+ .remove = cyttsp4_i2c_remove,
+ .id_table = cyttsp4_i2c_id,
+};
+
+module_i2c_driver(cyttsp4_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver");
+MODULE_AUTHOR("Cypress");
+MODULE_ALIAS("i2c:cyttsp4");
diff --git a/drivers/input/touchscreen/cyttsp4_spi.c b/drivers/input/touchscreen/cyttsp4_spi.c
new file mode 100644
index 000000000..b19434ceb
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_spi.c
@@ -0,0 +1,200 @@
+/*
+ * Source for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ * Copyright (C) 2013 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include "cyttsp4_core.h"
+
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+
+#define CY_SPI_WR_OP 0x00 /* r/~w */
+#define CY_SPI_RD_OP 0x01
+#define CY_SPI_BITS_PER_WORD 8
+#define CY_SPI_A8_BIT 0x02
+#define CY_SPI_WR_HEADER_BYTES 2
+#define CY_SPI_RD_HEADER_BYTES 1
+#define CY_SPI_CMD_BYTES 2
+#define CY_SPI_SYNC_BYTE 0
+#define CY_SPI_SYNC_ACK 0x62 /* from TRM *A protocol */
+#define CY_SPI_DATA_SIZE (2 * 256)
+
+#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE)
+
+static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf,
+ u8 op, u16 reg, u8 *buf, int length)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct spi_message msg;
+ struct spi_transfer xfer[2];
+ u8 *wr_buf = &xfer_buf[0];
+ u8 rd_buf[CY_SPI_CMD_BYTES];
+ int retval;
+ int i;
+
+ if (length > CY_SPI_DATA_SIZE) {
+ dev_err(dev, "%s: length %d is too big.\n",
+ __func__, length);
+ return -EINVAL;
+ }
+
+ memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE);
+ memset(rd_buf, 0, CY_SPI_CMD_BYTES);
+
+ wr_buf[0] = op + (((reg >> 8) & 0x1) ? CY_SPI_A8_BIT : 0);
+ if (op == CY_SPI_WR_OP) {
+ wr_buf[1] = reg & 0xFF;
+ if (length > 0)
+ memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length);
+ }
+
+ memset(xfer, 0, sizeof(xfer));
+ spi_message_init(&msg);
+
+ /*
+ We set both TX and RX buffers because Cypress TTSP
+ requires full duplex operation.
+ */
+ xfer[0].tx_buf = wr_buf;
+ xfer[0].rx_buf = rd_buf;
+ switch (op) {
+ case CY_SPI_WR_OP:
+ xfer[0].len = length + CY_SPI_CMD_BYTES;
+ spi_message_add_tail(&xfer[0], &msg);
+ break;
+
+ case CY_SPI_RD_OP:
+ xfer[0].len = CY_SPI_RD_HEADER_BYTES;
+ spi_message_add_tail(&xfer[0], &msg);
+
+ xfer[1].rx_buf = buf;
+ xfer[1].len = length;
+ spi_message_add_tail(&xfer[1], &msg);
+ break;
+
+ default:
+ dev_err(dev, "%s: bad operation code=%d\n", __func__, op);
+ return -EINVAL;
+ }
+
+ retval = spi_sync(spi, &msg);
+ if (retval < 0) {
+ dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n",
+ __func__, retval, xfer[1].len, op);
+
+ /*
+ * do not return here since was a bad ACK sequence
+ * let the following ACK check handle any errors and
+ * allow silent retries
+ */
+ }
+
+ if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK) {
+ dev_dbg(dev, "%s: operation %d failed\n", __func__, op);
+
+ for (i = 0; i < CY_SPI_CMD_BYTES; i++)
+ dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n",
+ __func__, i, rd_buf[i]);
+ for (i = 0; i < length; i++)
+ dev_dbg(dev, "%s: test buf[%d]:0x%02x\n",
+ __func__, i, buf[i]);
+
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf,
+ u16 addr, u8 length, void *data)
+{
+ int rc;
+
+ rc = cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, NULL, 0);
+ if (rc)
+ return rc;
+ else
+ return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, addr, data,
+ length);
+}
+
+static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf,
+ u16 addr, u8 length, const void *data)
+{
+ return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, (void *)data,
+ length);
+}
+
+static const struct cyttsp4_bus_ops cyttsp_spi_bus_ops = {
+ .bustype = BUS_SPI,
+ .write = cyttsp_spi_write_block_data,
+ .read = cyttsp_spi_read_block_data,
+};
+
+static int cyttsp4_spi_probe(struct spi_device *spi)
+{
+ struct cyttsp4 *ts;
+ int error;
+
+ /* Set up SPI*/
+ spi->bits_per_word = CY_SPI_BITS_PER_WORD;
+ spi->mode = SPI_MODE_0;
+ error = spi_setup(spi);
+ if (error < 0) {
+ dev_err(&spi->dev, "%s: SPI setup error %d\n",
+ __func__, error);
+ return error;
+ }
+
+ ts = cyttsp4_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq,
+ CY_SPI_DATA_BUF_SIZE);
+
+ return PTR_ERR_OR_ZERO(ts);
+}
+
+static int cyttsp4_spi_remove(struct spi_device *spi)
+{
+ struct cyttsp4 *ts = spi_get_drvdata(spi);
+ cyttsp4_remove(ts);
+
+ return 0;
+}
+
+static struct spi_driver cyttsp4_spi_driver = {
+ .driver = {
+ .name = CYTTSP4_SPI_NAME,
+ .owner = THIS_MODULE,
+ .pm = &cyttsp4_pm_ops,
+ },
+ .probe = cyttsp4_spi_probe,
+ .remove = cyttsp4_spi_remove,
+};
+
+module_spi_driver(cyttsp4_spi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
+MODULE_AUTHOR("Cypress");
+MODULE_ALIAS("spi:cyttsp4");
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c
new file mode 100644
index 000000000..5b74e8b84
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_core.c
@@ -0,0 +1,638 @@
+/*
+ * Core Source for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include "cyttsp_core.h"
+
+/* Bootloader number of command keys */
+#define CY_NUM_BL_KEYS 8
+
+/* helpers */
+#define GET_NUM_TOUCHES(x) ((x) & 0x0F)
+#define IS_LARGE_AREA(x) (((x) & 0x10) >> 4)
+#define IS_BAD_PKT(x) ((x) & 0x20)
+#define IS_VALID_APP(x) ((x) & 0x01)
+#define IS_OPERATIONAL_ERR(x) ((x) & 0x3F)
+#define GET_HSTMODE(reg) (((reg) & 0x70) >> 4)
+#define GET_BOOTLOADERMODE(reg) (((reg) & 0x10) >> 4)
+
+#define CY_REG_BASE 0x00
+#define CY_REG_ACT_DIST 0x1E
+#define CY_REG_ACT_INTRVL 0x1D
+#define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL + 1)
+#define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT + 1)
+#define CY_MAXZ 255
+#define CY_DELAY_DFLT 20 /* ms */
+#define CY_DELAY_MAX 500
+#define CY_ACT_DIST_DFLT 0xF8
+#define CY_HNDSHK_BIT 0x80
+/* device mode bits */
+#define CY_OPERATE_MODE 0x00
+#define CY_SYSINFO_MODE 0x10
+/* power mode select bits */
+#define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */
+#define CY_DEEP_SLEEP_MODE 0x02
+#define CY_LOW_POWER_MODE 0x04
+
+/* Slots management */
+#define CY_MAX_FINGER 4
+#define CY_MAX_ID 16
+
+static const u8 bl_command[] = {
+ 0x00, /* file offset */
+ 0xFF, /* command */
+ 0xA5, /* exit bootloader command */
+ 0, 1, 2, 3, 4, 5, 6, 7 /* default keys */
+};
+
+static int ttsp_read_block_data(struct cyttsp *ts, u8 command,
+ u8 length, void *buf)
+{
+ int error;
+ int tries;
+
+ for (tries = 0; tries < CY_NUM_RETRY; tries++) {
+ error = ts->bus_ops->read(ts->dev, ts->xfer_buf, command,
+ length, buf);
+ if (!error)
+ return 0;
+
+ msleep(CY_DELAY_DFLT);
+ }
+
+ return -EIO;
+}
+
+static int ttsp_write_block_data(struct cyttsp *ts, u8 command,
+ u8 length, void *buf)
+{
+ int error;
+ int tries;
+
+ for (tries = 0; tries < CY_NUM_RETRY; tries++) {
+ error = ts->bus_ops->write(ts->dev, ts->xfer_buf, command,
+ length, buf);
+ if (!error)
+ return 0;
+
+ msleep(CY_DELAY_DFLT);
+ }
+
+ return -EIO;
+}
+
+static int ttsp_send_command(struct cyttsp *ts, u8 cmd)
+{
+ return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd);
+}
+
+static int cyttsp_handshake(struct cyttsp *ts)
+{
+ if (ts->pdata->use_hndshk)
+ return ttsp_send_command(ts,
+ ts->xy_data.hst_mode ^ CY_HNDSHK_BIT);
+
+ return 0;
+}
+
+static int cyttsp_load_bl_regs(struct cyttsp *ts)
+{
+ memset(&ts->bl_data, 0, sizeof(ts->bl_data));
+ ts->bl_data.bl_status = 0x10;
+
+ return ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->bl_data), &ts->bl_data);
+}
+
+static int cyttsp_exit_bl_mode(struct cyttsp *ts)
+{
+ int error;
+ u8 bl_cmd[sizeof(bl_command)];
+
+ memcpy(bl_cmd, bl_command, sizeof(bl_command));
+ if (ts->pdata->bl_keys)
+ memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS],
+ ts->pdata->bl_keys, CY_NUM_BL_KEYS);
+
+ error = ttsp_write_block_data(ts, CY_REG_BASE,
+ sizeof(bl_cmd), bl_cmd);
+ if (error)
+ return error;
+
+ /* wait for TTSP Device to complete the operation */
+ msleep(CY_DELAY_DFLT);
+
+ error = cyttsp_load_bl_regs(ts);
+ if (error)
+ return error;
+
+ if (GET_BOOTLOADERMODE(ts->bl_data.bl_status))
+ return -EIO;
+
+ return 0;
+}
+
+static int cyttsp_set_operational_mode(struct cyttsp *ts)
+{
+ int error;
+
+ error = ttsp_send_command(ts, CY_OPERATE_MODE);
+ if (error)
+ return error;
+
+ /* wait for TTSP Device to complete switch to Operational mode */
+ error = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->xy_data), &ts->xy_data);
+ if (error)
+ return error;
+
+ error = cyttsp_handshake(ts);
+ if (error)
+ return error;
+
+ return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0;
+}
+
+static int cyttsp_set_sysinfo_mode(struct cyttsp *ts)
+{
+ int error;
+
+ memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data));
+
+ /* switch to sysinfo mode */
+ error = ttsp_send_command(ts, CY_SYSINFO_MODE);
+ if (error)
+ return error;
+
+ /* read sysinfo registers */
+ msleep(CY_DELAY_DFLT);
+ error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data),
+ &ts->sysinfo_data);
+ if (error)
+ return error;
+
+ error = cyttsp_handshake(ts);
+ if (error)
+ return error;
+
+ if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl)
+ return -EIO;
+
+ return 0;
+}
+
+static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
+{
+ int retval = 0;
+
+ if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT ||
+ ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT ||
+ ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) {
+
+ u8 intrvl_ray[] = {
+ ts->pdata->act_intrvl,
+ ts->pdata->tch_tmout,
+ ts->pdata->lp_intrvl
+ };
+
+ /* set intrvl registers */
+ retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL,
+ sizeof(intrvl_ray), intrvl_ray);
+ msleep(CY_DELAY_DFLT);
+ }
+
+ return retval;
+}
+
+static int cyttsp_soft_reset(struct cyttsp *ts)
+{
+ unsigned long timeout;
+ int retval;
+
+ /* wait for interrupt to set ready completion */
+ reinit_completion(&ts->bl_ready);
+ ts->state = CY_BL_STATE;
+
+ enable_irq(ts->irq);
+
+ retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE);
+ if (retval)
+ goto out;
+
+ timeout = wait_for_completion_timeout(&ts->bl_ready,
+ msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX));
+ retval = timeout ? 0 : -EIO;
+
+out:
+ ts->state = CY_IDLE_STATE;
+ disable_irq(ts->irq);
+ return retval;
+}
+
+static int cyttsp_act_dist_setup(struct cyttsp *ts)
+{
+ u8 act_dist_setup = ts->pdata->act_dist;
+
+ /* Init gesture; active distance setup */
+ return ttsp_write_block_data(ts, CY_REG_ACT_DIST,
+ sizeof(act_dist_setup), &act_dist_setup);
+}
+
+static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids)
+{
+ ids[0] = xy_data->touch12_id >> 4;
+ ids[1] = xy_data->touch12_id & 0xF;
+ ids[2] = xy_data->touch34_id >> 4;
+ ids[3] = xy_data->touch34_id & 0xF;
+}
+
+static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data,
+ int idx)
+{
+ switch (idx) {
+ case 0:
+ return &xy_data->tch1;
+ case 1:
+ return &xy_data->tch2;
+ case 2:
+ return &xy_data->tch3;
+ case 3:
+ return &xy_data->tch4;
+ default:
+ return NULL;
+ }
+}
+
+static void cyttsp_report_tchdata(struct cyttsp *ts)
+{
+ struct cyttsp_xydata *xy_data = &ts->xy_data;
+ struct input_dev *input = ts->input;
+ int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat);
+ const struct cyttsp_tch *tch;
+ int ids[CY_MAX_ID];
+ int i;
+ DECLARE_BITMAP(used, CY_MAX_ID);
+
+ if (IS_LARGE_AREA(xy_data->tt_stat) == 1) {
+ /* terminate all active tracks */
+ num_tch = 0;
+ dev_dbg(ts->dev, "%s: Large area detected\n", __func__);
+ } else if (num_tch > CY_MAX_FINGER) {
+ /* terminate all active tracks */
+ num_tch = 0;
+ dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__);
+ } else if (IS_BAD_PKT(xy_data->tt_mode)) {
+ /* terminate all active tracks */
+ num_tch = 0;
+ dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__);
+ }
+
+ cyttsp_extract_track_ids(xy_data, ids);
+
+ bitmap_zero(used, CY_MAX_ID);
+
+ for (i = 0; i < num_tch; i++) {
+ tch = cyttsp_get_tch(xy_data, i);
+
+ input_mt_slot(input, ids[i]);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+ input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x));
+ input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y));
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z);
+
+ __set_bit(ids[i], used);
+ }
+
+ for (i = 0; i < CY_MAX_ID; i++) {
+ if (test_bit(i, used))
+ continue;
+
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+ }
+
+ input_sync(input);
+}
+
+static irqreturn_t cyttsp_irq(int irq, void *handle)
+{
+ struct cyttsp *ts = handle;
+ int error;
+
+ if (unlikely(ts->state == CY_BL_STATE)) {
+ complete(&ts->bl_ready);
+ goto out;
+ }
+
+ /* Get touch data from CYTTSP device */
+ error = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(struct cyttsp_xydata), &ts->xy_data);
+ if (error)
+ goto out;
+
+ /* provide flow control handshake */
+ error = cyttsp_handshake(ts);
+ if (error)
+ goto out;
+
+ if (unlikely(ts->state == CY_IDLE_STATE))
+ goto out;
+
+ if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) {
+ /*
+ * TTSP device has reset back to bootloader mode.
+ * Restore to operational mode.
+ */
+ error = cyttsp_exit_bl_mode(ts);
+ if (error) {
+ dev_err(ts->dev,
+ "Could not return to operational mode, err: %d\n",
+ error);
+ ts->state = CY_IDLE_STATE;
+ }
+ } else {
+ cyttsp_report_tchdata(ts);
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int cyttsp_power_on(struct cyttsp *ts)
+{
+ int error;
+
+ error = cyttsp_soft_reset(ts);
+ if (error)
+ return error;
+
+ error = cyttsp_load_bl_regs(ts);
+ if (error)
+ return error;
+
+ if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) &&
+ IS_VALID_APP(ts->bl_data.bl_status)) {
+ error = cyttsp_exit_bl_mode(ts);
+ if (error)
+ return error;
+ }
+
+ if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE ||
+ IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) {
+ return -ENODEV;
+ }
+
+ error = cyttsp_set_sysinfo_mode(ts);
+ if (error)
+ return error;
+
+ error = cyttsp_set_sysinfo_regs(ts);
+ if (error)
+ return error;
+
+ error = cyttsp_set_operational_mode(ts);
+ if (error)
+ return error;
+
+ /* init active distance */
+ error = cyttsp_act_dist_setup(ts);
+ if (error)
+ return error;
+
+ ts->state = CY_ACTIVE_STATE;
+
+ return 0;
+}
+
+static int cyttsp_enable(struct cyttsp *ts)
+{
+ int error;
+
+ /*
+ * The device firmware can wake on an I2C or SPI memory slave
+ * address match. So just reading a register is sufficient to
+ * wake up the device. The first read attempt will fail but it
+ * will wake it up making the second read attempt successful.
+ */
+ error = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->xy_data), &ts->xy_data);
+ if (error)
+ return error;
+
+ if (GET_HSTMODE(ts->xy_data.hst_mode))
+ return -EIO;
+
+ enable_irq(ts->irq);
+
+ return 0;
+}
+
+static int cyttsp_disable(struct cyttsp *ts)
+{
+ int error;
+
+ error = ttsp_send_command(ts, CY_LOW_POWER_MODE);
+ if (error)
+ return error;
+
+ disable_irq(ts->irq);
+
+ return 0;
+}
+
+static int __maybe_unused cyttsp_suspend(struct device *dev)
+{
+ struct cyttsp *ts = dev_get_drvdata(dev);
+ int retval = 0;
+
+ mutex_lock(&ts->input->mutex);
+
+ if (ts->input->users) {
+ retval = cyttsp_disable(ts);
+ if (retval == 0)
+ ts->suspended = true;
+ }
+
+ mutex_unlock(&ts->input->mutex);
+
+ return retval;
+}
+
+static int __maybe_unused cyttsp_resume(struct device *dev)
+{
+ struct cyttsp *ts = dev_get_drvdata(dev);
+
+ mutex_lock(&ts->input->mutex);
+
+ if (ts->input->users)
+ cyttsp_enable(ts);
+
+ ts->suspended = false;
+
+ mutex_unlock(&ts->input->mutex);
+
+ return 0;
+}
+
+SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume);
+EXPORT_SYMBOL_GPL(cyttsp_pm_ops);
+
+static int cyttsp_open(struct input_dev *dev)
+{
+ struct cyttsp *ts = input_get_drvdata(dev);
+ int retval = 0;
+
+ if (!ts->suspended)
+ retval = cyttsp_enable(ts);
+
+ return retval;
+}
+
+static void cyttsp_close(struct input_dev *dev)
+{
+ struct cyttsp *ts = input_get_drvdata(dev);
+
+ if (!ts->suspended)
+ cyttsp_disable(ts);
+}
+
+struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
+ struct device *dev, int irq, size_t xfer_buf_size)
+{
+ const struct cyttsp_platform_data *pdata = dev_get_platdata(dev);
+ struct cyttsp *ts;
+ struct input_dev *input_dev;
+ int error;
+
+ if (!pdata || !pdata->name || irq <= 0) {
+ error = -EINVAL;
+ goto err_out;
+ }
+
+ ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ts->dev = dev;
+ ts->input = input_dev;
+ ts->pdata = dev_get_platdata(dev);
+ ts->bus_ops = bus_ops;
+ ts->irq = irq;
+
+ init_completion(&ts->bl_ready);
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
+
+ if (pdata->init) {
+ error = pdata->init();
+ if (error) {
+ dev_err(ts->dev, "platform init failed, err: %d\n",
+ error);
+ goto err_free_mem;
+ }
+ }
+
+ input_dev->name = pdata->name;
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = bus_ops->bustype;
+ input_dev->dev.parent = ts->dev;
+
+ input_dev->open = cyttsp_open;
+ input_dev->close = cyttsp_close;
+
+ input_set_drvdata(input_dev, ts);
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, pdata->maxx, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, pdata->maxy, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, CY_MAXZ, 0, 0);
+
+ input_mt_init_slots(input_dev, CY_MAX_ID, 0);
+
+ error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ pdata->name, ts);
+ if (error) {
+ dev_err(ts->dev, "failed to request IRQ %d, err: %d\n",
+ ts->irq, error);
+ goto err_platform_exit;
+ }
+
+ disable_irq(ts->irq);
+
+ error = cyttsp_power_on(ts);
+ if (error)
+ goto err_free_irq;
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(ts->dev, "failed to register input device: %d\n",
+ error);
+ goto err_free_irq;
+ }
+
+ return ts;
+
+err_free_irq:
+ free_irq(ts->irq, ts);
+err_platform_exit:
+ if (pdata->exit)
+ pdata->exit();
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+err_out:
+ return ERR_PTR(error);
+}
+EXPORT_SYMBOL_GPL(cyttsp_probe);
+
+void cyttsp_remove(struct cyttsp *ts)
+{
+ free_irq(ts->irq, ts);
+ input_unregister_device(ts->input);
+ if (ts->pdata->exit)
+ ts->pdata->exit();
+ kfree(ts);
+}
+EXPORT_SYMBOL_GPL(cyttsp_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core");
+MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h
new file mode 100644
index 000000000..07074110a
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_core.h
@@ -0,0 +1,154 @@
+/*
+ * Header file for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+
+#ifndef __CYTTSP_CORE_H__
+#define __CYTTSP_CORE_H__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/input/cyttsp.h>
+
+#define CY_NUM_RETRY 16 /* max number of retries for read ops */
+
+struct cyttsp_tch {
+ __be16 x, y;
+ u8 z;
+} __packed;
+
+/* TrueTouch Standard Product Gen3 interface definition */
+struct cyttsp_xydata {
+ u8 hst_mode;
+ u8 tt_mode;
+ u8 tt_stat;
+ struct cyttsp_tch tch1;
+ u8 touch12_id;
+ struct cyttsp_tch tch2;
+ u8 gest_cnt;
+ u8 gest_id;
+ struct cyttsp_tch tch3;
+ u8 touch34_id;
+ struct cyttsp_tch tch4;
+ u8 tt_undef[3];
+ u8 act_dist;
+ u8 tt_reserved;
+} __packed;
+
+
+/* TTSP System Information interface definition */
+struct cyttsp_sysinfo_data {
+ u8 hst_mode;
+ u8 mfg_stat;
+ u8 mfg_cmd;
+ u8 cid[3];
+ u8 tt_undef1;
+ u8 uid[8];
+ u8 bl_verh;
+ u8 bl_verl;
+ u8 tts_verh;
+ u8 tts_verl;
+ u8 app_idh;
+ u8 app_idl;
+ u8 app_verh;
+ u8 app_verl;
+ u8 tt_undef[5];
+ u8 scn_typ;
+ u8 act_intrvl;
+ u8 tch_tmout;
+ u8 lp_intrvl;
+};
+
+/* TTSP Bootloader Register Map interface definition */
+#define CY_BL_CHKSUM_OK 0x01
+struct cyttsp_bootloader_data {
+ u8 bl_file;
+ u8 bl_status;
+ u8 bl_error;
+ u8 blver_hi;
+ u8 blver_lo;
+ u8 bld_blver_hi;
+ u8 bld_blver_lo;
+ u8 ttspver_hi;
+ u8 ttspver_lo;
+ u8 appid_hi;
+ u8 appid_lo;
+ u8 appver_hi;
+ u8 appver_lo;
+ u8 cid_0;
+ u8 cid_1;
+ u8 cid_2;
+};
+
+struct cyttsp;
+
+struct cyttsp_bus_ops {
+ u16 bustype;
+ int (*write)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length,
+ const void *values);
+ int (*read)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length,
+ void *values);
+};
+
+enum cyttsp_state {
+ CY_IDLE_STATE,
+ CY_ACTIVE_STATE,
+ CY_BL_STATE,
+};
+
+struct cyttsp {
+ struct device *dev;
+ int irq;
+ struct input_dev *input;
+ char phys[32];
+ const struct cyttsp_platform_data *pdata;
+ const struct cyttsp_bus_ops *bus_ops;
+ struct cyttsp_bootloader_data bl_data;
+ struct cyttsp_sysinfo_data sysinfo_data;
+ struct cyttsp_xydata xy_data;
+ struct completion bl_ready;
+ enum cyttsp_state state;
+ bool suspended;
+
+ u8 xfer_buf[] ____cacheline_aligned;
+};
+
+struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
+ struct device *dev, int irq, size_t xfer_buf_size);
+void cyttsp_remove(struct cyttsp *ts);
+
+int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
+ u8 length, const void *values);
+int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
+ u8 length, void *values);
+extern const struct dev_pm_ops cyttsp_pm_ops;
+
+#endif /* __CYTTSP_CORE_H__ */
diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c
new file mode 100644
index 000000000..63104a86a
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_i2c.c
@@ -0,0 +1,90 @@
+/*
+ * cyttsp_i2c.c
+ * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include "cyttsp_core.h"
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+
+#define CY_I2C_DATA_SIZE 128
+
+static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = {
+ .bustype = BUS_I2C,
+ .write = cyttsp_i2c_write_block_data,
+ .read = cyttsp_i2c_read_block_data,
+};
+
+static int cyttsp_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cyttsp *ts;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "I2C functionality not Supported\n");
+ return -EIO;
+ }
+
+ ts = cyttsp_probe(&cyttsp_i2c_bus_ops, &client->dev, client->irq,
+ CY_I2C_DATA_SIZE);
+
+ if (IS_ERR(ts))
+ return PTR_ERR(ts);
+
+ i2c_set_clientdata(client, ts);
+ return 0;
+}
+
+static int cyttsp_i2c_remove(struct i2c_client *client)
+{
+ struct cyttsp *ts = i2c_get_clientdata(client);
+
+ cyttsp_remove(ts);
+
+ return 0;
+}
+
+static const struct i2c_device_id cyttsp_i2c_id[] = {
+ { CY_I2C_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id);
+
+static struct i2c_driver cyttsp_i2c_driver = {
+ .driver = {
+ .name = CY_I2C_NAME,
+ .owner = THIS_MODULE,
+ .pm = &cyttsp_pm_ops,
+ },
+ .probe = cyttsp_i2c_probe,
+ .remove = cyttsp_i2c_remove,
+ .id_table = cyttsp_i2c_id,
+};
+
+module_i2c_driver(cyttsp_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver");
+MODULE_AUTHOR("Cypress");
+MODULE_ALIAS("i2c:cyttsp");
diff --git a/drivers/input/touchscreen/cyttsp_i2c_common.c b/drivers/input/touchscreen/cyttsp_i2c_common.c
new file mode 100644
index 000000000..ccefa56ca
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_i2c_common.c
@@ -0,0 +1,95 @@
+/*
+ * cyttsp_i2c_common.c
+ * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver.
+ * For use with Cypress Txx3xx and Txx4xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include "cyttsp4_core.h"
+
+int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf,
+ u16 addr, u8 length, void *values)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 client_addr = client->addr | ((addr >> 8) & 0x1);
+ u8 addr_lo = addr & 0xFF;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &addr_lo,
+ },
+ {
+ .addr = client_addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = values,
+ },
+ };
+ int retval;
+
+ retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (retval < 0)
+ return retval;
+
+ return retval != ARRAY_SIZE(msgs) ? -EIO : 0;
+}
+EXPORT_SYMBOL_GPL(cyttsp_i2c_read_block_data);
+
+int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf,
+ u16 addr, u8 length, const void *values)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 client_addr = client->addr | ((addr >> 8) & 0x1);
+ u8 addr_lo = addr & 0xFF;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client_addr,
+ .flags = 0,
+ .len = length + 1,
+ .buf = xfer_buf,
+ },
+ };
+ int retval;
+
+ xfer_buf[0] = addr_lo;
+ memcpy(&xfer_buf[1], values, length);
+
+ retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (retval < 0)
+ return retval;
+
+ return retval != ARRAY_SIZE(msgs) ? -EIO : 0;
+}
+EXPORT_SYMBOL_GPL(cyttsp_i2c_write_block_data);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c
new file mode 100644
index 000000000..4728bcb19
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_spi.c
@@ -0,0 +1,197 @@
+/*
+ * Source for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ * Copyright (C) 2013 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include "cyttsp_core.h"
+
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+
+#define CY_SPI_WR_OP 0x00 /* r/~w */
+#define CY_SPI_RD_OP 0x01
+#define CY_SPI_CMD_BYTES 4
+#define CY_SPI_SYNC_BYTE 2
+#define CY_SPI_SYNC_ACK1 0x62 /* from protocol v.2 */
+#define CY_SPI_SYNC_ACK2 0x9D /* from protocol v.2 */
+#define CY_SPI_DATA_SIZE 128
+#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE)
+#define CY_SPI_BITS_PER_WORD 8
+
+static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf,
+ u8 op, u16 reg, u8 *buf, int length)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct spi_message msg;
+ struct spi_transfer xfer[2];
+ u8 *wr_buf = &xfer_buf[0];
+ u8 *rd_buf = &xfer_buf[CY_SPI_DATA_BUF_SIZE];
+ int retval;
+ int i;
+
+ if (length > CY_SPI_DATA_SIZE) {
+ dev_err(dev, "%s: length %d is too big.\n",
+ __func__, length);
+ return -EINVAL;
+ }
+
+ memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE);
+ memset(rd_buf, 0, CY_SPI_DATA_BUF_SIZE);
+
+ wr_buf[0] = 0x00; /* header byte 0 */
+ wr_buf[1] = 0xFF; /* header byte 1 */
+ wr_buf[2] = reg; /* reg index */
+ wr_buf[3] = op; /* r/~w */
+ if (op == CY_SPI_WR_OP)
+ memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length);
+
+ memset(xfer, 0, sizeof(xfer));
+ spi_message_init(&msg);
+
+ /*
+ We set both TX and RX buffers because Cypress TTSP
+ requires full duplex operation.
+ */
+ xfer[0].tx_buf = wr_buf;
+ xfer[0].rx_buf = rd_buf;
+ switch (op) {
+ case CY_SPI_WR_OP:
+ xfer[0].len = length + CY_SPI_CMD_BYTES;
+ spi_message_add_tail(&xfer[0], &msg);
+ break;
+
+ case CY_SPI_RD_OP:
+ xfer[0].len = CY_SPI_CMD_BYTES;
+ spi_message_add_tail(&xfer[0], &msg);
+
+ xfer[1].rx_buf = buf;
+ xfer[1].len = length;
+ spi_message_add_tail(&xfer[1], &msg);
+ break;
+
+ default:
+ dev_err(dev, "%s: bad operation code=%d\n", __func__, op);
+ return -EINVAL;
+ }
+
+ retval = spi_sync(spi, &msg);
+ if (retval < 0) {
+ dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n",
+ __func__, retval, xfer[1].len, op);
+
+ /*
+ * do not return here since was a bad ACK sequence
+ * let the following ACK check handle any errors and
+ * allow silent retries
+ */
+ }
+
+ if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK1 ||
+ rd_buf[CY_SPI_SYNC_BYTE + 1] != CY_SPI_SYNC_ACK2) {
+ dev_dbg(dev, "%s: operation %d failed\n", __func__, op);
+
+ for (i = 0; i < CY_SPI_CMD_BYTES; i++)
+ dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n",
+ __func__, i, rd_buf[i]);
+ for (i = 0; i < length; i++)
+ dev_dbg(dev, "%s: test buf[%d]:0x%02x\n",
+ __func__, i, buf[i]);
+
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf,
+ u16 addr, u8 length, void *data)
+{
+ return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, addr, data,
+ length);
+}
+
+static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf,
+ u16 addr, u8 length, const void *data)
+{
+ return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, (void *)data,
+ length);
+}
+
+static const struct cyttsp_bus_ops cyttsp_spi_bus_ops = {
+ .bustype = BUS_SPI,
+ .write = cyttsp_spi_write_block_data,
+ .read = cyttsp_spi_read_block_data,
+};
+
+static int cyttsp_spi_probe(struct spi_device *spi)
+{
+ struct cyttsp *ts;
+ int error;
+
+ /* Set up SPI*/
+ spi->bits_per_word = CY_SPI_BITS_PER_WORD;
+ spi->mode = SPI_MODE_0;
+ error = spi_setup(spi);
+ if (error < 0) {
+ dev_err(&spi->dev, "%s: SPI setup error %d\n",
+ __func__, error);
+ return error;
+ }
+
+ ts = cyttsp_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq,
+ CY_SPI_DATA_BUF_SIZE * 2);
+ if (IS_ERR(ts))
+ return PTR_ERR(ts);
+
+ spi_set_drvdata(spi, ts);
+
+ return 0;
+}
+
+static int cyttsp_spi_remove(struct spi_device *spi)
+{
+ struct cyttsp *ts = spi_get_drvdata(spi);
+
+ cyttsp_remove(ts);
+
+ return 0;
+}
+
+static struct spi_driver cyttsp_spi_driver = {
+ .driver = {
+ .name = CY_SPI_NAME,
+ .owner = THIS_MODULE,
+ .pm = &cyttsp_pm_ops,
+ },
+ .probe = cyttsp_spi_probe,
+ .remove = cyttsp_spi_remove,
+};
+
+module_spi_driver(cyttsp_spi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
+MODULE_AUTHOR("Cypress");
+MODULE_ALIAS("spi:cyttsp");
diff --git a/drivers/input/touchscreen/da9034-ts.c b/drivers/input/touchscreen/da9034-ts.c
new file mode 100644
index 000000000..8264822dc
--- /dev/null
+++ b/drivers/input/touchscreen/da9034-ts.c
@@ -0,0 +1,368 @@
+/*
+ * Touchscreen driver for Dialog Semiconductor DA9034
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Fengwei Yin <fengwei.yin@marvell.com>
+ * Bin Yang <bin.yang@marvell.com>
+ * Eric Miao <eric.miao@marvell.com>
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/da903x.h>
+#include <linux/slab.h>
+
+#define DA9034_MANUAL_CTRL 0x50
+#define DA9034_LDO_ADC_EN (1 << 4)
+
+#define DA9034_AUTO_CTRL1 0x51
+
+#define DA9034_AUTO_CTRL2 0x52
+#define DA9034_AUTO_TSI_EN (1 << 3)
+#define DA9034_PEN_DETECT (1 << 4)
+
+#define DA9034_TSI_CTRL1 0x53
+#define DA9034_TSI_CTRL2 0x54
+#define DA9034_TSI_X_MSB 0x6c
+#define DA9034_TSI_Y_MSB 0x6d
+#define DA9034_TSI_XY_LSB 0x6e
+
+enum {
+ STATE_IDLE, /* wait for pendown */
+ STATE_BUSY, /* TSI busy sampling */
+ STATE_STOP, /* sample available */
+ STATE_WAIT, /* Wait to start next sample */
+};
+
+enum {
+ EVENT_PEN_DOWN,
+ EVENT_PEN_UP,
+ EVENT_TSI_READY,
+ EVENT_TIMEDOUT,
+};
+
+struct da9034_touch {
+ struct device *da9034_dev;
+ struct input_dev *input_dev;
+
+ struct delayed_work tsi_work;
+ struct notifier_block notifier;
+
+ int state;
+
+ int interval_ms;
+ int x_inverted;
+ int y_inverted;
+
+ int last_x;
+ int last_y;
+};
+
+static inline int is_pen_down(struct da9034_touch *touch)
+{
+ return da903x_query_status(touch->da9034_dev, DA9034_STATUS_PEN_DOWN);
+}
+
+static inline int detect_pen_down(struct da9034_touch *touch, int on)
+{
+ if (on)
+ return da903x_set_bits(touch->da9034_dev,
+ DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
+ else
+ return da903x_clr_bits(touch->da9034_dev,
+ DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
+}
+
+static int read_tsi(struct da9034_touch *touch)
+{
+ uint8_t _x, _y, _v;
+ int ret;
+
+ ret = da903x_read(touch->da9034_dev, DA9034_TSI_X_MSB, &_x);
+ if (ret)
+ return ret;
+
+ ret = da903x_read(touch->da9034_dev, DA9034_TSI_Y_MSB, &_y);
+ if (ret)
+ return ret;
+
+ ret = da903x_read(touch->da9034_dev, DA9034_TSI_XY_LSB, &_v);
+ if (ret)
+ return ret;
+
+ touch->last_x = ((_x << 2) & 0x3fc) | (_v & 0x3);
+ touch->last_y = ((_y << 2) & 0x3fc) | ((_v & 0xc) >> 2);
+
+ return 0;
+}
+
+static inline int start_tsi(struct da9034_touch *touch)
+{
+ return da903x_set_bits(touch->da9034_dev,
+ DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
+}
+
+static inline int stop_tsi(struct da9034_touch *touch)
+{
+ return da903x_clr_bits(touch->da9034_dev,
+ DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
+}
+
+static inline void report_pen_down(struct da9034_touch *touch)
+{
+ int x = touch->last_x;
+ int y = touch->last_y;
+
+ x &= 0xfff;
+ if (touch->x_inverted)
+ x = 1024 - x;
+ y &= 0xfff;
+ if (touch->y_inverted)
+ y = 1024 - y;
+
+ input_report_abs(touch->input_dev, ABS_X, x);
+ input_report_abs(touch->input_dev, ABS_Y, y);
+ input_report_key(touch->input_dev, BTN_TOUCH, 1);
+
+ input_sync(touch->input_dev);
+}
+
+static inline void report_pen_up(struct da9034_touch *touch)
+{
+ input_report_key(touch->input_dev, BTN_TOUCH, 0);
+ input_sync(touch->input_dev);
+}
+
+static void da9034_event_handler(struct da9034_touch *touch, int event)
+{
+ int err;
+
+ switch (touch->state) {
+ case STATE_IDLE:
+ if (event != EVENT_PEN_DOWN)
+ break;
+
+ /* Enable auto measurement of the TSI, this will
+ * automatically disable pen down detection
+ */
+ err = start_tsi(touch);
+ if (err)
+ goto err_reset;
+
+ touch->state = STATE_BUSY;
+ break;
+
+ case STATE_BUSY:
+ if (event != EVENT_TSI_READY)
+ break;
+
+ err = read_tsi(touch);
+ if (err)
+ goto err_reset;
+
+ /* Disable auto measurement of the TSI, so that
+ * pen down status will be available
+ */
+ err = stop_tsi(touch);
+ if (err)
+ goto err_reset;
+
+ touch->state = STATE_STOP;
+
+ /* FIXME: PEN_{UP/DOWN} events are expected to be
+ * available by stopping TSI, but this is found not
+ * always true, delay and simulate such an event
+ * here is more reliable
+ */
+ mdelay(1);
+ da9034_event_handler(touch,
+ is_pen_down(touch) ? EVENT_PEN_DOWN :
+ EVENT_PEN_UP);
+ break;
+
+ case STATE_STOP:
+ if (event == EVENT_PEN_DOWN) {
+ report_pen_down(touch);
+ schedule_delayed_work(&touch->tsi_work,
+ msecs_to_jiffies(touch->interval_ms));
+ touch->state = STATE_WAIT;
+ }
+
+ if (event == EVENT_PEN_UP) {
+ report_pen_up(touch);
+ touch->state = STATE_IDLE;
+ }
+ break;
+
+ case STATE_WAIT:
+ if (event != EVENT_TIMEDOUT)
+ break;
+
+ if (is_pen_down(touch)) {
+ start_tsi(touch);
+ touch->state = STATE_BUSY;
+ } else {
+ report_pen_up(touch);
+ touch->state = STATE_IDLE;
+ }
+ break;
+ }
+ return;
+
+err_reset:
+ touch->state = STATE_IDLE;
+ stop_tsi(touch);
+ detect_pen_down(touch, 1);
+}
+
+static void da9034_tsi_work(struct work_struct *work)
+{
+ struct da9034_touch *touch =
+ container_of(work, struct da9034_touch, tsi_work.work);
+
+ da9034_event_handler(touch, EVENT_TIMEDOUT);
+}
+
+static int da9034_touch_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct da9034_touch *touch =
+ container_of(nb, struct da9034_touch, notifier);
+
+ if (event & DA9034_EVENT_TSI_READY)
+ da9034_event_handler(touch, EVENT_TSI_READY);
+
+ if ((event & DA9034_EVENT_PEN_DOWN) && touch->state == STATE_IDLE)
+ da9034_event_handler(touch, EVENT_PEN_DOWN);
+
+ return 0;
+}
+
+static int da9034_touch_open(struct input_dev *dev)
+{
+ struct da9034_touch *touch = input_get_drvdata(dev);
+ int ret;
+
+ ret = da903x_register_notifier(touch->da9034_dev, &touch->notifier,
+ DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
+ if (ret)
+ return -EBUSY;
+
+ /* Enable ADC LDO */
+ ret = da903x_set_bits(touch->da9034_dev,
+ DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
+ if (ret)
+ return ret;
+
+ /* TSI_DELAY: 3 slots, TSI_SKIP: 3 slots */
+ ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL1, 0x1b);
+ if (ret)
+ return ret;
+
+ ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL2, 0x00);
+ if (ret)
+ return ret;
+
+ touch->state = STATE_IDLE;
+ detect_pen_down(touch, 1);
+
+ return 0;
+}
+
+static void da9034_touch_close(struct input_dev *dev)
+{
+ struct da9034_touch *touch = input_get_drvdata(dev);
+
+ da903x_unregister_notifier(touch->da9034_dev, &touch->notifier,
+ DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
+
+ cancel_delayed_work_sync(&touch->tsi_work);
+
+ touch->state = STATE_IDLE;
+ stop_tsi(touch);
+ detect_pen_down(touch, 0);
+
+ /* Disable ADC LDO */
+ da903x_clr_bits(touch->da9034_dev,
+ DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
+}
+
+
+static int da9034_touch_probe(struct platform_device *pdev)
+{
+ struct da9034_touch_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct da9034_touch *touch;
+ struct input_dev *input_dev;
+ int error;
+
+ touch = devm_kzalloc(&pdev->dev, sizeof(struct da9034_touch),
+ GFP_KERNEL);
+ if (!touch) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ touch->da9034_dev = pdev->dev.parent;
+
+ if (pdata) {
+ touch->interval_ms = pdata->interval_ms;
+ touch->x_inverted = pdata->x_inverted;
+ touch->y_inverted = pdata->y_inverted;
+ } else {
+ /* fallback into default */
+ touch->interval_ms = 10;
+ }
+
+ INIT_DELAYED_WORK(&touch->tsi_work, da9034_tsi_work);
+ touch->notifier.notifier_call = da9034_touch_notifier;
+
+ input_dev = devm_input_allocate_device(&pdev->dev);
+ if (!input_dev) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ input_dev->name = pdev->name;
+ input_dev->open = da9034_touch_open;
+ input_dev->close = da9034_touch_close;
+ input_dev->dev.parent = &pdev->dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(ABS_X, input_dev->absbit);
+ __set_bit(ABS_Y, input_dev->absbit);
+ input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ touch->input_dev = input_dev;
+ input_set_drvdata(input_dev, touch);
+
+ error = input_register_device(input_dev);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static struct platform_driver da9034_touch_driver = {
+ .driver = {
+ .name = "da9034-touch",
+ },
+ .probe = da9034_touch_probe,
+};
+module_platform_driver(da9034_touch_driver);
+
+MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034");
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>, Bin Yang <bin.yang@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9034-touch");
diff --git a/drivers/input/touchscreen/da9052_tsi.c b/drivers/input/touchscreen/da9052_tsi.c
new file mode 100644
index 000000000..5a013bb7b
--- /dev/null
+++ b/drivers/input/touchscreen/da9052_tsi.c
@@ -0,0 +1,348 @@
+/*
+ * TSI driver for Dialog DA9052
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/da9052.h>
+
+#define TSI_PEN_DOWN_STATUS 0x40
+
+struct da9052_tsi {
+ struct da9052 *da9052;
+ struct input_dev *dev;
+ struct delayed_work ts_pen_work;
+ struct mutex mutex;
+ bool stopped;
+ bool adc_on;
+};
+
+static void da9052_ts_adc_toggle(struct da9052_tsi *tsi, bool on)
+{
+ da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 0, on);
+ tsi->adc_on = on;
+}
+
+static irqreturn_t da9052_ts_pendwn_irq(int irq, void *data)
+{
+ struct da9052_tsi *tsi = data;
+
+ if (!tsi->stopped) {
+ /* Mask PEN_DOWN event and unmask TSI_READY event */
+ da9052_disable_irq_nosync(tsi->da9052, DA9052_IRQ_PENDOWN);
+ da9052_enable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
+
+ da9052_ts_adc_toggle(tsi, true);
+
+ schedule_delayed_work(&tsi->ts_pen_work, HZ / 50);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void da9052_ts_read(struct da9052_tsi *tsi)
+{
+ struct input_dev *input = tsi->dev;
+ int ret;
+ u16 x, y, z;
+ u8 v;
+
+ ret = da9052_reg_read(tsi->da9052, DA9052_TSI_X_MSB_REG);
+ if (ret < 0)
+ return;
+
+ x = (u16) ret;
+
+ ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Y_MSB_REG);
+ if (ret < 0)
+ return;
+
+ y = (u16) ret;
+
+ ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Z_MSB_REG);
+ if (ret < 0)
+ return;
+
+ z = (u16) ret;
+
+ ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG);
+ if (ret < 0)
+ return;
+
+ v = (u8) ret;
+
+ x = ((x << 2) & 0x3fc) | (v & 0x3);
+ y = ((y << 2) & 0x3fc) | ((v & 0xc) >> 2);
+ z = ((z << 2) & 0x3fc) | ((v & 0x30) >> 4);
+
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ input_report_abs(input, ABS_PRESSURE, z);
+ input_sync(input);
+}
+
+static irqreturn_t da9052_ts_datardy_irq(int irq, void *data)
+{
+ struct da9052_tsi *tsi = data;
+
+ da9052_ts_read(tsi);
+
+ return IRQ_HANDLED;
+}
+
+static void da9052_ts_pen_work(struct work_struct *work)
+{
+ struct da9052_tsi *tsi = container_of(work, struct da9052_tsi,
+ ts_pen_work.work);
+ if (!tsi->stopped) {
+ int ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG);
+ if (ret < 0 || (ret & TSI_PEN_DOWN_STATUS)) {
+ /* Pen is still DOWN (or read error) */
+ schedule_delayed_work(&tsi->ts_pen_work, HZ / 50);
+ } else {
+ struct input_dev *input = tsi->dev;
+
+ /* Pen UP */
+ da9052_ts_adc_toggle(tsi, false);
+
+ /* Report Pen UP */
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_sync(input);
+
+ /*
+ * FIXME: Fixes the unhandled irq issue when quick
+ * pen down and pen up events occurs
+ */
+ ret = da9052_reg_update(tsi->da9052,
+ DA9052_EVENT_B_REG, 0xC0, 0xC0);
+ if (ret < 0)
+ return;
+
+ /* Mask TSI_READY event and unmask PEN_DOWN event */
+ da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
+ da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
+ }
+ }
+}
+
+static int da9052_ts_configure_gpio(struct da9052 *da9052)
+{
+ int error;
+
+ error = da9052_reg_update(da9052, DA9052_GPIO_2_3_REG, 0x30, 0);
+ if (error < 0)
+ return error;
+
+ error = da9052_reg_update(da9052, DA9052_GPIO_4_5_REG, 0x33, 0);
+ if (error < 0)
+ return error;
+
+ error = da9052_reg_update(da9052, DA9052_GPIO_6_7_REG, 0x33, 0);
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+static int da9052_configure_tsi(struct da9052_tsi *tsi)
+{
+ int error;
+
+ error = da9052_ts_configure_gpio(tsi->da9052);
+ if (error)
+ return error;
+
+ /* Measure TSI sample every 1ms */
+ error = da9052_reg_update(tsi->da9052, DA9052_ADC_CONT_REG,
+ 1 << 6, 1 << 6);
+ if (error < 0)
+ return error;
+
+ /* TSI_DELAY: 3 slots, TSI_SKIP: 0 slots, TSI_MODE: XYZP */
+ error = da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 0xFC, 0xC0);
+ if (error < 0)
+ return error;
+
+ /* Supply TSIRef through LD09 */
+ error = da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x59);
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+static int da9052_ts_input_open(struct input_dev *input_dev)
+{
+ struct da9052_tsi *tsi = input_get_drvdata(input_dev);
+
+ tsi->stopped = false;
+ mb();
+
+ /* Unmask PEN_DOWN event */
+ da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
+
+ /* Enable Pen Detect Circuit */
+ return da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG,
+ 1 << 1, 1 << 1);
+}
+
+static void da9052_ts_input_close(struct input_dev *input_dev)
+{
+ struct da9052_tsi *tsi = input_get_drvdata(input_dev);
+
+ tsi->stopped = true;
+ mb();
+ da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
+ cancel_delayed_work_sync(&tsi->ts_pen_work);
+
+ if (tsi->adc_on) {
+ da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
+ da9052_ts_adc_toggle(tsi, false);
+
+ /*
+ * If ADC was on that means that pendwn IRQ was disabled
+ * twice and we need to enable it to keep enable/disable
+ * counter balanced. IRQ is still off though.
+ */
+ da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
+ }
+
+ /* Disable Pen Detect Circuit */
+ da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0);
+}
+
+static int da9052_ts_probe(struct platform_device *pdev)
+{
+ struct da9052 *da9052;
+ struct da9052_tsi *tsi;
+ struct input_dev *input_dev;
+ int error;
+
+ da9052 = dev_get_drvdata(pdev->dev.parent);
+ if (!da9052)
+ return -EINVAL;
+
+ tsi = kzalloc(sizeof(struct da9052_tsi), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!tsi || !input_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ tsi->da9052 = da9052;
+ tsi->dev = input_dev;
+ tsi->stopped = true;
+ INIT_DELAYED_WORK(&tsi->ts_pen_work, da9052_ts_pen_work);
+
+ input_dev->id.version = 0x0101;
+ input_dev->id.vendor = 0x15B6;
+ input_dev->id.product = 0x9052;
+ input_dev->name = "Dialog DA9052 TouchScreen Driver";
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->open = da9052_ts_input_open;
+ input_dev->close = da9052_ts_input_close;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1023, 0, 0);
+
+ input_set_drvdata(input_dev, tsi);
+
+ /* Disable Pen Detect Circuit */
+ da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0);
+
+ /* Disable ADC */
+ da9052_ts_adc_toggle(tsi, false);
+
+ error = da9052_request_irq(tsi->da9052, DA9052_IRQ_PENDOWN,
+ "pendown-irq", da9052_ts_pendwn_irq, tsi);
+ if (error) {
+ dev_err(tsi->da9052->dev,
+ "Failed to register PENDWN IRQ: %d\n", error);
+ goto err_free_mem;
+ }
+
+ error = da9052_request_irq(tsi->da9052, DA9052_IRQ_TSIREADY,
+ "tsiready-irq", da9052_ts_datardy_irq, tsi);
+ if (error) {
+ dev_err(tsi->da9052->dev,
+ "Failed to register TSIRDY IRQ :%d\n", error);
+ goto err_free_pendwn_irq;
+ }
+
+ /* Mask PEN_DOWN and TSI_READY events */
+ da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
+ da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
+
+ error = da9052_configure_tsi(tsi);
+ if (error)
+ goto err_free_datardy_irq;
+
+ error = input_register_device(tsi->dev);
+ if (error)
+ goto err_free_datardy_irq;
+
+ platform_set_drvdata(pdev, tsi);
+
+ return 0;
+
+err_free_datardy_irq:
+ da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi);
+err_free_pendwn_irq:
+ da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi);
+err_free_mem:
+ kfree(tsi);
+ input_free_device(input_dev);
+
+ return error;
+}
+
+static int da9052_ts_remove(struct platform_device *pdev)
+{
+ struct da9052_tsi *tsi = platform_get_drvdata(pdev);
+
+ da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x19);
+
+ da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi);
+ da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi);
+
+ input_unregister_device(tsi->dev);
+ kfree(tsi);
+
+ return 0;
+}
+
+static struct platform_driver da9052_tsi_driver = {
+ .probe = da9052_ts_probe,
+ .remove = da9052_ts_remove,
+ .driver = {
+ .name = "da9052-tsi",
+ },
+};
+
+module_platform_driver(da9052_tsi_driver);
+
+MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9052");
+MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-tsi");
diff --git a/drivers/input/touchscreen/dynapro.c b/drivers/input/touchscreen/dynapro.c
new file mode 100644
index 000000000..86237a910
--- /dev/null
+++ b/drivers/input/touchscreen/dynapro.c
@@ -0,0 +1,190 @@
+/*
+ * Dynapro serial touchscreen driver
+ *
+ * Copyright (c) 2009 Tias Guns
+ * Based on the inexio driver (c) Vojtech Pavlik and Dan Streetman and
+ * Richard Lemon
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2009/09/19 Tias Guns <tias@ulyssis.org>
+ * Copied inexio.c and edited for Dynapro protocol (from retired Xorg module)
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "Dynapro serial touchscreen driver"
+
+MODULE_AUTHOR("Tias Guns <tias@ulyssis.org>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define DYNAPRO_FORMAT_TOUCH_BIT 0x40
+#define DYNAPRO_FORMAT_LENGTH 3
+#define DYNAPRO_RESPONSE_BEGIN_BYTE 0x80
+
+#define DYNAPRO_MIN_XC 0
+#define DYNAPRO_MAX_XC 0x3ff
+#define DYNAPRO_MIN_YC 0
+#define DYNAPRO_MAX_YC 0x3ff
+
+#define DYNAPRO_GET_XC(data) (data[1] | ((data[0] & 0x38) << 4))
+#define DYNAPRO_GET_YC(data) (data[2] | ((data[0] & 0x07) << 7))
+#define DYNAPRO_GET_TOUCHED(data) (DYNAPRO_FORMAT_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct dynapro {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[DYNAPRO_FORMAT_LENGTH];
+ char phys[32];
+};
+
+static void dynapro_process_data(struct dynapro *pdynapro)
+{
+ struct input_dev *dev = pdynapro->dev;
+
+ if (DYNAPRO_FORMAT_LENGTH == ++pdynapro->idx) {
+ input_report_abs(dev, ABS_X, DYNAPRO_GET_XC(pdynapro->data));
+ input_report_abs(dev, ABS_Y, DYNAPRO_GET_YC(pdynapro->data));
+ input_report_key(dev, BTN_TOUCH,
+ DYNAPRO_GET_TOUCHED(pdynapro->data));
+ input_sync(dev);
+
+ pdynapro->idx = 0;
+ }
+}
+
+static irqreturn_t dynapro_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct dynapro *pdynapro = serio_get_drvdata(serio);
+
+ pdynapro->data[pdynapro->idx] = data;
+
+ if (DYNAPRO_RESPONSE_BEGIN_BYTE & pdynapro->data[0])
+ dynapro_process_data(pdynapro);
+ else
+ dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
+ pdynapro->data[0]);
+
+ return IRQ_HANDLED;
+}
+
+static void dynapro_disconnect(struct serio *serio)
+{
+ struct dynapro *pdynapro = serio_get_drvdata(serio);
+
+ input_get_device(pdynapro->dev);
+ input_unregister_device(pdynapro->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(pdynapro->dev);
+ kfree(pdynapro);
+}
+
+/*
+ * dynapro_connect() is the routine that is called when someone adds a
+ * new serio device that supports dynapro protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+
+static int dynapro_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct dynapro *pdynapro;
+ struct input_dev *input_dev;
+ int err;
+
+ pdynapro = kzalloc(sizeof(struct dynapro), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!pdynapro || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ pdynapro->serio = serio;
+ pdynapro->dev = input_dev;
+ snprintf(pdynapro->phys, sizeof(pdynapro->phys),
+ "%s/input0", serio->phys);
+
+ input_dev->name = "Dynapro Serial TouchScreen";
+ input_dev->phys = pdynapro->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_DYNAPRO;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0001;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(pdynapro->dev, ABS_X,
+ DYNAPRO_MIN_XC, DYNAPRO_MAX_XC, 0, 0);
+ input_set_abs_params(pdynapro->dev, ABS_Y,
+ DYNAPRO_MIN_YC, DYNAPRO_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, pdynapro);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(pdynapro->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(pdynapro);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id dynapro_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_DYNAPRO,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, dynapro_serio_ids);
+
+static struct serio_driver dynapro_drv = {
+ .driver = {
+ .name = "dynapro",
+ },
+ .description = DRIVER_DESC,
+ .id_table = dynapro_serio_ids,
+ .interrupt = dynapro_interrupt,
+ .connect = dynapro_connect,
+ .disconnect = dynapro_disconnect,
+};
+
+module_serio_driver(dynapro_drv);
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
new file mode 100644
index 000000000..e6aef3e48
--- /dev/null
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -0,0 +1,1156 @@
+/*
+ * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de>
+ * Daniel Wagener <daniel.wagener@kernelconcepts.de> (M09 firmware support)
+ * Lothar WaĂŸmann <LW@KARO-electronics.de> (DT support)
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This is a driver for the EDT "Polytouch" family of touch controllers
+ * based on the FocalTech FT5x06 line of chips.
+ *
+ * Development of this driver has been sponsored by Glyn:
+ * http://www.glyn.com/Products/Displays
+ */
+
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/input/edt-ft5x06.h>
+
+#define MAX_SUPPORT_POINTS 5
+
+#define WORK_REGISTER_THRESHOLD 0x00
+#define WORK_REGISTER_REPORT_RATE 0x08
+#define WORK_REGISTER_GAIN 0x30
+#define WORK_REGISTER_OFFSET 0x31
+#define WORK_REGISTER_NUM_X 0x33
+#define WORK_REGISTER_NUM_Y 0x34
+
+#define M09_REGISTER_THRESHOLD 0x80
+#define M09_REGISTER_GAIN 0x92
+#define M09_REGISTER_OFFSET 0x93
+#define M09_REGISTER_NUM_X 0x94
+#define M09_REGISTER_NUM_Y 0x95
+
+#define NO_REGISTER 0xff
+
+#define WORK_REGISTER_OPMODE 0x3c
+#define FACTORY_REGISTER_OPMODE 0x01
+
+#define TOUCH_EVENT_DOWN 0x00
+#define TOUCH_EVENT_UP 0x01
+#define TOUCH_EVENT_ON 0x02
+#define TOUCH_EVENT_RESERVED 0x03
+
+#define EDT_NAME_LEN 23
+#define EDT_SWITCH_MODE_RETRIES 10
+#define EDT_SWITCH_MODE_DELAY 5 /* msec */
+#define EDT_RAW_DATA_RETRIES 100
+#define EDT_RAW_DATA_DELAY 1 /* msec */
+
+enum edt_ver {
+ M06,
+ M09,
+};
+
+struct edt_reg_addr {
+ int reg_threshold;
+ int reg_report_rate;
+ int reg_gain;
+ int reg_offset;
+ int reg_num_x;
+ int reg_num_y;
+};
+
+struct edt_ft5x06_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ u16 num_x;
+ u16 num_y;
+
+ int reset_pin;
+ int irq_pin;
+ int wake_pin;
+
+#if defined(CONFIG_DEBUG_FS)
+ struct dentry *debug_dir;
+ u8 *raw_buffer;
+ size_t raw_bufsize;
+#endif
+
+ struct mutex mutex;
+ bool factory_mode;
+ int threshold;
+ int gain;
+ int offset;
+ int report_rate;
+
+ char name[EDT_NAME_LEN];
+
+ struct edt_reg_addr reg_addr;
+ enum edt_ver version;
+};
+
+static int edt_ft5x06_ts_readwrite(struct i2c_client *client,
+ u16 wr_len, u8 *wr_buf,
+ u16 rd_len, u8 *rd_buf)
+{
+ struct i2c_msg wrmsg[2];
+ int i = 0;
+ int ret;
+
+ if (wr_len) {
+ wrmsg[i].addr = client->addr;
+ wrmsg[i].flags = 0;
+ wrmsg[i].len = wr_len;
+ wrmsg[i].buf = wr_buf;
+ i++;
+ }
+ if (rd_len) {
+ wrmsg[i].addr = client->addr;
+ wrmsg[i].flags = I2C_M_RD;
+ wrmsg[i].len = rd_len;
+ wrmsg[i].buf = rd_buf;
+ i++;
+ }
+
+ ret = i2c_transfer(client->adapter, wrmsg, i);
+ if (ret < 0)
+ return ret;
+ if (ret != i)
+ return -EIO;
+
+ return 0;
+}
+
+static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata,
+ u8 *buf, int buflen)
+{
+ int i;
+ u8 crc = 0;
+
+ for (i = 0; i < buflen - 1; i++)
+ crc ^= buf[i];
+
+ if (crc != buf[buflen-1]) {
+ dev_err_ratelimited(&tsdata->client->dev,
+ "crc error: 0x%02x expected, got 0x%02x\n",
+ crc, buf[buflen-1]);
+ return false;
+ }
+
+ return true;
+}
+
+static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
+{
+ struct edt_ft5x06_ts_data *tsdata = dev_id;
+ struct device *dev = &tsdata->client->dev;
+ u8 cmd;
+ u8 rdbuf[29];
+ int i, type, x, y, id;
+ int offset, tplen, datalen;
+ int error;
+
+ switch (tsdata->version) {
+ case M06:
+ cmd = 0xf9; /* tell the controller to send touch data */
+ offset = 5; /* where the actual touch data starts */
+ tplen = 4; /* data comes in so called frames */
+ datalen = 26; /* how much bytes to listen for */
+ break;
+
+ case M09:
+ cmd = 0x02;
+ offset = 1;
+ tplen = 6;
+ datalen = 29;
+ break;
+
+ default:
+ goto out;
+ }
+
+ memset(rdbuf, 0, sizeof(rdbuf));
+
+ error = edt_ft5x06_ts_readwrite(tsdata->client,
+ sizeof(cmd), &cmd,
+ datalen, rdbuf);
+ if (error) {
+ dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",
+ error);
+ goto out;
+ }
+
+ /* M09 does not send header or CRC */
+ if (tsdata->version == M06) {
+ if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa ||
+ rdbuf[2] != datalen) {
+ dev_err_ratelimited(dev,
+ "Unexpected header: %02x%02x%02x!\n",
+ rdbuf[0], rdbuf[1], rdbuf[2]);
+ goto out;
+ }
+
+ if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen))
+ goto out;
+ }
+
+ for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
+ u8 *buf = &rdbuf[i * tplen + offset];
+ bool down;
+
+ type = buf[0] >> 6;
+ /* ignore Reserved events */
+ if (type == TOUCH_EVENT_RESERVED)
+ continue;
+
+ /* M06 sometimes sends bogus coordinates in TOUCH_DOWN */
+ if (tsdata->version == M06 && type == TOUCH_EVENT_DOWN)
+ continue;
+
+ x = ((buf[0] << 8) | buf[1]) & 0x0fff;
+ y = ((buf[2] << 8) | buf[3]) & 0x0fff;
+ id = (buf[2] >> 4) & 0x0f;
+ down = type != TOUCH_EVENT_UP;
+
+ input_mt_slot(tsdata->input, id);
+ input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down);
+
+ if (!down)
+ continue;
+
+ input_report_abs(tsdata->input, ABS_MT_POSITION_X, x);
+ input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y);
+ }
+
+ input_mt_report_pointer_emulation(tsdata->input, true);
+ input_sync(tsdata->input);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
+ u8 addr, u8 value)
+{
+ u8 wrbuf[4];
+
+ switch (tsdata->version) {
+ case M06:
+ wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+ wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+ wrbuf[2] = value;
+ wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
+ return edt_ft5x06_ts_readwrite(tsdata->client, 4,
+ wrbuf, 0, NULL);
+ case M09:
+ wrbuf[0] = addr;
+ wrbuf[1] = value;
+
+ return edt_ft5x06_ts_readwrite(tsdata->client, 2,
+ wrbuf, 0, NULL);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
+ u8 addr)
+{
+ u8 wrbuf[2], rdbuf[2];
+ int error;
+
+ switch (tsdata->version) {
+ case M06:
+ wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+ wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+ wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40;
+
+ error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2,
+ rdbuf);
+ if (error)
+ return error;
+
+ if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) {
+ dev_err(&tsdata->client->dev,
+ "crc error: 0x%02x expected, got 0x%02x\n",
+ wrbuf[0] ^ wrbuf[1] ^ rdbuf[0],
+ rdbuf[1]);
+ return -EIO;
+ }
+ break;
+
+ case M09:
+ wrbuf[0] = addr;
+ error = edt_ft5x06_ts_readwrite(tsdata->client, 1,
+ wrbuf, 1, rdbuf);
+ if (error)
+ return error;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return rdbuf[0];
+}
+
+struct edt_ft5x06_attribute {
+ struct device_attribute dattr;
+ size_t field_offset;
+ u8 limit_low;
+ u8 limit_high;
+ u8 addr_m06;
+ u8 addr_m09;
+};
+
+#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, \
+ _limit_low, _limit_high) \
+ struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \
+ .dattr = __ATTR(_field, _mode, \
+ edt_ft5x06_setting_show, \
+ edt_ft5x06_setting_store), \
+ .field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \
+ .addr_m06 = _addr_m06, \
+ .addr_m09 = _addr_m09, \
+ .limit_low = _limit_low, \
+ .limit_high = _limit_high, \
+ }
+
+static ssize_t edt_ft5x06_setting_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct edt_ft5x06_attribute *attr =
+ container_of(dattr, struct edt_ft5x06_attribute, dattr);
+ u8 *field = (u8 *)tsdata + attr->field_offset;
+ int val;
+ size_t count = 0;
+ int error = 0;
+ u8 addr;
+
+ mutex_lock(&tsdata->mutex);
+
+ if (tsdata->factory_mode) {
+ error = -EIO;
+ goto out;
+ }
+
+ switch (tsdata->version) {
+ case M06:
+ addr = attr->addr_m06;
+ break;
+
+ case M09:
+ addr = attr->addr_m09;
+ break;
+
+ default:
+ error = -ENODEV;
+ goto out;
+ }
+
+ if (addr != NO_REGISTER) {
+ val = edt_ft5x06_register_read(tsdata, addr);
+ if (val < 0) {
+ error = val;
+ dev_err(&tsdata->client->dev,
+ "Failed to fetch attribute %s, error %d\n",
+ dattr->attr.name, error);
+ goto out;
+ }
+ } else {
+ val = *field;
+ }
+
+ if (val != *field) {
+ dev_warn(&tsdata->client->dev,
+ "%s: read (%d) and stored value (%d) differ\n",
+ dattr->attr.name, val, *field);
+ *field = val;
+ }
+
+ count = scnprintf(buf, PAGE_SIZE, "%d\n", val);
+out:
+ mutex_unlock(&tsdata->mutex);
+ return error ?: count;
+}
+
+static ssize_t edt_ft5x06_setting_store(struct device *dev,
+ struct device_attribute *dattr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct edt_ft5x06_attribute *attr =
+ container_of(dattr, struct edt_ft5x06_attribute, dattr);
+ u8 *field = (u8 *)tsdata + attr->field_offset;
+ unsigned int val;
+ int error;
+ u8 addr;
+
+ mutex_lock(&tsdata->mutex);
+
+ if (tsdata->factory_mode) {
+ error = -EIO;
+ goto out;
+ }
+
+ error = kstrtouint(buf, 0, &val);
+ if (error)
+ goto out;
+
+ if (val < attr->limit_low || val > attr->limit_high) {
+ error = -ERANGE;
+ goto out;
+ }
+
+ switch (tsdata->version) {
+ case M06:
+ addr = attr->addr_m06;
+ break;
+
+ case M09:
+ addr = attr->addr_m09;
+ break;
+
+ default:
+ error = -ENODEV;
+ goto out;
+ }
+
+ if (addr != NO_REGISTER) {
+ error = edt_ft5x06_register_write(tsdata, addr, val);
+ if (error) {
+ dev_err(&tsdata->client->dev,
+ "Failed to update attribute %s, error: %d\n",
+ dattr->attr.name, error);
+ goto out;
+ }
+ }
+ *field = val;
+
+out:
+ mutex_unlock(&tsdata->mutex);
+ return error ?: count;
+}
+
+static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN,
+ M09_REGISTER_GAIN, 0, 31);
+static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET,
+ M09_REGISTER_OFFSET, 0, 31);
+static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD,
+ M09_REGISTER_THRESHOLD, 20, 80);
+static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE,
+ NO_REGISTER, 3, 14);
+
+static struct attribute *edt_ft5x06_attrs[] = {
+ &edt_ft5x06_attr_gain.dattr.attr,
+ &edt_ft5x06_attr_offset.dattr.attr,
+ &edt_ft5x06_attr_threshold.dattr.attr,
+ &edt_ft5x06_attr_report_rate.dattr.attr,
+ NULL
+};
+
+static const struct attribute_group edt_ft5x06_attr_group = {
+ .attrs = edt_ft5x06_attrs,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
+{
+ struct i2c_client *client = tsdata->client;
+ int retries = EDT_SWITCH_MODE_RETRIES;
+ int ret;
+ int error;
+
+ disable_irq(client->irq);
+
+ if (!tsdata->raw_buffer) {
+ tsdata->raw_bufsize = tsdata->num_x * tsdata->num_y *
+ sizeof(u16);
+ tsdata->raw_buffer = kzalloc(tsdata->raw_bufsize, GFP_KERNEL);
+ if (!tsdata->raw_buffer) {
+ error = -ENOMEM;
+ goto err_out;
+ }
+ }
+
+ /* mode register is 0x3c when in the work mode */
+ if (tsdata->version == M09)
+ goto m09_out;
+
+ error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to switch to factory mode, error %d\n", error);
+ goto err_out;
+ }
+
+ tsdata->factory_mode = true;
+ do {
+ mdelay(EDT_SWITCH_MODE_DELAY);
+ /* mode register is 0x01 when in factory mode */
+ ret = edt_ft5x06_register_read(tsdata, FACTORY_REGISTER_OPMODE);
+ if (ret == 0x03)
+ break;
+ } while (--retries > 0);
+
+ if (retries == 0) {
+ dev_err(&client->dev, "not in factory mode after %dms.\n",
+ EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
+ error = -EIO;
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ kfree(tsdata->raw_buffer);
+ tsdata->raw_buffer = NULL;
+ tsdata->factory_mode = false;
+ enable_irq(client->irq);
+
+ return error;
+
+m09_out:
+ dev_err(&client->dev, "No factory mode support for M09\n");
+ return -EINVAL;
+
+}
+
+static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
+{
+ struct i2c_client *client = tsdata->client;
+ int retries = EDT_SWITCH_MODE_RETRIES;
+ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+ int ret;
+ int error;
+
+ /* mode register is 0x01 when in the factory mode */
+ error = edt_ft5x06_register_write(tsdata, FACTORY_REGISTER_OPMODE, 0x1);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to switch to work mode, error: %d\n", error);
+ return error;
+ }
+
+ tsdata->factory_mode = false;
+
+ do {
+ mdelay(EDT_SWITCH_MODE_DELAY);
+ /* mode register is 0x01 when in factory mode */
+ ret = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OPMODE);
+ if (ret == 0x01)
+ break;
+ } while (--retries > 0);
+
+ if (retries == 0) {
+ dev_err(&client->dev, "not in work mode after %dms.\n",
+ EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
+ tsdata->factory_mode = true;
+ return -EIO;
+ }
+
+ kfree(tsdata->raw_buffer);
+ tsdata->raw_buffer = NULL;
+
+ /* restore parameters */
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold,
+ 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_report_rate)
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
+ tsdata->report_rate);
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode)
+{
+ struct edt_ft5x06_ts_data *tsdata = data;
+
+ *mode = tsdata->factory_mode;
+
+ return 0;
+};
+
+static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode)
+{
+ struct edt_ft5x06_ts_data *tsdata = data;
+ int retval = 0;
+
+ if (mode > 1)
+ return -ERANGE;
+
+ mutex_lock(&tsdata->mutex);
+
+ if (mode != tsdata->factory_mode) {
+ retval = mode ? edt_ft5x06_factory_mode(tsdata) :
+ edt_ft5x06_work_mode(tsdata);
+ }
+
+ mutex_unlock(&tsdata->mutex);
+
+ return retval;
+};
+
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get,
+ edt_ft5x06_debugfs_mode_set, "%llu\n");
+
+static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
+ char __user *buf, size_t count, loff_t *off)
+{
+ struct edt_ft5x06_ts_data *tsdata = file->private_data;
+ struct i2c_client *client = tsdata->client;
+ int retries = EDT_RAW_DATA_RETRIES;
+ int val, i, error;
+ size_t read = 0;
+ int colbytes;
+ char wrbuf[3];
+ u8 *rdbuf;
+
+ if (*off < 0 || *off >= tsdata->raw_bufsize)
+ return 0;
+
+ mutex_lock(&tsdata->mutex);
+
+ if (!tsdata->factory_mode || !tsdata->raw_buffer) {
+ error = -EIO;
+ goto out;
+ }
+
+ error = edt_ft5x06_register_write(tsdata, 0x08, 0x01);
+ if (error) {
+ dev_dbg(&client->dev,
+ "failed to write 0x08 register, error %d\n", error);
+ goto out;
+ }
+
+ do {
+ msleep(EDT_RAW_DATA_DELAY);
+ val = edt_ft5x06_register_read(tsdata, 0x08);
+ if (val < 1)
+ break;
+ } while (--retries > 0);
+
+ if (val < 0) {
+ error = val;
+ dev_dbg(&client->dev,
+ "failed to read 0x08 register, error %d\n", error);
+ goto out;
+ }
+
+ if (retries == 0) {
+ dev_dbg(&client->dev,
+ "timed out waiting for register to settle\n");
+ error = -ETIMEDOUT;
+ goto out;
+ }
+
+ rdbuf = tsdata->raw_buffer;
+ colbytes = tsdata->num_y * sizeof(u16);
+
+ wrbuf[0] = 0xf5;
+ wrbuf[1] = 0x0e;
+ for (i = 0; i < tsdata->num_x; i++) {
+ wrbuf[2] = i; /* column index */
+ error = edt_ft5x06_ts_readwrite(tsdata->client,
+ sizeof(wrbuf), wrbuf,
+ colbytes, rdbuf);
+ if (error)
+ goto out;
+
+ rdbuf += colbytes;
+ }
+
+ read = min_t(size_t, count, tsdata->raw_bufsize - *off);
+ if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) {
+ error = -EFAULT;
+ goto out;
+ }
+
+ *off += read;
+out:
+ mutex_unlock(&tsdata->mutex);
+ return error ?: read;
+};
+
+static const struct file_operations debugfs_raw_data_fops = {
+ .open = simple_open,
+ .read = edt_ft5x06_debugfs_raw_data_read,
+};
+
+static void
+edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
+ const char *debugfs_name)
+{
+ tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL);
+ if (!tsdata->debug_dir)
+ return;
+
+ debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x);
+ debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y);
+
+ debugfs_create_file("mode", S_IRUSR | S_IWUSR,
+ tsdata->debug_dir, tsdata, &debugfs_mode_fops);
+ debugfs_create_file("raw_data", S_IRUSR,
+ tsdata->debug_dir, tsdata, &debugfs_raw_data_fops);
+}
+
+static void
+edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
+{
+ debugfs_remove_recursive(tsdata->debug_dir);
+ kfree(tsdata->raw_buffer);
+}
+
+#else
+
+static inline void
+edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
+ const char *debugfs_name)
+{
+}
+
+static inline void
+edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
+{
+}
+
+#endif /* CONFIG_DEBUGFS */
+
+static int edt_ft5x06_ts_reset(struct i2c_client *client,
+ struct edt_ft5x06_ts_data *tsdata)
+{
+ int error;
+
+ if (gpio_is_valid(tsdata->wake_pin)) {
+ error = devm_gpio_request_one(&client->dev,
+ tsdata->wake_pin, GPIOF_OUT_INIT_LOW,
+ "edt-ft5x06 wake");
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to request GPIO %d as wake pin, error %d\n",
+ tsdata->wake_pin, error);
+ return error;
+ }
+
+ msleep(5);
+ gpio_set_value(tsdata->wake_pin, 1);
+ }
+ if (gpio_is_valid(tsdata->reset_pin)) {
+ /* this pulls reset down, enabling the low active reset */
+ error = devm_gpio_request_one(&client->dev,
+ tsdata->reset_pin, GPIOF_OUT_INIT_LOW,
+ "edt-ft5x06 reset");
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to request GPIO %d as reset pin, error %d\n",
+ tsdata->reset_pin, error);
+ return error;
+ }
+
+ msleep(5);
+ gpio_set_value(tsdata->reset_pin, 1);
+ msleep(300);
+ }
+
+ return 0;
+}
+
+static int edt_ft5x06_ts_identify(struct i2c_client *client,
+ struct edt_ft5x06_ts_data *tsdata,
+ char *fw_version)
+{
+ u8 rdbuf[EDT_NAME_LEN];
+ char *p;
+ int error;
+ char *model_name = tsdata->name;
+
+ /* see what we find if we assume it is a M06 *
+ * if we get less than EDT_NAME_LEN, we don't want
+ * to have garbage in there
+ */
+ memset(rdbuf, 0, sizeof(rdbuf));
+ error = edt_ft5x06_ts_readwrite(client, 1, "\xbb",
+ EDT_NAME_LEN - 1, rdbuf);
+ if (error)
+ return error;
+
+ /* if we find something consistent, stay with that assumption
+ * at least M09 won't send 3 bytes here
+ */
+ if (!(strncasecmp(rdbuf + 1, "EP0", 3))) {
+ tsdata->version = M06;
+
+ /* remove last '$' end marker */
+ rdbuf[EDT_NAME_LEN - 1] = '\0';
+ if (rdbuf[EDT_NAME_LEN - 2] == '$')
+ rdbuf[EDT_NAME_LEN - 2] = '\0';
+
+ /* look for Model/Version separator */
+ p = strchr(rdbuf, '*');
+ if (p)
+ *p++ = '\0';
+ strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN);
+ strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
+ } else {
+ /* since there are only two versions around (M06, M09) */
+ tsdata->version = M09;
+
+ error = edt_ft5x06_ts_readwrite(client, 1, "\xA6",
+ 2, rdbuf);
+ if (error)
+ return error;
+
+ strlcpy(fw_version, rdbuf, 2);
+
+ error = edt_ft5x06_ts_readwrite(client, 1, "\xA8",
+ 1, rdbuf);
+ if (error)
+ return error;
+
+ snprintf(model_name, EDT_NAME_LEN, "EP0%i%i0M09",
+ rdbuf[0] >> 4, rdbuf[0] & 0x0F);
+ }
+
+ return 0;
+}
+
+#define EDT_ATTR_CHECKSET(name, reg) \
+do { \
+ if (pdata->name >= edt_ft5x06_attr_##name.limit_low && \
+ pdata->name <= edt_ft5x06_attr_##name.limit_high) \
+ edt_ft5x06_register_write(tsdata, reg, pdata->name); \
+} while (0)
+
+#define EDT_GET_PROP(name, reg) { \
+ u32 val; \
+ if (of_property_read_u32(np, #name, &val) == 0) \
+ edt_ft5x06_register_write(tsdata, reg, val); \
+}
+
+static void edt_ft5x06_ts_get_dt_defaults(struct device_node *np,
+ struct edt_ft5x06_ts_data *tsdata)
+{
+ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
+ EDT_GET_PROP(threshold, reg_addr->reg_threshold);
+ EDT_GET_PROP(gain, reg_addr->reg_gain);
+ EDT_GET_PROP(offset, reg_addr->reg_offset);
+}
+
+static void
+edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata,
+ const struct edt_ft5x06_platform_data *pdata)
+{
+ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
+ if (!pdata->use_parameters)
+ return;
+
+ /* pick up defaults from the platform data */
+ EDT_ATTR_CHECKSET(threshold, reg_addr->reg_threshold);
+ EDT_ATTR_CHECKSET(gain, reg_addr->reg_gain);
+ EDT_ATTR_CHECKSET(offset, reg_addr->reg_offset);
+ if (reg_addr->reg_report_rate != NO_REGISTER)
+ EDT_ATTR_CHECKSET(report_rate, reg_addr->reg_report_rate);
+}
+
+static void
+edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
+{
+ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
+ 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_report_rate != NO_REGISTER)
+ tsdata->report_rate = edt_ft5x06_register_read(tsdata,
+ reg_addr->reg_report_rate);
+ tsdata->num_x = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_x);
+ tsdata->num_y = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_y);
+}
+
+static void
+edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
+{
+ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
+ switch (tsdata->version) {
+ case M06:
+ reg_addr->reg_threshold = WORK_REGISTER_THRESHOLD;
+ 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_num_x = WORK_REGISTER_NUM_X;
+ reg_addr->reg_num_y = WORK_REGISTER_NUM_Y;
+ break;
+
+ case M09:
+ reg_addr->reg_threshold = M09_REGISTER_THRESHOLD;
+ reg_addr->reg_gain = M09_REGISTER_GAIN;
+ reg_addr->reg_offset = M09_REGISTER_OFFSET;
+ reg_addr->reg_num_x = M09_REGISTER_NUM_X;
+ reg_addr->reg_num_y = M09_REGISTER_NUM_Y;
+ break;
+ }
+}
+
+#ifdef CONFIG_OF
+static int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
+ struct edt_ft5x06_ts_data *tsdata)
+{
+ struct device_node *np = dev->of_node;
+
+ /*
+ * irq_pin is not needed for DT setup.
+ * irq is associated via 'interrupts' property in DT
+ */
+ tsdata->irq_pin = -EINVAL;
+ tsdata->reset_pin = of_get_named_gpio(np, "reset-gpios", 0);
+ tsdata->wake_pin = of_get_named_gpio(np, "wake-gpios", 0);
+
+ return 0;
+}
+#else
+static inline int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
+ struct edt_ft5x06_ts_data *tsdata)
+{
+ return -ENODEV;
+}
+#endif
+
+static int edt_ft5x06_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct edt_ft5x06_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+ struct edt_ft5x06_ts_data *tsdata;
+ struct input_dev *input;
+ int error;
+ char fw_version[EDT_NAME_LEN];
+
+ dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
+
+ tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
+ if (!tsdata) {
+ dev_err(&client->dev, "failed to allocate driver data.\n");
+ return -ENOMEM;
+ }
+
+ if (!pdata) {
+ error = edt_ft5x06_i2c_ts_probe_dt(&client->dev, tsdata);
+ if (error) {
+ dev_err(&client->dev,
+ "DT probe failed and no platform data present\n");
+ return error;
+ }
+ } else {
+ tsdata->reset_pin = pdata->reset_pin;
+ tsdata->irq_pin = pdata->irq_pin;
+ tsdata->wake_pin = -EINVAL;
+ }
+
+ error = edt_ft5x06_ts_reset(client, tsdata);
+ if (error)
+ return error;
+
+ if (gpio_is_valid(tsdata->irq_pin)) {
+ error = devm_gpio_request_one(&client->dev, tsdata->irq_pin,
+ GPIOF_IN, "edt-ft5x06 irq");
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to request GPIO %d, error %d\n",
+ tsdata->irq_pin, error);
+ return error;
+ }
+ }
+
+ input = devm_input_allocate_device(&client->dev);
+ if (!input) {
+ dev_err(&client->dev, "failed to allocate input device.\n");
+ return -ENOMEM;
+ }
+
+ mutex_init(&tsdata->mutex);
+ tsdata->client = client;
+ tsdata->input = input;
+ tsdata->factory_mode = false;
+
+ error = edt_ft5x06_ts_identify(client, tsdata, fw_version);
+ if (error) {
+ dev_err(&client->dev, "touchscreen probe failed\n");
+ return error;
+ }
+
+ edt_ft5x06_ts_set_regs(tsdata);
+
+ if (!pdata)
+ edt_ft5x06_ts_get_dt_defaults(client->dev.of_node, tsdata);
+ else
+ edt_ft5x06_ts_get_defaults(tsdata, pdata);
+
+ edt_ft5x06_ts_get_parameters(tsdata);
+
+ dev_dbg(&client->dev,
+ "Model \"%s\", Rev. \"%s\", %dx%d sensors\n",
+ tsdata->name, fw_version, tsdata->num_x, tsdata->num_y);
+
+ input->name = tsdata->name;
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &client->dev;
+
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_X,
+ 0, tsdata->num_x * 64 - 1, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y,
+ 0, tsdata->num_y * 64 - 1, 0, 0);
+
+ if (!pdata)
+ touchscreen_parse_of_params(input);
+
+ error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
+ if (error) {
+ dev_err(&client->dev, "Unable to init MT slots.\n");
+ return error;
+ }
+
+ input_set_drvdata(input, tsdata);
+ i2c_set_clientdata(client, tsdata);
+
+ error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ edt_ft5x06_ts_isr,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ client->name, tsdata);
+ if (error) {
+ dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+ return error;
+ }
+
+ error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+ if (error)
+ return error;
+
+ error = input_register_device(input);
+ if (error)
+ goto err_remove_attrs;
+
+ edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev));
+ device_init_wakeup(&client->dev, 1);
+
+ dev_dbg(&client->dev,
+ "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n",
+ client->irq, tsdata->wake_pin, tsdata->reset_pin);
+
+ return 0;
+
+err_remove_attrs:
+ sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+ return error;
+}
+
+static int edt_ft5x06_ts_remove(struct i2c_client *client)
+{
+ struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+
+ edt_ft5x06_ts_teardown_debugfs(tsdata);
+ sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+
+ return 0;
+}
+
+static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(client->irq);
+
+ return 0;
+}
+
+static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(client->irq);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
+ edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume);
+
+static const struct i2c_device_id edt_ft5x06_ts_id[] = {
+ { "edt-ft5x06", 0, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id edt_ft5x06_of_match[] = {
+ { .compatible = "edt,edt-ft5206", },
+ { .compatible = "edt,edt-ft5306", },
+ { .compatible = "edt,edt-ft5406", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
+#endif
+
+static struct i2c_driver edt_ft5x06_ts_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "edt_ft5x06",
+ .of_match_table = of_match_ptr(edt_ft5x06_of_match),
+ .pm = &edt_ft5x06_ts_pm_ops,
+ },
+ .id_table = edt_ft5x06_ts_id,
+ .probe = edt_ft5x06_ts_probe,
+ .remove = edt_ft5x06_ts_remove,
+};
+
+module_i2c_driver(edt_ft5x06_ts_driver);
+
+MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>");
+MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c
new file mode 100644
index 000000000..09be6ced7
--- /dev/null
+++ b/drivers/input/touchscreen/eeti_ts.c
@@ -0,0 +1,327 @@
+/*
+ * Touch Screen driver for EETI's I2C connected touch screen panels
+ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * See EETI's software guide for the protocol specification:
+ * http://home.eeti.com.tw/web20/eg/guide.htm
+ *
+ * Based on migor_ts.c
+ * Copyright (c) 2008 Magnus Damm
+ * Copyright (c) 2007 Ujjwal Pande <ujjwal@kenati.com>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/timer.h>
+#include <linux/gpio.h>
+#include <linux/input/eeti_ts.h>
+#include <linux/slab.h>
+
+static bool flip_x;
+module_param(flip_x, bool, 0644);
+MODULE_PARM_DESC(flip_x, "flip x coordinate");
+
+static bool flip_y;
+module_param(flip_y, bool, 0644);
+MODULE_PARM_DESC(flip_y, "flip y coordinate");
+
+struct eeti_ts_priv {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct work_struct work;
+ struct mutex mutex;
+ int irq_gpio, irq, irq_active_high;
+};
+
+#define EETI_TS_BITDEPTH (11)
+#define EETI_MAXVAL ((1 << (EETI_TS_BITDEPTH + 1)) - 1)
+
+#define REPORT_BIT_PRESSED (1 << 0)
+#define REPORT_BIT_AD0 (1 << 1)
+#define REPORT_BIT_AD1 (1 << 2)
+#define REPORT_BIT_HAS_PRESSURE (1 << 6)
+#define REPORT_RES_BITS(v) (((v) >> 1) + EETI_TS_BITDEPTH)
+
+static inline int eeti_ts_irq_active(struct eeti_ts_priv *priv)
+{
+ return gpio_get_value(priv->irq_gpio) == priv->irq_active_high;
+}
+
+static void eeti_ts_read(struct work_struct *work)
+{
+ char buf[6];
+ unsigned int x, y, res, pressed, to = 100;
+ struct eeti_ts_priv *priv =
+ container_of(work, struct eeti_ts_priv, work);
+
+ mutex_lock(&priv->mutex);
+
+ while (eeti_ts_irq_active(priv) && --to)
+ i2c_master_recv(priv->client, buf, sizeof(buf));
+
+ if (!to) {
+ dev_err(&priv->client->dev,
+ "unable to clear IRQ - line stuck?\n");
+ goto out;
+ }
+
+ /* drop non-report packets */
+ if (!(buf[0] & 0x80))
+ goto out;
+
+ pressed = buf[0] & REPORT_BIT_PRESSED;
+ res = REPORT_RES_BITS(buf[0] & (REPORT_BIT_AD0 | REPORT_BIT_AD1));
+ x = buf[2] | (buf[1] << 8);
+ y = buf[4] | (buf[3] << 8);
+
+ /* fix the range to 11 bits */
+ x >>= res - EETI_TS_BITDEPTH;
+ y >>= res - EETI_TS_BITDEPTH;
+
+ if (flip_x)
+ x = EETI_MAXVAL - x;
+
+ if (flip_y)
+ y = EETI_MAXVAL - y;
+
+ if (buf[0] & REPORT_BIT_HAS_PRESSURE)
+ input_report_abs(priv->input, ABS_PRESSURE, buf[5]);
+
+ input_report_abs(priv->input, ABS_X, x);
+ input_report_abs(priv->input, ABS_Y, y);
+ input_report_key(priv->input, BTN_TOUCH, !!pressed);
+ input_sync(priv->input);
+
+out:
+ mutex_unlock(&priv->mutex);
+}
+
+static irqreturn_t eeti_ts_isr(int irq, void *dev_id)
+{
+ struct eeti_ts_priv *priv = dev_id;
+
+ /* postpone I2C transactions as we are atomic */
+ schedule_work(&priv->work);
+
+ return IRQ_HANDLED;
+}
+
+static void eeti_ts_start(struct eeti_ts_priv *priv)
+{
+ enable_irq(priv->irq);
+
+ /* Read the events once to arm the IRQ */
+ eeti_ts_read(&priv->work);
+}
+
+static void eeti_ts_stop(struct eeti_ts_priv *priv)
+{
+ disable_irq(priv->irq);
+ cancel_work_sync(&priv->work);
+}
+
+static int eeti_ts_open(struct input_dev *dev)
+{
+ struct eeti_ts_priv *priv = input_get_drvdata(dev);
+
+ eeti_ts_start(priv);
+
+ return 0;
+}
+
+static void eeti_ts_close(struct input_dev *dev)
+{
+ struct eeti_ts_priv *priv = input_get_drvdata(dev);
+
+ eeti_ts_stop(priv);
+}
+
+static int eeti_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *idp)
+{
+ struct eeti_ts_platform_data *pdata = dev_get_platdata(&client->dev);
+ struct eeti_ts_priv *priv;
+ struct input_dev *input;
+ unsigned int irq_flags;
+ int err = -ENOMEM;
+
+ /*
+ * In contrast to what's described in the datasheet, there seems
+ * to be no way of probing the presence of that device using I2C
+ * commands. So we need to blindly believe it is there, and wait
+ * for interrupts to occur.
+ */
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&client->dev, "failed to allocate driver data\n");
+ goto err0;
+ }
+
+ mutex_init(&priv->mutex);
+ input = input_allocate_device();
+
+ if (!input) {
+ dev_err(&client->dev, "Failed to allocate input device.\n");
+ goto err1;
+ }
+
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input, ABS_X, 0, EETI_MAXVAL, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, EETI_MAXVAL, 0, 0);
+ input_set_abs_params(input, ABS_PRESSURE, 0, 0xff, 0, 0);
+
+ input->name = client->name;
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &client->dev;
+ input->open = eeti_ts_open;
+ input->close = eeti_ts_close;
+
+ priv->client = client;
+ priv->input = input;
+ priv->irq_gpio = pdata->irq_gpio;
+ priv->irq = gpio_to_irq(pdata->irq_gpio);
+
+ err = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name);
+ if (err < 0)
+ goto err1;
+
+ priv->irq_active_high = pdata->irq_active_high;
+
+ irq_flags = priv->irq_active_high ?
+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+
+ INIT_WORK(&priv->work, eeti_ts_read);
+ i2c_set_clientdata(client, priv);
+ input_set_drvdata(input, priv);
+
+ err = input_register_device(input);
+ if (err)
+ goto err2;
+
+ err = request_irq(priv->irq, eeti_ts_isr, irq_flags,
+ client->name, priv);
+ if (err) {
+ dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+ goto err3;
+ }
+
+ /*
+ * Disable the device for now. It will be enabled once the
+ * input device is opened.
+ */
+ eeti_ts_stop(priv);
+
+ device_init_wakeup(&client->dev, 0);
+ return 0;
+
+err3:
+ input_unregister_device(input);
+ input = NULL; /* so we dont try to free it below */
+err2:
+ gpio_free(pdata->irq_gpio);
+err1:
+ input_free_device(input);
+ kfree(priv);
+err0:
+ return err;
+}
+
+static int eeti_ts_remove(struct i2c_client *client)
+{
+ struct eeti_ts_priv *priv = i2c_get_clientdata(client);
+
+ free_irq(priv->irq, priv);
+ /*
+ * eeti_ts_stop() leaves IRQ disabled. We need to re-enable it
+ * so that device still works if we reload the driver.
+ */
+ enable_irq(priv->irq);
+
+ input_unregister_device(priv->input);
+ kfree(priv);
+
+ return 0;
+}
+
+static int __maybe_unused eeti_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct eeti_ts_priv *priv = i2c_get_clientdata(client);
+ struct input_dev *input_dev = priv->input;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ eeti_ts_stop(priv);
+
+ mutex_unlock(&input_dev->mutex);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(priv->irq);
+
+ return 0;
+}
+
+static int __maybe_unused eeti_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct eeti_ts_priv *priv = i2c_get_clientdata(client);
+ struct input_dev *input_dev = priv->input;
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(priv->irq);
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ eeti_ts_start(priv);
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(eeti_ts_pm, eeti_ts_suspend, eeti_ts_resume);
+
+static const struct i2c_device_id eeti_ts_id[] = {
+ { "eeti_ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, eeti_ts_id);
+
+static struct i2c_driver eeti_ts_driver = {
+ .driver = {
+ .name = "eeti_ts",
+ .pm = &eeti_ts_pm,
+ },
+ .probe = eeti_ts_probe,
+ .remove = eeti_ts_remove,
+ .id_table = eeti_ts_id,
+};
+
+module_i2c_driver(eeti_ts_driver);
+
+MODULE_DESCRIPTION("EETI Touchscreen driver");
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
new file mode 100644
index 000000000..4c5629928
--- /dev/null
+++ b/drivers/input/touchscreen/egalax_ts.c
@@ -0,0 +1,283 @@
+/*
+ * Driver for EETI eGalax Multiple Touch Controller
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ *
+ * based on max11801_ts.c
+ *
+ * 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.
+ */
+
+/* EETI eGalax serial touch screen controller is a I2C based multiple
+ * touch screen controller, it supports 5 point multiple touch. */
+
+/* TODO:
+ - auto idle mode support
+*/
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/input/mt.h>
+#include <linux/of_gpio.h>
+
+/*
+ * Mouse Mode: some panel may configure the controller to mouse mode,
+ * which can only report one point at a given time.
+ * This driver will ignore events in this mode.
+ */
+#define REPORT_MODE_MOUSE 0x1
+/*
+ * Vendor Mode: this mode is used to transfer some vendor specific
+ * messages.
+ * This driver will ignore events in this mode.
+ */
+#define REPORT_MODE_VENDOR 0x3
+/* Multiple Touch Mode */
+#define REPORT_MODE_MTTOUCH 0x4
+
+#define MAX_SUPPORT_POINTS 5
+
+#define EVENT_VALID_OFFSET 7
+#define EVENT_VALID_MASK (0x1 << EVENT_VALID_OFFSET)
+#define EVENT_ID_OFFSET 2
+#define EVENT_ID_MASK (0xf << EVENT_ID_OFFSET)
+#define EVENT_IN_RANGE (0x1 << 1)
+#define EVENT_DOWN_UP (0X1 << 0)
+
+#define MAX_I2C_DATA_LEN 10
+
+#define EGALAX_MAX_X 32760
+#define EGALAX_MAX_Y 32760
+#define EGALAX_MAX_TRIES 100
+
+struct egalax_ts {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+};
+
+static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
+{
+ struct egalax_ts *ts = dev_id;
+ struct input_dev *input_dev = ts->input_dev;
+ struct i2c_client *client = ts->client;
+ u8 buf[MAX_I2C_DATA_LEN];
+ int id, ret, x, y, z;
+ int tries = 0;
+ bool down, valid;
+ u8 state;
+
+ do {
+ ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN);
+ } while (ret == -EAGAIN && tries++ < EGALAX_MAX_TRIES);
+
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ if (buf[0] != REPORT_MODE_MTTOUCH) {
+ /* ignore mouse events and vendor events */
+ return IRQ_HANDLED;
+ }
+
+ state = buf[1];
+ x = (buf[3] << 8) | buf[2];
+ y = (buf[5] << 8) | buf[4];
+ z = (buf[7] << 8) | buf[6];
+
+ valid = state & EVENT_VALID_MASK;
+ id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET;
+ down = state & EVENT_DOWN_UP;
+
+ if (!valid || id > MAX_SUPPORT_POINTS) {
+ dev_dbg(&client->dev, "point invalid\n");
+ return IRQ_HANDLED;
+ }
+
+ input_mt_slot(input_dev, id);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down);
+
+ dev_dbg(&client->dev, "%s id:%d x:%d y:%d z:%d",
+ down ? "down" : "up", id, x, y, z);
+
+ if (down) {
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, z);
+ }
+
+ input_mt_report_pointer_emulation(input_dev, true);
+ input_sync(input_dev);
+
+ return IRQ_HANDLED;
+}
+
+/* wake up controller by an falling edge of interrupt gpio. */
+static int egalax_wake_up_device(struct i2c_client *client)
+{
+ struct device_node *np = client->dev.of_node;
+ int gpio;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ gpio = of_get_named_gpio(np, "wakeup-gpios", 0);
+ if (!gpio_is_valid(gpio))
+ return -ENODEV;
+
+ ret = gpio_request(gpio, "egalax_irq");
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "request gpio failed, cannot wake up controller: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* wake up controller via an falling edge on IRQ gpio. */
+ gpio_direction_output(gpio, 0);
+ gpio_set_value(gpio, 1);
+
+ /* controller should be waken up, return irq. */
+ gpio_direction_input(gpio);
+ gpio_free(gpio);
+
+ return 0;
+}
+
+static int egalax_firmware_version(struct i2c_client *client)
+{
+ static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 };
+ int ret;
+
+ ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int egalax_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct egalax_ts *ts;
+ struct input_dev *input_dev;
+ int error;
+
+ ts = devm_kzalloc(&client->dev, sizeof(struct egalax_ts), GFP_KERNEL);
+ if (!ts) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ ts->client = client;
+ ts->input_dev = input_dev;
+
+ /* controller may be in sleep, wake it up. */
+ error = egalax_wake_up_device(client);
+ if (error) {
+ dev_err(&client->dev, "Failed to wake up the controller\n");
+ return error;
+ }
+
+ error = egalax_firmware_version(client);
+ if (error < 0) {
+ dev_err(&client->dev, "Failed to read firmware version\n");
+ return error;
+ }
+
+ input_dev->name = "EETI eGalax Touch Screen";
+ input_dev->id.bustype = BUS_I2C;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ input_set_abs_params(input_dev, ABS_X, 0, EGALAX_MAX_X, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, EGALAX_MAX_Y, 0, 0);
+ input_set_abs_params(input_dev,
+ ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0);
+ input_set_abs_params(input_dev,
+ ABS_MT_POSITION_Y, 0, EGALAX_MAX_Y, 0, 0);
+ input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0);
+
+ input_set_drvdata(input_dev, ts);
+
+ error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ egalax_ts_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "egalax_ts", ts);
+ if (error < 0) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ return error;
+ }
+
+ error = input_register_device(ts->input_dev);
+ if (error)
+ return error;
+
+ i2c_set_clientdata(client, ts);
+ return 0;
+}
+
+static const struct i2c_device_id egalax_ts_id[] = {
+ { "egalax_ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, egalax_ts_id);
+
+static int __maybe_unused egalax_ts_suspend(struct device *dev)
+{
+ static const u8 suspend_cmd[MAX_I2C_DATA_LEN] = {
+ 0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0
+ };
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+
+ ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN);
+ return ret > 0 ? 0 : ret;
+}
+
+static int __maybe_unused egalax_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return egalax_wake_up_device(client);
+}
+
+static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume);
+
+static const struct of_device_id egalax_ts_dt_ids[] = {
+ { .compatible = "eeti,egalax_ts" },
+ { /* sentinel */ }
+};
+
+static struct i2c_driver egalax_ts_driver = {
+ .driver = {
+ .name = "egalax_ts",
+ .owner = THIS_MODULE,
+ .pm = &egalax_ts_pm_ops,
+ .of_match_table = egalax_ts_dt_ids,
+ },
+ .id_table = egalax_ts_id,
+ .probe = egalax_ts_probe,
+};
+
+module_i2c_driver(egalax_ts_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
new file mode 100644
index 000000000..5e65be97f
--- /dev/null
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -0,0 +1,1275 @@
+/*
+ * Elan Microelectronics touch panels with I2C interface
+ *
+ * Copyright (C) 2014 Elan Microelectronics Corporation.
+ * Scott Liu <scott.liu@emc.com.tw>
+ *
+ * This code is partly based on hid-multitouch.c:
+ *
+ * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr>
+ * Copyright (c) 2010-2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ * Copyright (c) 2010-2012 Ecole Nationale de l'Aviation Civile, France
+ *
+ *
+ * This code is partly based on i2c-hid.c:
+ *
+ * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
+ * Copyright (c) 2012 Red Hat, Inc
+ */
+
+/*
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/async.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/buffer_head.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <asm/unaligned.h>
+
+/* Device, Driver information */
+#define DEVICE_NAME "elants_i2c"
+#define DRV_VERSION "1.0.9"
+
+/* Convert from rows or columns into resolution */
+#define ELAN_TS_RESOLUTION(n, m) (((n) - 1) * (m))
+
+/* FW header data */
+#define HEADER_SIZE 4
+#define FW_HDR_TYPE 0
+#define FW_HDR_COUNT 1
+#define FW_HDR_LENGTH 2
+
+/* Buffer mode Queue Header information */
+#define QUEUE_HEADER_SINGLE 0x62
+#define QUEUE_HEADER_NORMAL 0X63
+#define QUEUE_HEADER_WAIT 0x64
+
+/* Command header definition */
+#define CMD_HEADER_WRITE 0x54
+#define CMD_HEADER_READ 0x53
+#define CMD_HEADER_6B_READ 0x5B
+#define CMD_HEADER_RESP 0x52
+#define CMD_HEADER_6B_RESP 0x9B
+#define CMD_HEADER_HELLO 0x55
+#define CMD_HEADER_REK 0x66
+
+/* FW position data */
+#define PACKET_SIZE 55
+#define MAX_CONTACT_NUM 10
+#define FW_POS_HEADER 0
+#define FW_POS_STATE 1
+#define FW_POS_TOTAL 2
+#define FW_POS_XY 3
+#define FW_POS_CHECKSUM 34
+#define FW_POS_WIDTH 35
+#define FW_POS_PRESSURE 45
+
+#define HEADER_REPORT_10_FINGER 0x62
+
+/* Header (4 bytes) plus 3 fill 10-finger packets */
+#define MAX_PACKET_SIZE 169
+
+#define BOOT_TIME_DELAY_MS 50
+
+/* FW read command, 0x53 0x?? 0x0, 0x01 */
+#define E_ELAN_INFO_FW_VER 0x00
+#define E_ELAN_INFO_BC_VER 0x10
+#define E_ELAN_INFO_TEST_VER 0xE0
+#define E_ELAN_INFO_FW_ID 0xF0
+#define E_INFO_OSR 0xD6
+#define E_INFO_PHY_SCAN 0xD7
+#define E_INFO_PHY_DRIVER 0xD8
+
+#define MAX_RETRIES 3
+#define MAX_FW_UPDATE_RETRIES 30
+
+#define ELAN_FW_PAGESIZE 132
+
+/* calibration timeout definition */
+#define ELAN_CALI_TIMEOUT_MSEC 10000
+
+enum elants_state {
+ ELAN_STATE_NORMAL,
+ ELAN_WAIT_QUEUE_HEADER,
+ ELAN_WAIT_RECALIBRATION,
+};
+
+enum elants_iap_mode {
+ ELAN_IAP_OPERATIONAL,
+ ELAN_IAP_RECOVERY,
+};
+
+/* struct elants_data - represents state of Elan touchscreen device */
+struct elants_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+
+ u16 fw_version;
+ u8 test_version;
+ u8 solution_version;
+ u8 bc_version;
+ u8 iap_version;
+ u16 hw_version;
+ unsigned int x_res; /* resolution in units/mm */
+ unsigned int y_res;
+ unsigned int x_max;
+ unsigned int y_max;
+
+ enum elants_state state;
+ enum elants_iap_mode iap_mode;
+
+ /* Guards against concurrent access to the device via sysfs */
+ struct mutex sysfs_mutex;
+
+ u8 cmd_resp[HEADER_SIZE];
+ struct completion cmd_done;
+
+ u8 buf[MAX_PACKET_SIZE];
+
+ bool wake_irq_enabled;
+};
+
+static int elants_i2c_send(struct i2c_client *client,
+ const void *data, size_t size)
+{
+ int ret;
+
+ ret = i2c_master_send(client, data, size);
+ if (ret == size)
+ return 0;
+
+ if (ret >= 0)
+ ret = -EIO;
+
+ dev_err(&client->dev, "%s failed (%*ph): %d\n",
+ __func__, (int)size, data, ret);
+
+ return ret;
+}
+
+static int elants_i2c_read(struct i2c_client *client, void *data, size_t size)
+{
+ int ret;
+
+ ret = i2c_master_recv(client, data, size);
+ if (ret == size)
+ return 0;
+
+ if (ret >= 0)
+ ret = -EIO;
+
+ dev_err(&client->dev, "%s failed: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int elants_i2c_execute_command(struct i2c_client *client,
+ const u8 *cmd, size_t cmd_size,
+ u8 *resp, size_t resp_size)
+{
+ struct i2c_msg msgs[2];
+ int ret;
+ u8 expected_response;
+
+ switch (cmd[0]) {
+ case CMD_HEADER_READ:
+ expected_response = CMD_HEADER_RESP;
+ break;
+
+ case CMD_HEADER_6B_READ:
+ expected_response = CMD_HEADER_6B_RESP;
+ break;
+
+ default:
+ dev_err(&client->dev, "%s: invalid command %*ph\n",
+ __func__, (int)cmd_size, cmd);
+ return -EINVAL;
+ }
+
+ msgs[0].addr = client->addr;
+ msgs[0].flags = client->flags & I2C_M_TEN;
+ msgs[0].len = cmd_size;
+ msgs[0].buf = (u8 *)cmd;
+
+ msgs[1].addr = client->addr;
+ msgs[1].flags = client->flags & I2C_M_TEN;
+ msgs[1].flags |= I2C_M_RD;
+ msgs[1].len = resp_size;
+ msgs[1].buf = resp;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+
+ if (ret != ARRAY_SIZE(msgs) || resp[FW_HDR_TYPE] != expected_response)
+ return -EIO;
+
+ return 0;
+}
+
+static int elants_i2c_calibrate(struct elants_data *ts)
+{
+ struct i2c_client *client = ts->client;
+ int ret, error;
+ static const u8 w_flashkey[] = { 0x54, 0xC0, 0xE1, 0x5A };
+ static const u8 rek[] = { 0x54, 0x29, 0x00, 0x01 };
+ static const u8 rek_resp[] = { CMD_HEADER_REK, 0x66, 0x66, 0x66 };
+
+ disable_irq(client->irq);
+
+ ts->state = ELAN_WAIT_RECALIBRATION;
+ reinit_completion(&ts->cmd_done);
+
+ elants_i2c_send(client, w_flashkey, sizeof(w_flashkey));
+ elants_i2c_send(client, rek, sizeof(rek));
+
+ enable_irq(client->irq);
+
+ ret = wait_for_completion_interruptible_timeout(&ts->cmd_done,
+ msecs_to_jiffies(ELAN_CALI_TIMEOUT_MSEC));
+
+ ts->state = ELAN_STATE_NORMAL;
+
+ if (ret <= 0) {
+ error = ret < 0 ? ret : -ETIMEDOUT;
+ dev_err(&client->dev,
+ "error while waiting for calibration to complete: %d\n",
+ error);
+ return error;
+ }
+
+ if (memcmp(rek_resp, ts->cmd_resp, sizeof(rek_resp))) {
+ dev_err(&client->dev,
+ "unexpected calibration response: %*ph\n",
+ (int)sizeof(ts->cmd_resp), ts->cmd_resp);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int elants_i2c_sw_reset(struct i2c_client *client)
+{
+ const u8 soft_rst_cmd[] = { 0x77, 0x77, 0x77, 0x77 };
+ int error;
+
+ error = elants_i2c_send(client, soft_rst_cmd,
+ sizeof(soft_rst_cmd));
+ if (error) {
+ dev_err(&client->dev, "software reset failed: %d\n", error);
+ return error;
+ }
+
+ /*
+ * We should wait at least 10 msec (but no more than 40) before
+ * sending fastboot or IAP command to the device.
+ */
+ msleep(30);
+
+ return 0;
+}
+
+static u16 elants_i2c_parse_version(u8 *buf)
+{
+ return get_unaligned_be32(buf) >> 4;
+}
+
+static int elants_i2c_query_fw_id(struct elants_data *ts)
+{
+ struct i2c_client *client = ts->client;
+ int error, retry_cnt;
+ const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_FW_ID, 0x00, 0x01 };
+ u8 resp[HEADER_SIZE];
+
+ for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+ error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
+ resp, sizeof(resp));
+ if (!error) {
+ ts->hw_version = elants_i2c_parse_version(resp);
+ if (ts->hw_version != 0xffff)
+ return 0;
+ }
+
+ dev_dbg(&client->dev, "read fw id error=%d, buf=%*phC\n",
+ error, (int)sizeof(resp), resp);
+ }
+
+ dev_err(&client->dev,
+ "Failed to read fw id or fw id is invalid\n");
+
+ return -EINVAL;
+}
+
+static int elants_i2c_query_fw_version(struct elants_data *ts)
+{
+ struct i2c_client *client = ts->client;
+ int error, retry_cnt;
+ const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_FW_VER, 0x00, 0x01 };
+ u8 resp[HEADER_SIZE];
+
+ for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+ error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
+ resp, sizeof(resp));
+ if (!error) {
+ ts->fw_version = elants_i2c_parse_version(resp);
+ if (ts->fw_version != 0x0000 &&
+ ts->fw_version != 0xffff)
+ return 0;
+ }
+
+ dev_dbg(&client->dev, "read fw version error=%d, buf=%*phC\n",
+ error, (int)sizeof(resp), resp);
+ }
+
+ dev_err(&client->dev,
+ "Failed to read fw version or fw version is invalid\n");
+
+ return -EINVAL;
+}
+
+static int elants_i2c_query_test_version(struct elants_data *ts)
+{
+ struct i2c_client *client = ts->client;
+ int error, retry_cnt;
+ u16 version;
+ const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_TEST_VER, 0x00, 0x01 };
+ u8 resp[HEADER_SIZE];
+
+ for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+ error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
+ resp, sizeof(resp));
+ if (!error) {
+ version = elants_i2c_parse_version(resp);
+ ts->test_version = version >> 8;
+ ts->solution_version = version & 0xff;
+
+ return 0;
+ }
+
+ dev_dbg(&client->dev,
+ "read test version error rc=%d, buf=%*phC\n",
+ error, (int)sizeof(resp), resp);
+ }
+
+ dev_err(&client->dev, "Failed to read test version\n");
+
+ return -EINVAL;
+}
+
+static int elants_i2c_query_bc_version(struct elants_data *ts)
+{
+ struct i2c_client *client = ts->client;
+ const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_BC_VER, 0x00, 0x01 };
+ u8 resp[HEADER_SIZE];
+ u16 version;
+ int error;
+
+ error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
+ resp, sizeof(resp));
+ if (error) {
+ dev_err(&client->dev,
+ "read BC version error=%d, buf=%*phC\n",
+ error, (int)sizeof(resp), resp);
+ return error;
+ }
+
+ version = elants_i2c_parse_version(resp);
+ ts->bc_version = version >> 8;
+ ts->iap_version = version & 0xff;
+
+ return 0;
+}
+
+static int elants_i2c_query_ts_info(struct elants_data *ts)
+{
+ struct i2c_client *client = ts->client;
+ int error;
+ u8 resp[17];
+ u16 phy_x, phy_y, rows, cols, osr;
+ const u8 get_resolution_cmd[] = {
+ CMD_HEADER_6B_READ, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ const u8 get_osr_cmd[] = {
+ CMD_HEADER_READ, E_INFO_OSR, 0x00, 0x01
+ };
+ const u8 get_physical_scan_cmd[] = {
+ CMD_HEADER_READ, E_INFO_PHY_SCAN, 0x00, 0x01
+ };
+ const u8 get_physical_drive_cmd[] = {
+ CMD_HEADER_READ, E_INFO_PHY_DRIVER, 0x00, 0x01
+ };
+
+ /* Get trace number */
+ error = elants_i2c_execute_command(client,
+ get_resolution_cmd,
+ sizeof(get_resolution_cmd),
+ resp, sizeof(resp));
+ if (error) {
+ dev_err(&client->dev, "get resolution command failed: %d\n",
+ error);
+ return error;
+ }
+
+ rows = resp[2] + resp[6] + resp[10];
+ cols = resp[3] + resp[7] + resp[11];
+
+ /* Process mm_to_pixel information */
+ error = elants_i2c_execute_command(client,
+ get_osr_cmd, sizeof(get_osr_cmd),
+ resp, sizeof(resp));
+ if (error) {
+ dev_err(&client->dev, "get osr command failed: %d\n",
+ error);
+ return error;
+ }
+
+ osr = resp[3];
+
+ error = elants_i2c_execute_command(client,
+ get_physical_scan_cmd,
+ sizeof(get_physical_scan_cmd),
+ resp, sizeof(resp));
+ if (error) {
+ dev_err(&client->dev, "get physical scan command failed: %d\n",
+ error);
+ return error;
+ }
+
+ phy_x = get_unaligned_be16(&resp[2]);
+
+ error = elants_i2c_execute_command(client,
+ get_physical_drive_cmd,
+ sizeof(get_physical_drive_cmd),
+ resp, sizeof(resp));
+ if (error) {
+ dev_err(&client->dev, "get physical drive command failed: %d\n",
+ error);
+ return error;
+ }
+
+ phy_y = get_unaligned_be16(&resp[2]);
+
+ dev_dbg(&client->dev, "phy_x=%d, phy_y=%d\n", phy_x, phy_y);
+
+ if (rows == 0 || cols == 0 || osr == 0) {
+ dev_warn(&client->dev,
+ "invalid trace number data: %d, %d, %d\n",
+ rows, cols, osr);
+ } else {
+ /* translate trace number to TS resolution */
+ ts->x_max = ELAN_TS_RESOLUTION(rows, osr);
+ ts->x_res = DIV_ROUND_CLOSEST(ts->x_max, phy_x);
+ ts->y_max = ELAN_TS_RESOLUTION(cols, osr);
+ ts->y_res = DIV_ROUND_CLOSEST(ts->y_max, phy_y);
+ }
+
+ return 0;
+}
+
+static int elants_i2c_fastboot(struct i2c_client *client)
+{
+ const u8 boot_cmd[] = { 0x4D, 0x61, 0x69, 0x6E };
+ int error;
+
+ error = elants_i2c_send(client, boot_cmd, sizeof(boot_cmd));
+ if (error) {
+ dev_err(&client->dev, "boot failed: %d\n", error);
+ return error;
+ }
+
+ dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
+ return 0;
+}
+
+static int elants_i2c_initialize(struct elants_data *ts)
+{
+ struct i2c_client *client = ts->client;
+ int error, retry_cnt;
+ const u8 hello_packet[] = { 0x55, 0x55, 0x55, 0x55 };
+ const u8 recov_packet[] = { 0x55, 0x55, 0x80, 0x80 };
+ u8 buf[HEADER_SIZE];
+
+ for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+ error = elants_i2c_sw_reset(client);
+ if (error) {
+ /* Continue initializing if it's the last try */
+ if (retry_cnt < MAX_RETRIES - 1)
+ continue;
+ }
+
+ error = elants_i2c_fastboot(client);
+ if (error) {
+ /* Continue initializing if it's the last try */
+ if (retry_cnt < MAX_RETRIES - 1)
+ continue;
+ }
+
+ /* Wait for Hello packet */
+ msleep(BOOT_TIME_DELAY_MS);
+
+ error = elants_i2c_read(client, buf, sizeof(buf));
+ if (error) {
+ dev_err(&client->dev,
+ "failed to read 'hello' packet: %d\n", error);
+ } else if (!memcmp(buf, hello_packet, sizeof(hello_packet))) {
+ ts->iap_mode = ELAN_IAP_OPERATIONAL;
+ break;
+ } else if (!memcmp(buf, recov_packet, sizeof(recov_packet))) {
+ /*
+ * Setting error code will mark device
+ * in recovery mode below.
+ */
+ error = -EIO;
+ break;
+ } else {
+ error = -EINVAL;
+ dev_err(&client->dev,
+ "invalid 'hello' packet: %*ph\n",
+ (int)sizeof(buf), buf);
+ }
+ }
+
+ if (!error)
+ error = elants_i2c_query_fw_id(ts);
+ if (!error)
+ error = elants_i2c_query_fw_version(ts);
+
+ if (error) {
+ ts->iap_mode = ELAN_IAP_RECOVERY;
+ } else {
+ elants_i2c_query_test_version(ts);
+ elants_i2c_query_bc_version(ts);
+ elants_i2c_query_ts_info(ts);
+ }
+
+ return 0;
+}
+
+/*
+ * Firmware update interface.
+ */
+
+static int elants_i2c_fw_write_page(struct i2c_client *client,
+ const void *page)
+{
+ const u8 ack_ok[] = { 0xaa, 0xaa };
+ u8 buf[2];
+ int retry;
+ int error;
+
+ for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
+ error = elants_i2c_send(client, page, ELAN_FW_PAGESIZE);
+ if (error) {
+ dev_err(&client->dev,
+ "IAP Write Page failed: %d\n", error);
+ continue;
+ }
+
+ error = elants_i2c_read(client, buf, 2);
+ if (error) {
+ dev_err(&client->dev,
+ "IAP Ack read failed: %d\n", error);
+ return error;
+ }
+
+ if (!memcmp(buf, ack_ok, sizeof(ack_ok)))
+ return 0;
+
+ error = -EIO;
+ dev_err(&client->dev,
+ "IAP Get Ack Error [%02x:%02x]\n",
+ buf[0], buf[1]);
+ }
+
+ return error;
+}
+
+static int elants_i2c_do_update_firmware(struct i2c_client *client,
+ const struct firmware *fw,
+ bool force)
+{
+ const u8 enter_iap[] = { 0x45, 0x49, 0x41, 0x50 };
+ const u8 enter_iap2[] = { 0x54, 0x00, 0x12, 0x34 };
+ const u8 iap_ack[] = { 0x55, 0xaa, 0x33, 0xcc };
+ u8 buf[HEADER_SIZE];
+ u16 send_id;
+ int page, n_fw_pages;
+ int error;
+
+ /* Recovery mode detection! */
+ if (force) {
+ dev_dbg(&client->dev, "Recovery mode procedure\n");
+ error = elants_i2c_send(client, enter_iap2, sizeof(enter_iap2));
+ } else {
+ /* Start IAP Procedure */
+ dev_dbg(&client->dev, "Normal IAP procedure\n");
+ elants_i2c_sw_reset(client);
+
+ error = elants_i2c_send(client, enter_iap, sizeof(enter_iap));
+ }
+
+ if (error) {
+ dev_err(&client->dev, "failed to enter IAP mode: %d\n", error);
+ return error;
+ }
+
+ msleep(20);
+
+ /* check IAP state */
+ error = elants_i2c_read(client, buf, 4);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to read IAP acknowledgement: %d\n",
+ error);
+ return error;
+ }
+
+ if (memcmp(buf, iap_ack, sizeof(iap_ack))) {
+ dev_err(&client->dev,
+ "failed to enter IAP: %*ph (expected %*ph)\n",
+ (int)sizeof(buf), buf, (int)sizeof(iap_ack), iap_ack);
+ return -EIO;
+ }
+
+ dev_info(&client->dev, "successfully entered IAP mode");
+
+ send_id = client->addr;
+ error = elants_i2c_send(client, &send_id, 1);
+ if (error) {
+ dev_err(&client->dev, "sending dummy byte failed: %d\n",
+ error);
+ return error;
+ }
+
+ /* Clear the last page of Master */
+ error = elants_i2c_send(client, fw->data, ELAN_FW_PAGESIZE);
+ if (error) {
+ dev_err(&client->dev, "clearing of the last page failed: %d\n",
+ error);
+ return error;
+ }
+
+ error = elants_i2c_read(client, buf, 2);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to read ACK for clearing the last page: %d\n",
+ error);
+ return error;
+ }
+
+ n_fw_pages = fw->size / ELAN_FW_PAGESIZE;
+ dev_dbg(&client->dev, "IAP Pages = %d\n", n_fw_pages);
+
+ for (page = 0; page < n_fw_pages; page++) {
+ error = elants_i2c_fw_write_page(client,
+ fw->data + page * ELAN_FW_PAGESIZE);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to write FW page %d: %d\n",
+ page, error);
+ return error;
+ }
+ }
+
+ /* Old iap needs to wait 200ms for WDT and rest is for hello packets */
+ msleep(300);
+
+ dev_info(&client->dev, "firmware update completed\n");
+ return 0;
+}
+
+static int elants_i2c_fw_update(struct elants_data *ts)
+{
+ struct i2c_client *client = ts->client;
+ const struct firmware *fw;
+ char *fw_name;
+ int error;
+
+ fw_name = kasprintf(GFP_KERNEL, "/*(DEBLOBBED)*/", ts->hw_version);
+ if (!fw_name)
+ return -ENOMEM;
+
+ dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
+ error = reject_firmware(&fw, fw_name, &client->dev);
+ kfree(fw_name);
+ if (error) {
+ dev_err(&client->dev, "failed to request firmware: %d\n",
+ error);
+ return error;
+ }
+
+ if (fw->size % ELAN_FW_PAGESIZE) {
+ dev_err(&client->dev, "invalid firmware length: %zu\n",
+ fw->size);
+ error = -EINVAL;
+ goto out;
+ }
+
+ disable_irq(client->irq);
+
+ error = elants_i2c_do_update_firmware(client, fw,
+ ts->iap_mode == ELAN_IAP_RECOVERY);
+ if (error) {
+ dev_err(&client->dev, "firmware update failed: %d\n", error);
+ ts->iap_mode = ELAN_IAP_RECOVERY;
+ goto out_enable_irq;
+ }
+
+ error = elants_i2c_initialize(ts);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to initialize device after firmware update: %d\n",
+ error);
+ ts->iap_mode = ELAN_IAP_RECOVERY;
+ goto out_enable_irq;
+ }
+
+ ts->iap_mode = ELAN_IAP_OPERATIONAL;
+
+out_enable_irq:
+ ts->state = ELAN_STATE_NORMAL;
+ enable_irq(client->irq);
+ msleep(100);
+
+ if (!error)
+ elants_i2c_calibrate(ts);
+out:
+ release_firmware(fw);
+ return error;
+}
+
+/*
+ * Event reporting.
+ */
+
+static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf)
+{
+ struct input_dev *input = ts->input;
+ unsigned int n_fingers;
+ u16 finger_state;
+ int i;
+
+ n_fingers = buf[FW_POS_STATE + 1] & 0x0f;
+ finger_state = ((buf[FW_POS_STATE + 1] & 0x30) << 4) |
+ buf[FW_POS_STATE];
+
+ dev_dbg(&ts->client->dev,
+ "n_fingers: %u, state: %04x\n", n_fingers, finger_state);
+
+ for (i = 0; i < MAX_CONTACT_NUM && n_fingers; i++) {
+ if (finger_state & 1) {
+ unsigned int x, y, p, w;
+ u8 *pos;
+
+ pos = &buf[FW_POS_XY + i * 3];
+ x = (((u16)pos[0] & 0xf0) << 4) | pos[1];
+ y = (((u16)pos[0] & 0x0f) << 8) | pos[2];
+ p = buf[FW_POS_PRESSURE + i];
+ w = buf[FW_POS_WIDTH + i];
+
+ dev_dbg(&ts->client->dev, "i=%d x=%d y=%d p=%d w=%d\n",
+ i, x, y, p, w);
+
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, x);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, y);
+ input_event(input, EV_ABS, ABS_MT_PRESSURE, p);
+ input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, w);
+
+ n_fingers--;
+ }
+
+ finger_state >>= 1;
+ }
+
+ input_mt_sync_frame(input);
+ input_sync(input);
+}
+
+static u8 elants_i2c_calculate_checksum(u8 *buf)
+{
+ u8 checksum = 0;
+ u8 i;
+
+ for (i = 0; i < FW_POS_CHECKSUM; i++)
+ checksum += buf[i];
+
+ return checksum;
+}
+
+static void elants_i2c_event(struct elants_data *ts, u8 *buf)
+{
+ u8 checksum = elants_i2c_calculate_checksum(buf);
+
+ if (unlikely(buf[FW_POS_CHECKSUM] != checksum))
+ dev_warn(&ts->client->dev,
+ "%s: invalid checksum for packet %02x: %02x vs. %02x\n",
+ __func__, buf[FW_POS_HEADER],
+ checksum, buf[FW_POS_CHECKSUM]);
+ else if (unlikely(buf[FW_POS_HEADER] != HEADER_REPORT_10_FINGER))
+ dev_warn(&ts->client->dev,
+ "%s: unknown packet type: %02x\n",
+ __func__, buf[FW_POS_HEADER]);
+ else
+ elants_i2c_mt_event(ts, buf);
+}
+
+static irqreturn_t elants_i2c_irq(int irq, void *_dev)
+{
+ const u8 wait_packet[] = { 0x64, 0x64, 0x64, 0x64 };
+ struct elants_data *ts = _dev;
+ struct i2c_client *client = ts->client;
+ int report_count, report_len;
+ int i;
+ int len;
+
+ len = i2c_master_recv(client, ts->buf, sizeof(ts->buf));
+ if (len < 0) {
+ dev_err(&client->dev, "%s: failed to read data: %d\n",
+ __func__, len);
+ goto out;
+ }
+
+ dev_dbg(&client->dev, "%s: packet %*ph\n",
+ __func__, HEADER_SIZE, ts->buf);
+
+ switch (ts->state) {
+ case ELAN_WAIT_RECALIBRATION:
+ if (ts->buf[FW_HDR_TYPE] == CMD_HEADER_REK) {
+ memcpy(ts->cmd_resp, ts->buf, sizeof(ts->cmd_resp));
+ complete(&ts->cmd_done);
+ ts->state = ELAN_STATE_NORMAL;
+ }
+ break;
+
+ case ELAN_WAIT_QUEUE_HEADER:
+ if (ts->buf[FW_HDR_TYPE] != QUEUE_HEADER_NORMAL)
+ break;
+
+ ts->state = ELAN_STATE_NORMAL;
+ /* fall through */
+
+ case ELAN_STATE_NORMAL:
+
+ switch (ts->buf[FW_HDR_TYPE]) {
+ case CMD_HEADER_HELLO:
+ case CMD_HEADER_RESP:
+ case CMD_HEADER_REK:
+ break;
+
+ case QUEUE_HEADER_WAIT:
+ if (memcmp(ts->buf, wait_packet, sizeof(wait_packet))) {
+ dev_err(&client->dev,
+ "invalid wait packet %*ph\n",
+ HEADER_SIZE, ts->buf);
+ } else {
+ ts->state = ELAN_WAIT_QUEUE_HEADER;
+ udelay(30);
+ }
+ break;
+
+ case QUEUE_HEADER_SINGLE:
+ elants_i2c_event(ts, &ts->buf[HEADER_SIZE]);
+ break;
+
+ case QUEUE_HEADER_NORMAL:
+ report_count = ts->buf[FW_HDR_COUNT];
+ if (report_count > 3) {
+ dev_err(&client->dev,
+ "too large report count: %*ph\n",
+ HEADER_SIZE, ts->buf);
+ break;
+ }
+
+ report_len = ts->buf[FW_HDR_LENGTH] / report_count;
+ if (report_len != PACKET_SIZE) {
+ dev_err(&client->dev,
+ "mismatching report length: %*ph\n",
+ HEADER_SIZE, ts->buf);
+ break;
+ }
+
+ for (i = 0; i < report_count; i++) {
+ u8 *buf = ts->buf + HEADER_SIZE +
+ i * PACKET_SIZE;
+ elants_i2c_event(ts, buf);
+ }
+ break;
+
+ default:
+ dev_err(&client->dev, "unknown packet %*ph\n",
+ HEADER_SIZE, ts->buf);
+ break;
+ }
+ break;
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+/*
+ * sysfs interface
+ */
+static ssize_t calibrate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+ int error;
+
+ error = mutex_lock_interruptible(&ts->sysfs_mutex);
+ if (error)
+ return error;
+
+ error = elants_i2c_calibrate(ts);
+
+ mutex_unlock(&ts->sysfs_mutex);
+ return error ?: count;
+}
+
+static ssize_t write_update_fw(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+ int error;
+
+ error = mutex_lock_interruptible(&ts->sysfs_mutex);
+ if (error)
+ return error;
+
+ error = elants_i2c_fw_update(ts);
+ dev_dbg(dev, "firmware update result: %d\n", error);
+
+ mutex_unlock(&ts->sysfs_mutex);
+ return error ?: count;
+}
+
+static ssize_t show_iap_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%s\n",
+ ts->iap_mode == ELAN_IAP_OPERATIONAL ?
+ "Normal" : "Recovery");
+}
+
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, calibrate_store);
+static DEVICE_ATTR(iap_mode, S_IRUGO, show_iap_mode, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
+
+struct elants_version_attribute {
+ struct device_attribute dattr;
+ size_t field_offset;
+ size_t field_size;
+};
+
+#define __ELANTS_FIELD_SIZE(_field) \
+ sizeof(((struct elants_data *)NULL)->_field)
+#define __ELANTS_VERIFY_SIZE(_field) \
+ (BUILD_BUG_ON_ZERO(__ELANTS_FIELD_SIZE(_field) > 2) + \
+ __ELANTS_FIELD_SIZE(_field))
+#define ELANTS_VERSION_ATTR(_field) \
+ struct elants_version_attribute elants_ver_attr_##_field = { \
+ .dattr = __ATTR(_field, S_IRUGO, \
+ elants_version_attribute_show, NULL), \
+ .field_offset = offsetof(struct elants_data, _field), \
+ .field_size = __ELANTS_VERIFY_SIZE(_field), \
+ }
+
+static ssize_t elants_version_attribute_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+ struct elants_version_attribute *attr =
+ container_of(dattr, struct elants_version_attribute, dattr);
+ u8 *field = (u8 *)((char *)ts + attr->field_offset);
+ unsigned int fmt_size;
+ unsigned int val;
+
+ if (attr->field_size == 1) {
+ val = *field;
+ fmt_size = 2; /* 2 HEX digits */
+ } else {
+ val = *(u16 *)field;
+ fmt_size = 4; /* 4 HEX digits */
+ }
+
+ return sprintf(buf, "%0*x\n", fmt_size, val);
+}
+
+static ELANTS_VERSION_ATTR(fw_version);
+static ELANTS_VERSION_ATTR(hw_version);
+static ELANTS_VERSION_ATTR(test_version);
+static ELANTS_VERSION_ATTR(solution_version);
+static ELANTS_VERSION_ATTR(bc_version);
+static ELANTS_VERSION_ATTR(iap_version);
+
+static struct attribute *elants_attributes[] = {
+ &dev_attr_calibrate.attr,
+ &dev_attr_update_fw.attr,
+ &dev_attr_iap_mode.attr,
+
+ &elants_ver_attr_fw_version.dattr.attr,
+ &elants_ver_attr_hw_version.dattr.attr,
+ &elants_ver_attr_test_version.dattr.attr,
+ &elants_ver_attr_solution_version.dattr.attr,
+ &elants_ver_attr_bc_version.dattr.attr,
+ &elants_ver_attr_iap_version.dattr.attr,
+ NULL
+};
+
+static struct attribute_group elants_attribute_group = {
+ .attrs = elants_attributes,
+};
+
+static void elants_i2c_remove_sysfs_group(void *_data)
+{
+ struct elants_data *ts = _data;
+
+ sysfs_remove_group(&ts->client->dev.kobj, &elants_attribute_group);
+}
+
+static int elants_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ union i2c_smbus_data dummy;
+ struct elants_data *ts;
+ unsigned long irqflags;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev,
+ "%s: i2c check functionality error\n", DEVICE_NAME);
+ return -ENXIO;
+ }
+
+ /* Make sure there is something at this address */
+ if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+ I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+ dev_err(&client->dev, "nothing at this address\n");
+ return -ENXIO;
+ }
+
+ ts = devm_kzalloc(&client->dev, sizeof(struct elants_data), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ mutex_init(&ts->sysfs_mutex);
+ init_completion(&ts->cmd_done);
+
+ ts->client = client;
+ i2c_set_clientdata(client, ts);
+
+ error = elants_i2c_initialize(ts);
+ if (error) {
+ dev_err(&client->dev, "failed to initialize: %d\n", error);
+ return error;
+ }
+
+ ts->input = devm_input_allocate_device(&client->dev);
+ if (!ts->input) {
+ dev_err(&client->dev, "Failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ ts->input->name = "Elan Touchscreen";
+ ts->input->id.bustype = BUS_I2C;
+
+ __set_bit(BTN_TOUCH, ts->input->keybit);
+ __set_bit(EV_ABS, ts->input->evbit);
+ __set_bit(EV_KEY, ts->input->evbit);
+
+ /* Single touch input params setup */
+ input_set_abs_params(ts->input, ABS_X, 0, ts->x_max, 0, 0);
+ input_set_abs_params(ts->input, ABS_Y, 0, ts->y_max, 0, 0);
+ input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0);
+ input_abs_set_res(ts->input, ABS_X, ts->x_res);
+ input_abs_set_res(ts->input, ABS_Y, ts->y_res);
+
+ /* Multitouch input params setup */
+ error = input_mt_init_slots(ts->input, MAX_CONTACT_NUM,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to initialize MT slots: %d\n", error);
+ return error;
+ }
+
+ input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->x_max, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->y_max, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+ input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res);
+ input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res);
+
+ input_set_drvdata(ts->input, ts);
+
+ error = input_register_device(ts->input);
+ if (error) {
+ dev_err(&client->dev,
+ "unable to register input device: %d\n", error);
+ return error;
+ }
+
+ /*
+ * Systems using device tree should set up interrupt via DTS,
+ * the rest will use the default falling edge interrupts.
+ */
+ irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING;
+
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, elants_i2c_irq,
+ irqflags | IRQF_ONESHOT,
+ client->name, ts);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ return error;
+ }
+
+ /*
+ * Systems using device tree should set up wakeup via DTS,
+ * the rest will configure device as wakeup source by default.
+ */
+ if (!client->dev.of_node)
+ device_init_wakeup(&client->dev, true);
+
+ error = sysfs_create_group(&client->dev.kobj, &elants_attribute_group);
+ if (error) {
+ dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+ error);
+ return error;
+ }
+
+ error = devm_add_action(&client->dev,
+ elants_i2c_remove_sysfs_group, ts);
+ if (error) {
+ elants_i2c_remove_sysfs_group(ts);
+ dev_err(&client->dev,
+ "Failed to add sysfs cleanup action: %d\n",
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused elants_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+ const u8 set_sleep_cmd[] = { 0x54, 0x50, 0x00, 0x01 };
+ int retry_cnt;
+ int error;
+
+ /* Command not support in IAP recovery mode */
+ if (ts->iap_mode != ELAN_IAP_OPERATIONAL)
+ return -EBUSY;
+
+ disable_irq(client->irq);
+
+ for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+ error = elants_i2c_send(client, set_sleep_cmd,
+ sizeof(set_sleep_cmd));
+ if (!error)
+ break;
+
+ dev_err(&client->dev, "suspend command failed: %d\n", error);
+ }
+
+ if (device_may_wakeup(dev))
+ ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+
+ return 0;
+}
+
+static int __maybe_unused elants_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+ const u8 set_active_cmd[] = { 0x54, 0x58, 0x00, 0x01 };
+ int retry_cnt;
+ int error;
+
+ if (device_may_wakeup(dev) && ts->wake_irq_enabled)
+ disable_irq_wake(client->irq);
+
+ for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+ error = elants_i2c_send(client, set_active_cmd,
+ sizeof(set_active_cmd));
+ if (!error)
+ break;
+
+ dev_err(&client->dev, "resume command failed: %d\n", error);
+ }
+
+ ts->state = ELAN_STATE_NORMAL;
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(elants_i2c_pm_ops,
+ elants_i2c_suspend, elants_i2c_resume);
+
+static const struct i2c_device_id elants_i2c_id[] = {
+ { DEVICE_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, elants_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id elants_acpi_id[] = {
+ { "ELAN0001", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, elants_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id elants_of_match[] = {
+ { .compatible = "elan,ekth3500" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, elants_of_match);
+#endif
+
+static struct i2c_driver elants_i2c_driver = {
+ .probe = elants_i2c_probe,
+ .id_table = elants_i2c_id,
+ .driver = {
+ .name = DEVICE_NAME,
+ .owner = THIS_MODULE,
+ .pm = &elants_i2c_pm_ops,
+ .acpi_match_table = ACPI_PTR(elants_acpi_id),
+ .of_match_table = of_match_ptr(elants_of_match),
+ },
+};
+module_i2c_driver(elants_i2c_driver);
+
+MODULE_AUTHOR("Scott Liu <scott.liu@emc.com.tw>");
+MODULE_DESCRIPTION("Elan I2c Touchscreen driver");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c
new file mode 100644
index 000000000..8051a4b70
--- /dev/null
+++ b/drivers/input/touchscreen/elo.c
@@ -0,0 +1,407 @@
+/*
+ * Elo serial touchscreen driver
+ *
+ * Copyright (c) 2004 Vojtech Pavlik
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * This driver can handle serial Elo touchscreens using either the Elo standard
+ * 'E271-2210' 10-byte protocol, Elo legacy 'E281A-4002' 6-byte protocol, Elo
+ * legacy 'E271-140' 4-byte protocol and Elo legacy 'E261-280' 3-byte protocol.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/ctype.h>
+
+#define DRIVER_DESC "Elo serial touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define ELO_MAX_LENGTH 10
+
+#define ELO10_PACKET_LEN 8
+#define ELO10_TOUCH 0x03
+#define ELO10_PRESSURE 0x80
+
+#define ELO10_LEAD_BYTE 'U'
+
+#define ELO10_ID_CMD 'i'
+
+#define ELO10_TOUCH_PACKET 'T'
+#define ELO10_ACK_PACKET 'A'
+#define ELI10_ID_PACKET 'I'
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct elo {
+ struct input_dev *dev;
+ struct serio *serio;
+ struct mutex cmd_mutex;
+ struct completion cmd_done;
+ int id;
+ int idx;
+ unsigned char expected_packet;
+ unsigned char csum;
+ unsigned char data[ELO_MAX_LENGTH];
+ unsigned char response[ELO10_PACKET_LEN];
+ char phys[32];
+};
+
+static void elo_process_data_10(struct elo *elo, unsigned char data)
+{
+ struct input_dev *dev = elo->dev;
+
+ elo->data[elo->idx] = data;
+
+ switch (elo->idx++) {
+ case 0:
+ elo->csum = 0xaa;
+ if (data != ELO10_LEAD_BYTE) {
+ dev_dbg(&elo->serio->dev,
+ "unsynchronized data: 0x%02x\n", data);
+ elo->idx = 0;
+ }
+ break;
+
+ case 9:
+ elo->idx = 0;
+ if (data != elo->csum) {
+ dev_dbg(&elo->serio->dev,
+ "bad checksum: 0x%02x, expected 0x%02x\n",
+ data, elo->csum);
+ break;
+ }
+ if (elo->data[1] != elo->expected_packet) {
+ if (elo->data[1] != ELO10_TOUCH_PACKET)
+ dev_dbg(&elo->serio->dev,
+ "unexpected packet: 0x%02x\n",
+ elo->data[1]);
+ break;
+ }
+ if (likely(elo->data[1] == ELO10_TOUCH_PACKET)) {
+ input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]);
+ input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]);
+ if (elo->data[2] & ELO10_PRESSURE)
+ input_report_abs(dev, ABS_PRESSURE,
+ (elo->data[8] << 8) | elo->data[7]);
+ input_report_key(dev, BTN_TOUCH, elo->data[2] & ELO10_TOUCH);
+ input_sync(dev);
+ } else if (elo->data[1] == ELO10_ACK_PACKET) {
+ if (elo->data[2] == '0')
+ elo->expected_packet = ELO10_TOUCH_PACKET;
+ complete(&elo->cmd_done);
+ } else {
+ memcpy(elo->response, &elo->data[1], ELO10_PACKET_LEN);
+ elo->expected_packet = ELO10_ACK_PACKET;
+ }
+ break;
+ }
+ elo->csum += data;
+}
+
+static void elo_process_data_6(struct elo *elo, unsigned char data)
+{
+ struct input_dev *dev = elo->dev;
+
+ elo->data[elo->idx] = data;
+
+ switch (elo->idx++) {
+
+ case 0:
+ if ((data & 0xc0) != 0xc0)
+ elo->idx = 0;
+ break;
+
+ case 1:
+ if ((data & 0xc0) != 0x80)
+ elo->idx = 0;
+ break;
+
+ case 2:
+ if ((data & 0xc0) != 0x40)
+ elo->idx = 0;
+ break;
+
+ case 3:
+ if (data & 0xc0) {
+ elo->idx = 0;
+ break;
+ }
+
+ input_report_abs(dev, ABS_X, ((elo->data[0] & 0x3f) << 6) | (elo->data[1] & 0x3f));
+ input_report_abs(dev, ABS_Y, ((elo->data[2] & 0x3f) << 6) | (elo->data[3] & 0x3f));
+
+ if (elo->id == 2) {
+ input_report_key(dev, BTN_TOUCH, 1);
+ input_sync(dev);
+ elo->idx = 0;
+ }
+
+ break;
+
+ case 4:
+ if (data) {
+ input_sync(dev);
+ elo->idx = 0;
+ }
+ break;
+
+ case 5:
+ if ((data & 0xf0) == 0) {
+ input_report_abs(dev, ABS_PRESSURE, elo->data[5]);
+ input_report_key(dev, BTN_TOUCH, !!elo->data[5]);
+ }
+ input_sync(dev);
+ elo->idx = 0;
+ break;
+ }
+}
+
+static void elo_process_data_3(struct elo *elo, unsigned char data)
+{
+ struct input_dev *dev = elo->dev;
+
+ elo->data[elo->idx] = data;
+
+ switch (elo->idx++) {
+
+ case 0:
+ if ((data & 0x7f) != 0x01)
+ elo->idx = 0;
+ break;
+ case 2:
+ input_report_key(dev, BTN_TOUCH, !(elo->data[1] & 0x80));
+ input_report_abs(dev, ABS_X, elo->data[1]);
+ input_report_abs(dev, ABS_Y, elo->data[2]);
+ input_sync(dev);
+ elo->idx = 0;
+ break;
+ }
+}
+
+static irqreturn_t elo_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct elo *elo = serio_get_drvdata(serio);
+
+ switch (elo->id) {
+ case 0:
+ elo_process_data_10(elo, data);
+ break;
+
+ case 1:
+ case 2:
+ elo_process_data_6(elo, data);
+ break;
+
+ case 3:
+ elo_process_data_3(elo, data);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int elo_command_10(struct elo *elo, unsigned char *packet)
+{
+ int rc = -1;
+ int i;
+ unsigned char csum = 0xaa + ELO10_LEAD_BYTE;
+
+ mutex_lock(&elo->cmd_mutex);
+
+ serio_pause_rx(elo->serio);
+ elo->expected_packet = toupper(packet[0]);
+ init_completion(&elo->cmd_done);
+ serio_continue_rx(elo->serio);
+
+ if (serio_write(elo->serio, ELO10_LEAD_BYTE))
+ goto out;
+
+ for (i = 0; i < ELO10_PACKET_LEN; i++) {
+ csum += packet[i];
+ if (serio_write(elo->serio, packet[i]))
+ goto out;
+ }
+
+ if (serio_write(elo->serio, csum))
+ goto out;
+
+ wait_for_completion_timeout(&elo->cmd_done, HZ);
+
+ if (elo->expected_packet == ELO10_TOUCH_PACKET) {
+ /* We are back in reporting mode, the command was ACKed */
+ memcpy(packet, elo->response, ELO10_PACKET_LEN);
+ rc = 0;
+ }
+
+ out:
+ mutex_unlock(&elo->cmd_mutex);
+ return rc;
+}
+
+static int elo_setup_10(struct elo *elo)
+{
+ static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" };
+ struct input_dev *dev = elo->dev;
+ unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD };
+
+ if (elo_command_10(elo, packet))
+ return -1;
+
+ dev->id.version = (packet[5] << 8) | packet[4];
+
+ input_set_abs_params(dev, ABS_X, 96, 4000, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 96, 4000, 0, 0);
+ if (packet[3] & ELO10_PRESSURE)
+ input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+
+ dev_info(&elo->serio->dev,
+ "%sTouch touchscreen, fw: %02x.%02x, features: 0x%02x, controller: 0x%02x\n",
+ elo_types[(packet[1] -'0') & 0x03],
+ packet[5], packet[4], packet[3], packet[7]);
+
+ return 0;
+}
+
+/*
+ * elo_disconnect() is the opposite of elo_connect()
+ */
+
+static void elo_disconnect(struct serio *serio)
+{
+ struct elo *elo = serio_get_drvdata(serio);
+
+ input_get_device(elo->dev);
+ input_unregister_device(elo->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(elo->dev);
+ kfree(elo);
+}
+
+/*
+ * elo_connect() is the routine that is called when someone adds a
+ * new serio device that supports Gunze protocol and registers it as
+ * an input device.
+ */
+
+static int elo_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct elo *elo;
+ struct input_dev *input_dev;
+ int err;
+
+ elo = kzalloc(sizeof(struct elo), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!elo || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ elo->serio = serio;
+ elo->id = serio->id.id;
+ elo->dev = input_dev;
+ elo->expected_packet = ELO10_TOUCH_PACKET;
+ mutex_init(&elo->cmd_mutex);
+ init_completion(&elo->cmd_done);
+ snprintf(elo->phys, sizeof(elo->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "Elo Serial TouchScreen";
+ input_dev->phys = elo->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_ELO;
+ input_dev->id.product = elo->id;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ serio_set_drvdata(serio, elo);
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ switch (elo->id) {
+
+ case 0: /* 10-byte protocol */
+ if (elo_setup_10(elo))
+ goto fail3;
+
+ break;
+
+ case 1: /* 6-byte protocol */
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 15, 0, 0);
+
+ case 2: /* 4-byte protocol */
+ input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 96, 4000, 0, 0);
+ break;
+
+ case 3: /* 3-byte protocol */
+ input_set_abs_params(input_dev, ABS_X, 0, 255, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 255, 0, 0);
+ break;
+ }
+
+ err = input_register_device(elo->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(elo);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id elo_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_ELO,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, elo_serio_ids);
+
+static struct serio_driver elo_drv = {
+ .driver = {
+ .name = "elo",
+ },
+ .description = DRIVER_DESC,
+ .id_table = elo_serio_ids,
+ .interrupt = elo_interrupt,
+ .connect = elo_connect,
+ .disconnect = elo_disconnect,
+};
+
+module_serio_driver(elo_drv);
diff --git a/drivers/input/touchscreen/fujitsu_ts.c b/drivers/input/touchscreen/fujitsu_ts.c
new file mode 100644
index 000000000..d0e46a7e1
--- /dev/null
+++ b/drivers/input/touchscreen/fujitsu_ts.c
@@ -0,0 +1,177 @@
+/*
+ * Fujitsu serial touchscreen driver
+ *
+ * Copyright (c) Dmitry Torokhov <dtor@mail.ru>
+ */
+
+/*
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "Fujitsu serial touchscreen driver"
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define FUJITSU_LENGTH 5
+
+/*
+ * Per-touchscreen data.
+ */
+struct fujitsu {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[FUJITSU_LENGTH];
+ char phys[32];
+};
+
+/*
+ * Decode serial data (5 bytes per packet)
+ * First byte
+ * 1 C 0 0 R S S S
+ * Where C is 1 while in calibration mode (which we don't use)
+ * R is 1 when no coordinate corection was done.
+ * S are button state
+ */
+static irqreturn_t fujitsu_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct fujitsu *fujitsu = serio_get_drvdata(serio);
+ struct input_dev *dev = fujitsu->dev;
+
+ if (fujitsu->idx == 0) {
+ /* resync skip until start of frame */
+ if ((data & 0xf0) != 0x80)
+ return IRQ_HANDLED;
+ } else {
+ /* resync skip garbage */
+ if (data & 0x80) {
+ fujitsu->idx = 0;
+ return IRQ_HANDLED;
+ }
+ }
+
+ fujitsu->data[fujitsu->idx++] = data;
+ if (fujitsu->idx == FUJITSU_LENGTH) {
+ input_report_abs(dev, ABS_X,
+ (fujitsu->data[2] << 7) | fujitsu->data[1]);
+ input_report_abs(dev, ABS_Y,
+ (fujitsu->data[4] << 7) | fujitsu->data[3]);
+ input_report_key(dev, BTN_TOUCH,
+ (fujitsu->data[0] & 0x03) != 2);
+ input_sync(dev);
+ fujitsu->idx = 0;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * fujitsu_disconnect() is the opposite of fujitsu_connect()
+ */
+static void fujitsu_disconnect(struct serio *serio)
+{
+ struct fujitsu *fujitsu = serio_get_drvdata(serio);
+
+ input_get_device(fujitsu->dev);
+ input_unregister_device(fujitsu->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(fujitsu->dev);
+ kfree(fujitsu);
+}
+
+/*
+ * fujitsu_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Fujitsu protocol and registers it
+ * as input device.
+ */
+static int fujitsu_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct fujitsu *fujitsu;
+ struct input_dev *input_dev;
+ int err;
+
+ fujitsu = kzalloc(sizeof(struct fujitsu), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!fujitsu || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ fujitsu->serio = serio;
+ fujitsu->dev = input_dev;
+ snprintf(fujitsu->phys, sizeof(fujitsu->phys),
+ "%s/input0", serio->phys);
+
+ input_dev->name = "Fujitsu Serial Touchscreen";
+ input_dev->phys = fujitsu->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_FUJITSU;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, 4096, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 4096, 0, 0);
+ serio_set_drvdata(serio, fujitsu);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(fujitsu->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3:
+ serio_close(serio);
+ fail2:
+ serio_set_drvdata(serio, NULL);
+ fail1:
+ input_free_device(input_dev);
+ kfree(fujitsu);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+static struct serio_device_id fujitsu_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_FUJITSU,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, fujitsu_serio_ids);
+
+static struct serio_driver fujitsu_drv = {
+ .driver = {
+ .name = "fujitsu_ts",
+ },
+ .description = DRIVER_DESC,
+ .id_table = fujitsu_serio_ids,
+ .interrupt = fujitsu_interrupt,
+ .connect = fujitsu_connect,
+ .disconnect = fujitsu_disconnect,
+};
+
+module_serio_driver(fujitsu_drv);
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
new file mode 100644
index 000000000..3af16984d
--- /dev/null
+++ b/drivers/input/touchscreen/goodix.c
@@ -0,0 +1,417 @@
+/*
+ * Driver for Goodix Touchscreens
+ *
+ * Copyright (c) 2014 Red Hat Inc.
+ *
+ * This code is based on gt9xx.c authored by andrew@goodix.com:
+ *
+ * 2010 - 2012 Goodix Technology.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ */
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <asm/unaligned.h>
+
+struct goodix_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ int abs_x_max;
+ int abs_y_max;
+ unsigned int max_touch_num;
+ unsigned int int_trigger_type;
+};
+
+#define GOODIX_MAX_HEIGHT 4096
+#define GOODIX_MAX_WIDTH 4096
+#define GOODIX_INT_TRIGGER 1
+#define GOODIX_CONTACT_SIZE 8
+#define GOODIX_MAX_CONTACTS 10
+
+#define GOODIX_CONFIG_MAX_LENGTH 240
+
+/* Register defines */
+#define GOODIX_READ_COOR_ADDR 0x814E
+#define GOODIX_REG_CONFIG_DATA 0x8047
+#define GOODIX_REG_VERSION 0x8140
+
+#define RESOLUTION_LOC 1
+#define MAX_CONTACTS_LOC 5
+#define TRIGGER_LOC 6
+
+static const unsigned long goodix_irq_flags[] = {
+ IRQ_TYPE_EDGE_RISING,
+ IRQ_TYPE_EDGE_FALLING,
+ IRQ_TYPE_LEVEL_LOW,
+ IRQ_TYPE_LEVEL_HIGH,
+};
+
+/**
+ * goodix_i2c_read - read data from a register of the i2c slave device.
+ *
+ * @client: i2c device.
+ * @reg: the register to read from.
+ * @buf: raw write data buffer.
+ * @len: length of the buffer to write
+ */
+static int goodix_i2c_read(struct i2c_client *client,
+ u16 reg, u8 *buf, int len)
+{
+ struct i2c_msg msgs[2];
+ u16 wbuf = cpu_to_be16(reg);
+ int ret;
+
+ msgs[0].flags = 0;
+ msgs[0].addr = client->addr;
+ msgs[0].len = 2;
+ msgs[0].buf = (u8 *) &wbuf;
+
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].addr = client->addr;
+ msgs[1].len = len;
+ msgs[1].buf = buf;
+
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
+}
+
+static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
+{
+ int touch_num;
+ int error;
+
+ error = goodix_i2c_read(ts->client, GOODIX_READ_COOR_ADDR, data,
+ GOODIX_CONTACT_SIZE + 1);
+ if (error) {
+ dev_err(&ts->client->dev, "I2C transfer error: %d\n", error);
+ return error;
+ }
+
+ touch_num = data[0] & 0x0f;
+ if (touch_num > ts->max_touch_num)
+ return -EPROTO;
+
+ if (touch_num > 1) {
+ data += 1 + GOODIX_CONTACT_SIZE;
+ error = goodix_i2c_read(ts->client,
+ GOODIX_READ_COOR_ADDR +
+ 1 + GOODIX_CONTACT_SIZE,
+ data,
+ GOODIX_CONTACT_SIZE * (touch_num - 1));
+ if (error)
+ return error;
+ }
+
+ return touch_num;
+}
+
+static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data)
+{
+ int id = coor_data[0] & 0x0F;
+ int input_x = get_unaligned_le16(&coor_data[1]);
+ int input_y = get_unaligned_le16(&coor_data[3]);
+ int input_w = get_unaligned_le16(&coor_data[5]);
+
+ input_mt_slot(ts->input_dev, id);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);
+}
+
+/**
+ * goodix_process_events - Process incoming events
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * Called when the IRQ is triggered. Read the current device state, and push
+ * the input events to the user space.
+ */
+static void goodix_process_events(struct goodix_ts_data *ts)
+{
+ u8 point_data[1 + GOODIX_CONTACT_SIZE * ts->max_touch_num];
+ int touch_num;
+ int i;
+
+ touch_num = goodix_ts_read_input_report(ts, point_data);
+ if (touch_num < 0)
+ return;
+
+ for (i = 0; i < touch_num; i++)
+ goodix_ts_report_touch(ts,
+ &point_data[1 + GOODIX_CONTACT_SIZE * i]);
+
+ input_mt_sync_frame(ts->input_dev);
+ input_sync(ts->input_dev);
+}
+
+/**
+ * goodix_ts_irq_handler - The IRQ handler
+ *
+ * @irq: interrupt number.
+ * @dev_id: private data pointer.
+ */
+static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
+{
+ static const u8 end_cmd[] = {
+ GOODIX_READ_COOR_ADDR >> 8,
+ GOODIX_READ_COOR_ADDR & 0xff,
+ 0
+ };
+ struct goodix_ts_data *ts = dev_id;
+
+ goodix_process_events(ts);
+
+ if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0)
+ dev_err(&ts->client->dev, "I2C write end_cmd error\n");
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * goodix_read_config - Read the embedded configuration of the panel
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * Must be called during probe
+ */
+static void goodix_read_config(struct goodix_ts_data *ts)
+{
+ u8 config[GOODIX_CONFIG_MAX_LENGTH];
+ int error;
+
+ error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
+ config,
+ GOODIX_CONFIG_MAX_LENGTH);
+ if (error) {
+ dev_warn(&ts->client->dev,
+ "Error reading config (%d), using defaults\n",
+ error);
+ ts->abs_x_max = GOODIX_MAX_WIDTH;
+ ts->abs_y_max = GOODIX_MAX_HEIGHT;
+ ts->int_trigger_type = GOODIX_INT_TRIGGER;
+ ts->max_touch_num = GOODIX_MAX_CONTACTS;
+ return;
+ }
+
+ ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]);
+ ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]);
+ ts->int_trigger_type = config[TRIGGER_LOC] & 0x03;
+ ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f;
+ if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) {
+ dev_err(&ts->client->dev,
+ "Invalid config, using defaults\n");
+ ts->abs_x_max = GOODIX_MAX_WIDTH;
+ ts->abs_y_max = GOODIX_MAX_HEIGHT;
+ ts->max_touch_num = GOODIX_MAX_CONTACTS;
+ }
+}
+
+/**
+ * goodix_read_version - Read goodix touchscreen version
+ *
+ * @client: the i2c client
+ * @version: output buffer containing the version on success
+ */
+static int goodix_read_version(struct i2c_client *client, u16 *version)
+{
+ int error;
+ u8 buf[6];
+
+ error = goodix_i2c_read(client, GOODIX_REG_VERSION, buf, sizeof(buf));
+ if (error) {
+ dev_err(&client->dev, "read version failed: %d\n", error);
+ return error;
+ }
+
+ if (version)
+ *version = get_unaligned_le16(&buf[4]);
+
+ dev_info(&client->dev, "IC VERSION: %6ph\n", buf);
+
+ return 0;
+}
+
+/**
+ * goodix_i2c_test - I2C test function to check if the device answers.
+ *
+ * @client: the i2c client
+ */
+static int goodix_i2c_test(struct i2c_client *client)
+{
+ int retry = 0;
+ int error;
+ u8 test;
+
+ while (retry++ < 2) {
+ error = goodix_i2c_read(client, GOODIX_REG_CONFIG_DATA,
+ &test, 1);
+ if (!error)
+ return 0;
+
+ dev_err(&client->dev, "i2c test failed attempt %d: %d\n",
+ retry, error);
+ msleep(20);
+ }
+
+ return error;
+}
+
+/**
+ * goodix_request_input_dev - Allocate, populate and register the input device
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * Must be called during probe
+ */
+static int goodix_request_input_dev(struct goodix_ts_data *ts)
+{
+ int error;
+
+ ts->input_dev = devm_input_allocate_device(&ts->client->dev);
+ if (!ts->input_dev) {
+ dev_err(&ts->client->dev, "Failed to allocate input device.");
+ return -ENOMEM;
+ }
+
+ ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) |
+ BIT_MASK(EV_KEY) |
+ BIT_MASK(EV_ABS);
+
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0,
+ ts->abs_x_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0,
+ ts->abs_y_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+ input_mt_init_slots(ts->input_dev, ts->max_touch_num,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+
+ ts->input_dev->name = "Goodix Capacitive TouchScreen";
+ ts->input_dev->phys = "input/ts";
+ ts->input_dev->id.bustype = BUS_I2C;
+ ts->input_dev->id.vendor = 0x0416;
+ ts->input_dev->id.product = 0x1001;
+ ts->input_dev->id.version = 10427;
+
+ error = input_register_device(ts->input_dev);
+ if (error) {
+ dev_err(&ts->client->dev,
+ "Failed to register input device: %d", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int goodix_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct goodix_ts_data *ts;
+ unsigned long irq_flags;
+ int error;
+ u16 version_info;
+
+ dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "I2C check functionality failed.\n");
+ return -ENXIO;
+ }
+
+ ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ ts->client = client;
+ i2c_set_clientdata(client, ts);
+
+ error = goodix_i2c_test(client);
+ if (error) {
+ dev_err(&client->dev, "I2C communication failure: %d\n", error);
+ return error;
+ }
+
+ error = goodix_read_version(client, &version_info);
+ if (error) {
+ dev_err(&client->dev, "Read version failed.\n");
+ return error;
+ }
+
+ goodix_read_config(ts);
+
+ error = goodix_request_input_dev(ts);
+ if (error)
+ return error;
+
+ irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
+ error = devm_request_threaded_irq(&ts->client->dev, client->irq,
+ NULL, goodix_ts_irq_handler,
+ irq_flags, client->name, ts);
+ if (error) {
+ dev_err(&client->dev, "request IRQ failed: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id goodix_ts_id[] = {
+ { "GDIX1001:00", 0 },
+ { }
+};
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id goodix_acpi_match[] = {
+ { "GDIX1001", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id goodix_of_match[] = {
+ { .compatible = "goodix,gt911" },
+ { .compatible = "goodix,gt9110" },
+ { .compatible = "goodix,gt912" },
+ { .compatible = "goodix,gt927" },
+ { .compatible = "goodix,gt9271" },
+ { .compatible = "goodix,gt928" },
+ { .compatible = "goodix,gt967" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, goodix_of_match);
+#endif
+
+static struct i2c_driver goodix_ts_driver = {
+ .probe = goodix_ts_probe,
+ .id_table = goodix_ts_id,
+ .driver = {
+ .name = "Goodix-TS",
+ .owner = THIS_MODULE,
+ .acpi_match_table = ACPI_PTR(goodix_acpi_match),
+ .of_match_table = of_match_ptr(goodix_of_match),
+ },
+};
+module_i2c_driver(goodix_ts_driver);
+
+MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
+MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
+MODULE_DESCRIPTION("Goodix touchscreen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c
new file mode 100644
index 000000000..e2ee62615
--- /dev/null
+++ b/drivers/input/touchscreen/gunze.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ */
+
+/*
+ * Gunze AHL-51S touchscreen driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "Gunze AHL-51S touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define GUNZE_MAX_LENGTH 10
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct gunze {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[GUNZE_MAX_LENGTH];
+ char phys[32];
+};
+
+static void gunze_process_packet(struct gunze* gunze)
+{
+ struct input_dev *dev = gunze->dev;
+
+ if (gunze->idx != GUNZE_MAX_LENGTH || gunze->data[5] != ',' ||
+ (gunze->data[0] != 'T' && gunze->data[0] != 'R')) {
+ printk(KERN_WARNING "gunze.c: bad packet: >%.*s<\n", GUNZE_MAX_LENGTH, gunze->data);
+ return;
+ }
+
+ input_report_abs(dev, ABS_X, simple_strtoul(gunze->data + 1, NULL, 10));
+ input_report_abs(dev, ABS_Y, 1024 - simple_strtoul(gunze->data + 6, NULL, 10));
+ input_report_key(dev, BTN_TOUCH, gunze->data[0] == 'T');
+ input_sync(dev);
+}
+
+static irqreturn_t gunze_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct gunze* gunze = serio_get_drvdata(serio);
+
+ if (data == '\r') {
+ gunze_process_packet(gunze);
+ gunze->idx = 0;
+ } else {
+ if (gunze->idx < GUNZE_MAX_LENGTH)
+ gunze->data[gunze->idx++] = data;
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+ * gunze_disconnect() is the opposite of gunze_connect()
+ */
+
+static void gunze_disconnect(struct serio *serio)
+{
+ struct gunze *gunze = serio_get_drvdata(serio);
+
+ input_get_device(gunze->dev);
+ input_unregister_device(gunze->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(gunze->dev);
+ kfree(gunze);
+}
+
+/*
+ * gunze_connect() is the routine that is called when someone adds a
+ * new serio device that supports Gunze protocol and registers it as
+ * an input device.
+ */
+
+static int gunze_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct gunze *gunze;
+ struct input_dev *input_dev;
+ int err;
+
+ gunze = kzalloc(sizeof(struct gunze), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!gunze || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ gunze->serio = serio;
+ gunze->dev = input_dev;
+ snprintf(gunze->phys, sizeof(serio->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "Gunze AHL-51S TouchScreen";
+ input_dev->phys = gunze->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_GUNZE;
+ input_dev->id.product = 0x0051;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(input_dev, ABS_X, 24, 1000, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 24, 1000, 0, 0);
+
+ serio_set_drvdata(serio, gunze);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(gunze->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(gunze);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id gunze_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_GUNZE,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, gunze_serio_ids);
+
+static struct serio_driver gunze_drv = {
+ .driver = {
+ .name = "gunze",
+ },
+ .description = DRIVER_DESC,
+ .id_table = gunze_serio_ids,
+ .interrupt = gunze_interrupt,
+ .connect = gunze_connect,
+ .disconnect = gunze_disconnect,
+};
+
+module_serio_driver(gunze_drv);
diff --git a/drivers/input/touchscreen/hampshire.c b/drivers/input/touchscreen/hampshire.c
new file mode 100644
index 000000000..ecb1e0e01
--- /dev/null
+++ b/drivers/input/touchscreen/hampshire.c
@@ -0,0 +1,189 @@
+/*
+ * Hampshire serial touchscreen driver
+ *
+ * Copyright (c) 2010 Adam Bennett
+ * Based on the dynapro driver (c) Tias Guns
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2010/04/08 Adam Bennett <abennett72@gmail.com>
+ * Copied dynapro.c and edited for Hampshire 4-byte protocol
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "Hampshire serial touchscreen driver"
+
+MODULE_AUTHOR("Adam Bennett <abennett72@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define HAMPSHIRE_FORMAT_TOUCH_BIT 0x40
+#define HAMPSHIRE_FORMAT_LENGTH 4
+#define HAMPSHIRE_RESPONSE_BEGIN_BYTE 0x80
+
+#define HAMPSHIRE_MIN_XC 0
+#define HAMPSHIRE_MAX_XC 0x1000
+#define HAMPSHIRE_MIN_YC 0
+#define HAMPSHIRE_MAX_YC 0x1000
+
+#define HAMPSHIRE_GET_XC(data) (((data[3] & 0x0c) >> 2) | (data[1] << 2) | ((data[0] & 0x38) << 6))
+#define HAMPSHIRE_GET_YC(data) ((data[3] & 0x03) | (data[2] << 2) | ((data[0] & 0x07) << 9))
+#define HAMPSHIRE_GET_TOUCHED(data) (HAMPSHIRE_FORMAT_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct hampshire {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[HAMPSHIRE_FORMAT_LENGTH];
+ char phys[32];
+};
+
+static void hampshire_process_data(struct hampshire *phampshire)
+{
+ struct input_dev *dev = phampshire->dev;
+
+ if (HAMPSHIRE_FORMAT_LENGTH == ++phampshire->idx) {
+ input_report_abs(dev, ABS_X, HAMPSHIRE_GET_XC(phampshire->data));
+ input_report_abs(dev, ABS_Y, HAMPSHIRE_GET_YC(phampshire->data));
+ input_report_key(dev, BTN_TOUCH,
+ HAMPSHIRE_GET_TOUCHED(phampshire->data));
+ input_sync(dev);
+
+ phampshire->idx = 0;
+ }
+}
+
+static irqreturn_t hampshire_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct hampshire *phampshire = serio_get_drvdata(serio);
+
+ phampshire->data[phampshire->idx] = data;
+
+ if (HAMPSHIRE_RESPONSE_BEGIN_BYTE & phampshire->data[0])
+ hampshire_process_data(phampshire);
+ else
+ dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
+ phampshire->data[0]);
+
+ return IRQ_HANDLED;
+}
+
+static void hampshire_disconnect(struct serio *serio)
+{
+ struct hampshire *phampshire = serio_get_drvdata(serio);
+
+ input_get_device(phampshire->dev);
+ input_unregister_device(phampshire->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(phampshire->dev);
+ kfree(phampshire);
+}
+
+/*
+ * hampshire_connect() is the routine that is called when someone adds a
+ * new serio device that supports hampshire protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+
+static int hampshire_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct hampshire *phampshire;
+ struct input_dev *input_dev;
+ int err;
+
+ phampshire = kzalloc(sizeof(struct hampshire), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!phampshire || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ phampshire->serio = serio;
+ phampshire->dev = input_dev;
+ snprintf(phampshire->phys, sizeof(phampshire->phys),
+ "%s/input0", serio->phys);
+
+ input_dev->name = "Hampshire Serial TouchScreen";
+ input_dev->phys = phampshire->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_HAMPSHIRE;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0001;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(phampshire->dev, ABS_X,
+ HAMPSHIRE_MIN_XC, HAMPSHIRE_MAX_XC, 0, 0);
+ input_set_abs_params(phampshire->dev, ABS_Y,
+ HAMPSHIRE_MIN_YC, HAMPSHIRE_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, phampshire);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(phampshire->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(phampshire);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id hampshire_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_HAMPSHIRE,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, hampshire_serio_ids);
+
+static struct serio_driver hampshire_drv = {
+ .driver = {
+ .name = "hampshire",
+ },
+ .description = DRIVER_DESC,
+ .id_table = hampshire_serio_ids,
+ .interrupt = hampshire_interrupt,
+ .connect = hampshire_connect,
+ .disconnect = hampshire_disconnect,
+};
+
+module_serio_driver(hampshire_drv);
diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c
new file mode 100644
index 000000000..85cf9bee8
--- /dev/null
+++ b/drivers/input/touchscreen/hp680_ts_input.c
@@ -0,0 +1,127 @@
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/adc.h>
+#include <mach/hp6xx.h>
+
+#define MODNAME "hp680_ts_input"
+
+#define HP680_TS_ABS_X_MIN 40
+#define HP680_TS_ABS_X_MAX 950
+#define HP680_TS_ABS_Y_MIN 80
+#define HP680_TS_ABS_Y_MAX 910
+
+#define PHDR 0xa400012e
+#define SCPDR 0xa4000136
+
+static void do_softint(struct work_struct *work);
+
+static struct input_dev *hp680_ts_dev;
+static DECLARE_DELAYED_WORK(work, do_softint);
+
+static void do_softint(struct work_struct *work)
+{
+ int absx = 0, absy = 0;
+ u8 scpdr;
+ int touched = 0;
+
+ if (__raw_readb(PHDR) & PHDR_TS_PEN_DOWN) {
+ scpdr = __raw_readb(SCPDR);
+ scpdr |= SCPDR_TS_SCAN_ENABLE;
+ scpdr &= ~SCPDR_TS_SCAN_Y;
+ __raw_writeb(scpdr, SCPDR);
+ udelay(30);
+
+ absy = adc_single(ADC_CHANNEL_TS_Y);
+
+ scpdr = __raw_readb(SCPDR);
+ scpdr |= SCPDR_TS_SCAN_Y;
+ scpdr &= ~SCPDR_TS_SCAN_X;
+ __raw_writeb(scpdr, SCPDR);
+ udelay(30);
+
+ absx = adc_single(ADC_CHANNEL_TS_X);
+
+ scpdr = __raw_readb(SCPDR);
+ scpdr |= SCPDR_TS_SCAN_X;
+ scpdr &= ~SCPDR_TS_SCAN_ENABLE;
+ __raw_writeb(scpdr, SCPDR);
+ udelay(100);
+ touched = __raw_readb(PHDR) & PHDR_TS_PEN_DOWN;
+ }
+
+ if (touched) {
+ input_report_key(hp680_ts_dev, BTN_TOUCH, 1);
+ input_report_abs(hp680_ts_dev, ABS_X, absx);
+ input_report_abs(hp680_ts_dev, ABS_Y, absy);
+ } else {
+ input_report_key(hp680_ts_dev, BTN_TOUCH, 0);
+ }
+
+ input_sync(hp680_ts_dev);
+ enable_irq(HP680_TS_IRQ);
+}
+
+static irqreturn_t hp680_ts_interrupt(int irq, void *dev)
+{
+ disable_irq_nosync(irq);
+ schedule_delayed_work(&work, HZ / 20);
+
+ return IRQ_HANDLED;
+}
+
+static int __init hp680_ts_init(void)
+{
+ int err;
+
+ hp680_ts_dev = input_allocate_device();
+ if (!hp680_ts_dev)
+ return -ENOMEM;
+
+ hp680_ts_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
+ hp680_ts_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(hp680_ts_dev, ABS_X,
+ HP680_TS_ABS_X_MIN, HP680_TS_ABS_X_MAX, 0, 0);
+ input_set_abs_params(hp680_ts_dev, ABS_Y,
+ HP680_TS_ABS_Y_MIN, HP680_TS_ABS_Y_MAX, 0, 0);
+
+ hp680_ts_dev->name = "HP Jornada touchscreen";
+ hp680_ts_dev->phys = "hp680_ts/input0";
+
+ if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt,
+ 0, MODNAME, NULL) < 0) {
+ printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n",
+ HP680_TS_IRQ);
+ err = -EBUSY;
+ goto fail1;
+ }
+
+ err = input_register_device(hp680_ts_dev);
+ if (err)
+ goto fail2;
+
+ return 0;
+
+ fail2: free_irq(HP680_TS_IRQ, NULL);
+ cancel_delayed_work_sync(&work);
+ fail1: input_free_device(hp680_ts_dev);
+ return err;
+}
+
+static void __exit hp680_ts_exit(void)
+{
+ free_irq(HP680_TS_IRQ, NULL);
+ cancel_delayed_work_sync(&work);
+ input_unregister_device(hp680_ts_dev);
+}
+
+module_init(hp680_ts_init);
+module_exit(hp680_ts_exit);
+
+MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
+MODULE_DESCRIPTION("HP Jornada 680 touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/htcpen.c b/drivers/input/touchscreen/htcpen.c
new file mode 100644
index 000000000..92e2243fb
--- /dev/null
+++ b/drivers/input/touchscreen/htcpen.c
@@ -0,0 +1,248 @@
+/*
+ * HTC Shift touchscreen driver
+ *
+ * Copyright (C) 2008 Pau Oliva Fora <pof@eslack.org>
+ *
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/isa.h>
+#include <linux/ioport.h>
+#include <linux/dmi.h>
+
+MODULE_AUTHOR("Pau Oliva Fora <pau@eslack.org>");
+MODULE_DESCRIPTION("HTC Shift touchscreen driver");
+MODULE_LICENSE("GPL");
+
+#define HTCPEN_PORT_IRQ_CLEAR 0x068
+#define HTCPEN_PORT_INIT 0x06c
+#define HTCPEN_PORT_INDEX 0x0250
+#define HTCPEN_PORT_DATA 0x0251
+#define HTCPEN_IRQ 3
+
+#define DEVICE_ENABLE 0xa2
+#define DEVICE_DISABLE 0xa3
+
+#define X_INDEX 3
+#define Y_INDEX 5
+#define TOUCH_INDEX 0xb
+#define LSB_XY_INDEX 0xc
+#define X_AXIS_MAX 2040
+#define Y_AXIS_MAX 2040
+
+static bool invert_x;
+module_param(invert_x, bool, 0644);
+MODULE_PARM_DESC(invert_x, "If set, X axis is inverted");
+static bool invert_y;
+module_param(invert_y, bool, 0644);
+MODULE_PARM_DESC(invert_y, "If set, Y axis is inverted");
+
+static irqreturn_t htcpen_interrupt(int irq, void *handle)
+{
+ struct input_dev *htcpen_dev = handle;
+ unsigned short x, y, xy;
+
+ /* 0 = press; 1 = release */
+ outb_p(TOUCH_INDEX, HTCPEN_PORT_INDEX);
+
+ if (inb_p(HTCPEN_PORT_DATA)) {
+ input_report_key(htcpen_dev, BTN_TOUCH, 0);
+ } else {
+ outb_p(X_INDEX, HTCPEN_PORT_INDEX);
+ x = inb_p(HTCPEN_PORT_DATA);
+
+ outb_p(Y_INDEX, HTCPEN_PORT_INDEX);
+ y = inb_p(HTCPEN_PORT_DATA);
+
+ outb_p(LSB_XY_INDEX, HTCPEN_PORT_INDEX);
+ xy = inb_p(HTCPEN_PORT_DATA);
+
+ /* get high resolution value of X and Y using LSB */
+ x = X_AXIS_MAX - ((x * 8) + ((xy >> 4) & 0xf));
+ y = (y * 8) + (xy & 0xf);
+ if (invert_x)
+ x = X_AXIS_MAX - x;
+ if (invert_y)
+ y = Y_AXIS_MAX - y;
+
+ if (x != X_AXIS_MAX && x != 0) {
+ input_report_key(htcpen_dev, BTN_TOUCH, 1);
+ input_report_abs(htcpen_dev, ABS_X, x);
+ input_report_abs(htcpen_dev, ABS_Y, y);
+ }
+ }
+
+ input_sync(htcpen_dev);
+
+ inb_p(HTCPEN_PORT_IRQ_CLEAR);
+
+ return IRQ_HANDLED;
+}
+
+static int htcpen_open(struct input_dev *dev)
+{
+ outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
+
+ return 0;
+}
+
+static void htcpen_close(struct input_dev *dev)
+{
+ outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
+ synchronize_irq(HTCPEN_IRQ);
+}
+
+static int htcpen_isa_probe(struct device *dev, unsigned int id)
+{
+ struct input_dev *htcpen_dev;
+ int err = -EBUSY;
+
+ if (!request_region(HTCPEN_PORT_IRQ_CLEAR, 1, "htcpen")) {
+ printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
+ HTCPEN_PORT_IRQ_CLEAR);
+ goto request_region1_failed;
+ }
+
+ if (!request_region(HTCPEN_PORT_INIT, 1, "htcpen")) {
+ printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
+ HTCPEN_PORT_INIT);
+ goto request_region2_failed;
+ }
+
+ if (!request_region(HTCPEN_PORT_INDEX, 2, "htcpen")) {
+ printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
+ HTCPEN_PORT_INDEX);
+ goto request_region3_failed;
+ }
+
+ htcpen_dev = input_allocate_device();
+ if (!htcpen_dev) {
+ printk(KERN_ERR "htcpen: can't allocate device\n");
+ err = -ENOMEM;
+ goto input_alloc_failed;
+ }
+
+ htcpen_dev->name = "HTC Shift EC TouchScreen";
+ htcpen_dev->id.bustype = BUS_ISA;
+
+ htcpen_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
+ htcpen_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(htcpen_dev, ABS_X, 0, X_AXIS_MAX, 0, 0);
+ input_set_abs_params(htcpen_dev, ABS_Y, 0, Y_AXIS_MAX, 0, 0);
+
+ htcpen_dev->open = htcpen_open;
+ htcpen_dev->close = htcpen_close;
+
+ err = request_irq(HTCPEN_IRQ, htcpen_interrupt, 0, "htcpen",
+ htcpen_dev);
+ if (err) {
+ printk(KERN_ERR "htcpen: irq busy\n");
+ goto request_irq_failed;
+ }
+
+ inb_p(HTCPEN_PORT_IRQ_CLEAR);
+
+ err = input_register_device(htcpen_dev);
+ if (err)
+ goto input_register_failed;
+
+ dev_set_drvdata(dev, htcpen_dev);
+
+ return 0;
+
+ input_register_failed:
+ free_irq(HTCPEN_IRQ, htcpen_dev);
+ request_irq_failed:
+ input_free_device(htcpen_dev);
+ input_alloc_failed:
+ release_region(HTCPEN_PORT_INDEX, 2);
+ request_region3_failed:
+ release_region(HTCPEN_PORT_INIT, 1);
+ request_region2_failed:
+ release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
+ request_region1_failed:
+ return err;
+}
+
+static int htcpen_isa_remove(struct device *dev, unsigned int id)
+{
+ struct input_dev *htcpen_dev = dev_get_drvdata(dev);
+
+ input_unregister_device(htcpen_dev);
+
+ free_irq(HTCPEN_IRQ, htcpen_dev);
+
+ release_region(HTCPEN_PORT_INDEX, 2);
+ release_region(HTCPEN_PORT_INIT, 1);
+ release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int htcpen_isa_suspend(struct device *dev, unsigned int n,
+ pm_message_t state)
+{
+ outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
+
+ return 0;
+}
+
+static int htcpen_isa_resume(struct device *dev, unsigned int n)
+{
+ outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
+
+ return 0;
+}
+#endif
+
+static struct isa_driver htcpen_isa_driver = {
+ .probe = htcpen_isa_probe,
+ .remove = htcpen_isa_remove,
+#ifdef CONFIG_PM
+ .suspend = htcpen_isa_suspend,
+ .resume = htcpen_isa_resume,
+#endif
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "htcpen",
+ }
+};
+
+static struct dmi_system_id htcshift_dmi_table[] __initdata = {
+ {
+ .ident = "Shift",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "High Tech Computer Corp"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Shift"),
+ },
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(dmi, htcshift_dmi_table);
+
+static int __init htcpen_isa_init(void)
+{
+ if (!dmi_check_system(htcshift_dmi_table))
+ return -ENODEV;
+
+ return isa_register_driver(&htcpen_isa_driver, 1);
+}
+
+static void __exit htcpen_isa_exit(void)
+{
+ isa_unregister_driver(&htcpen_isa_driver);
+}
+
+module_init(htcpen_isa_init);
+module_exit(htcpen_isa_exit);
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c
new file mode 100644
index 000000000..da6dc819c
--- /dev/null
+++ b/drivers/input/touchscreen/ili210x.c
@@ -0,0 +1,358 @@
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/input/ili210x.h>
+
+#define MAX_TOUCHES 2
+#define DEFAULT_POLL_PERIOD 20
+
+/* Touchscreen commands */
+#define REG_TOUCHDATA 0x10
+#define REG_PANEL_INFO 0x20
+#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;
+
+struct ili210x {
+ struct i2c_client *client;
+ struct input_dev *input;
+ bool (*get_pendown_state)(void);
+ unsigned int poll_period;
+ struct delayed_work dwork;
+};
+
+static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
+ size_t len)
+{
+ struct i2c_msg msg[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ }
+ };
+
+ if (i2c_transfer(client->adapter, msg, 2) != 2) {
+ dev_err(&client->dev, "i2c transfer failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void ili210x_report_events(struct input_dev *input,
+ const struct touchdata *touchdata)
+{
+ int i;
+ bool touch;
+ unsigned int x, y;
+ const struct finger *finger;
+
+ for (i = 0; i < MAX_TOUCHES; i++) {
+ input_mt_slot(input, i);
+
+ finger = &touchdata->finger[i];
+
+ 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);
+
+ input_report_abs(input, ABS_MT_POSITION_X, x);
+ input_report_abs(input, ABS_MT_POSITION_Y, y);
+ }
+ }
+
+ 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();
+
+ return state;
+}
+
+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;
+
+ 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);
+
+ if ((touchdata.status & 0xf3) || get_pendown_state(priv))
+ schedule_delayed_work(&priv->dwork,
+ msecs_to_jiffies(priv->poll_period));
+}
+
+static irqreturn_t ili210x_irq(int irq, void *irq_data)
+{
+ struct ili210x *priv = irq_data;
+
+ schedule_delayed_work(&priv->dwork, 0);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t ili210x_calibrate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ili210x *priv = i2c_get_clientdata(client);
+ unsigned long calibrate;
+ int rc;
+ u8 cmd = REG_CALIBRATE;
+
+ if (kstrtoul(buf, 10, &calibrate))
+ return -EINVAL;
+
+ if (calibrate > 1)
+ return -EINVAL;
+
+ if (calibrate) {
+ rc = i2c_master_send(priv->client, &cmd, sizeof(cmd));
+ if (rc != sizeof(cmd))
+ return -EIO;
+ }
+
+ return count;
+}
+static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate);
+
+static struct attribute *ili210x_attributes[] = {
+ &dev_attr_calibrate.attr,
+ NULL,
+};
+
+static const struct attribute_group ili210x_attr_group = {
+ .attrs = ili210x_attributes,
+};
+
+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 input_dev *input;
+ struct panel_info panel;
+ struct firmware_version firmware;
+ int xmax, ymax;
+ int error;
+
+ dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
+
+ if (!pdata) {
+ dev_err(dev, "No platform data!\n");
+ return -EINVAL;
+ }
+
+ 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;
+ }
+
+ /* get panel info */
+ error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel));
+ if (error) {
+ dev_err(dev, "Failed to get panel informations, err: %d\n",
+ error);
+ return error;
+ }
+
+ 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 = kzalloc(sizeof(*priv), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!priv || !input) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ priv->client = client;
+ priv->input = input;
+ priv->get_pendown_state = pdata->get_pendown_state;
+ priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD;
+ INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
+
+ /* 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_drvdata(input, priv);
+ i2c_set_clientdata(client, priv);
+
+ error = request_irq(client->irq, ili210x_irq, pdata->irq_flags,
+ client->name, priv);
+ if (error) {
+ dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
+ error);
+ goto err_free_mem;
+ }
+
+ error = sysfs_create_group(&dev->kobj, &ili210x_attr_group);
+ if (error) {
+ dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
+ error);
+ goto err_free_irq;
+ }
+
+ error = input_register_device(priv->input);
+ if (error) {
+ dev_err(dev, "Cannot regiser input device, err: %d\n", error);
+ goto err_remove_sysfs;
+ }
+
+ device_init_wakeup(&client->dev, 1);
+
+ dev_dbg(dev,
+ "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d",
+ 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)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+
+ return 0;
+}
+
+static int __maybe_unused ili210x_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+
+ return 0;
+}
+
+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 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
+
+static struct i2c_driver ili210x_ts_driver = {
+ .driver = {
+ .name = "ili210x_i2c",
+ .owner = THIS_MODULE,
+ .pm = &ili210x_i2c_pm,
+ },
+ .id_table = ili210x_i2c_id,
+ .probe = ili210x_i2c_probe,
+ .remove = ili210x_i2c_remove,
+};
+
+module_i2c_driver(ili210x_ts_driver);
+
+MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>");
+MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/inexio.c b/drivers/input/touchscreen/inexio.c
new file mode 100644
index 000000000..adb80b65a
--- /dev/null
+++ b/drivers/input/touchscreen/inexio.c
@@ -0,0 +1,191 @@
+/*
+ * iNexio serial touchscreen driver
+ *
+ * Copyright (c) 2008 Richard Lemon
+ * Based on the mtouch driver (c) Vojtech Pavlik and Dan Streetman
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2008/06/19 Richard Lemon <richard@codelemon.com>
+ * Copied mtouch.c and edited for iNexio protocol
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "iNexio serial touchscreen driver"
+
+MODULE_AUTHOR("Richard Lemon <richard@codelemon.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define INEXIO_FORMAT_TOUCH_BIT 0x01
+#define INEXIO_FORMAT_LENGTH 5
+#define INEXIO_RESPONSE_BEGIN_BYTE 0x80
+
+/* todo: check specs for max length of all responses */
+#define INEXIO_MAX_LENGTH 16
+
+#define INEXIO_MIN_XC 0
+#define INEXIO_MAX_XC 0x3fff
+#define INEXIO_MIN_YC 0
+#define INEXIO_MAX_YC 0x3fff
+
+#define INEXIO_GET_XC(data) (((data[1])<<7) | data[2])
+#define INEXIO_GET_YC(data) (((data[3])<<7) | data[4])
+#define INEXIO_GET_TOUCHED(data) (INEXIO_FORMAT_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct inexio {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[INEXIO_MAX_LENGTH];
+ char phys[32];
+};
+
+static void inexio_process_data(struct inexio *pinexio)
+{
+ struct input_dev *dev = pinexio->dev;
+
+ if (INEXIO_FORMAT_LENGTH == ++pinexio->idx) {
+ input_report_abs(dev, ABS_X, INEXIO_GET_XC(pinexio->data));
+ input_report_abs(dev, ABS_Y, INEXIO_GET_YC(pinexio->data));
+ input_report_key(dev, BTN_TOUCH, INEXIO_GET_TOUCHED(pinexio->data));
+ input_sync(dev);
+
+ pinexio->idx = 0;
+ }
+}
+
+static irqreturn_t inexio_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct inexio* pinexio = serio_get_drvdata(serio);
+
+ pinexio->data[pinexio->idx] = data;
+
+ if (INEXIO_RESPONSE_BEGIN_BYTE&pinexio->data[0])
+ inexio_process_data(pinexio);
+ else
+ printk(KERN_DEBUG "inexio.c: unknown/unsynchronized data from device, byte %x\n",pinexio->data[0]);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * inexio_disconnect() is the opposite of inexio_connect()
+ */
+
+static void inexio_disconnect(struct serio *serio)
+{
+ struct inexio* pinexio = serio_get_drvdata(serio);
+
+ input_get_device(pinexio->dev);
+ input_unregister_device(pinexio->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(pinexio->dev);
+ kfree(pinexio);
+}
+
+/*
+ * inexio_connect() is the routine that is called when someone adds a
+ * new serio device that supports iNexio protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+
+static int inexio_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct inexio *pinexio;
+ struct input_dev *input_dev;
+ int err;
+
+ pinexio = kzalloc(sizeof(struct inexio), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!pinexio || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ pinexio->serio = serio;
+ pinexio->dev = input_dev;
+ snprintf(pinexio->phys, sizeof(pinexio->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "iNexio Serial TouchScreen";
+ input_dev->phys = pinexio->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_INEXIO;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0001;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(pinexio->dev, ABS_X, INEXIO_MIN_XC, INEXIO_MAX_XC, 0, 0);
+ input_set_abs_params(pinexio->dev, ABS_Y, INEXIO_MIN_YC, INEXIO_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, pinexio);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(pinexio->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(pinexio);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id inexio_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_INEXIO,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, inexio_serio_ids);
+
+static struct serio_driver inexio_drv = {
+ .driver = {
+ .name = "inexio",
+ },
+ .description = DRIVER_DESC,
+ .id_table = inexio_serio_ids,
+ .interrupt = inexio_interrupt,
+ .connect = inexio_connect,
+ .disconnect = inexio_disconnect,
+};
+
+module_serio_driver(inexio_drv);
diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c
new file mode 100644
index 000000000..b4f0725a1
--- /dev/null
+++ b/drivers/input/touchscreen/intel-mid-touch.c
@@ -0,0 +1,654 @@
+/*
+ * Intel MID Resistive Touch Screen Driver
+ *
+ * Copyright (C) 2008 Intel Corp
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com)
+ * Ramesh Agarwal (ramesh.agarwal@intel.com)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * TODO:
+ * review conversion of r/m/w sequences
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/param.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <asm/intel_scu_ipc.h>
+#include <linux/device.h>
+
+/* PMIC Interrupt registers */
+#define PMIC_REG_ID1 0x00 /* PMIC ID1 register */
+
+/* PMIC Interrupt registers */
+#define PMIC_REG_INT 0x04 /* PMIC interrupt register */
+#define PMIC_REG_MINT 0x05 /* PMIC interrupt mask register */
+
+/* ADC Interrupt registers */
+#define PMIC_REG_ADCINT 0x5F /* ADC interrupt register */
+#define PMIC_REG_MADCINT 0x60 /* ADC interrupt mask register */
+
+/* ADC Control registers */
+#define PMIC_REG_ADCCNTL1 0x61 /* ADC control register */
+
+/* ADC Channel Selection registers */
+#define PMICADDR0 0xA4
+#define END_OF_CHANNEL 0x1F
+
+/* ADC Result register */
+#define PMIC_REG_ADCSNS0H 0x64
+
+/* ADC channels for touch screen */
+#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */
+#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */
+#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */
+#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */
+
+/* Touch screen channel BIAS constants */
+#define MRST_XBIAS 0x20
+#define MRST_YBIAS 0x40
+#define MRST_ZBIAS 0x80
+
+/* Touch screen coordinates */
+#define MRST_X_MIN 10
+#define MRST_X_MAX 1024
+#define MRST_X_FUZZ 5
+#define MRST_Y_MIN 10
+#define MRST_Y_MAX 1024
+#define MRST_Y_FUZZ 5
+#define MRST_PRESSURE_MIN 0
+#define MRST_PRESSURE_NOMINAL 50
+#define MRST_PRESSURE_MAX 100
+
+#define WAIT_ADC_COMPLETION 10 /* msec */
+
+/* PMIC ADC round robin delays */
+#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */
+#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */
+
+/* PMIC Vendor Identifiers */
+#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */
+#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */
+#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */
+#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */
+
+/* Touch screen device structure */
+struct mrstouch_dev {
+ struct device *dev; /* device associated with touch screen */
+ struct input_dev *input;
+ char phys[32];
+ u16 asr; /* Address selection register */
+ int irq;
+ unsigned int vendor; /* PMIC vendor */
+ unsigned int rev; /* PMIC revision */
+
+ int (*read_prepare)(struct mrstouch_dev *tsdev);
+ int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z);
+ int (*read_finish)(struct mrstouch_dev *tsdev);
+};
+
+
+/*************************** NEC and Maxim Interface ************************/
+
+static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev)
+{
+ return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20);
+}
+
+/*
+ * Enables PENDET interrupt.
+ */
+static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev)
+{
+ int err;
+
+ err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20);
+ if (!err)
+ err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05);
+
+ return err;
+}
+
+/*
+ * Reads PMIC ADC touch screen result
+ * Reads ADC storage registers for higher 7 and lower 3 bits and
+ * converts the two readings into a single value and turns off gain bit
+ */
+static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm)
+{
+ int err;
+ u16 result;
+ u32 res;
+
+ result = PMIC_REG_ADCSNS0H + offset;
+
+ if (chan == MRST_TS_CHAN12)
+ result += 4;
+
+ err = intel_scu_ipc_ioread32(result, &res);
+ if (err)
+ return err;
+
+ /* Mash the bits up */
+
+ *vp = (res & 0xFF) << 3; /* Highest 7 bits */
+ *vp |= (res >> 8) & 0x07; /* Lower 3 bits */
+ *vp &= 0x3FF;
+
+ res >>= 16;
+
+ *vm = (res & 0xFF) << 3; /* Highest 7 bits */
+ *vm |= (res >> 8) & 0x07; /* Lower 3 bits */
+ *vm &= 0x3FF;
+
+ return 0;
+}
+
+/*
+ * Enables X, Y and Z bias values
+ * Enables YPYM for X channels and XPXM for Y channels
+ */
+static int mrstouch_ts_bias_set(uint offset, uint bias)
+{
+ int count;
+ u16 chan, start;
+ u16 reg[4];
+ u8 data[4];
+
+ chan = PMICADDR0 + offset;
+ start = MRST_TS_CHAN10;
+
+ for (count = 0; count <= 3; count++) {
+ reg[count] = chan++;
+ data[count] = bias | (start + count);
+ }
+
+ return intel_scu_ipc_writev(reg, data, 4);
+}
+
+/* To read touch screen channel values */
+static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev,
+ u16 *x, u16 *y, u16 *z)
+{
+ int err;
+ u16 xm, ym, zm;
+
+ /* configure Y bias for X channels */
+ err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* read x+ and x- channels */
+ err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm);
+ if (err)
+ goto ipc_error;
+
+ /* configure x bias for y channels */
+ err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* read y+ and y- channels */
+ err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym);
+ if (err)
+ goto ipc_error;
+
+ /* configure z bias for x and y channels */
+ err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* read z+ and z- channels */
+ err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm);
+ if (err)
+ goto ipc_error;
+
+ return 0;
+
+ipc_error:
+ dev_err(tsdev->dev, "ipc error during adc read\n");
+ return err;
+}
+
+
+/*************************** Freescale Interface ************************/
+
+static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev)
+{
+ int err, count;
+ u16 chan;
+ u16 reg[5];
+ u8 data[5];
+
+ /* Stop the ADC */
+ err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02);
+ if (err)
+ goto ipc_error;
+
+ chan = PMICADDR0 + tsdev->asr;
+
+ /* Set X BIAS */
+ for (count = 0; count <= 3; count++) {
+ reg[count] = chan++;
+ data[count] = 0x2A;
+ }
+ reg[count] = chan++; /* Dummy */
+ data[count] = 0;
+
+ err = intel_scu_ipc_writev(reg, data, 5);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* Set Y BIAS */
+ for (count = 0; count <= 3; count++) {
+ reg[count] = chan++;
+ data[count] = 0x4A;
+ }
+ reg[count] = chan++; /* Dummy */
+ data[count] = 0;
+
+ err = intel_scu_ipc_writev(reg, data, 5);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* Set Z BIAS */
+ err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ return 0;
+
+ipc_error:
+ dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+ return err;
+}
+
+static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev,
+ u16 *x, u16 *y, u16 *z)
+{
+ int err;
+ u16 result;
+ u16 reg[4];
+ u8 data[4];
+
+ result = PMIC_REG_ADCSNS0H + tsdev->asr;
+
+ reg[0] = result + 4;
+ reg[1] = result + 5;
+ reg[2] = result + 16;
+ reg[3] = result + 17;
+
+ err = intel_scu_ipc_readv(reg, data, 4);
+ if (err)
+ goto ipc_error;
+
+ *x = data[0] << 3; /* Higher 7 bits */
+ *x |= data[1] & 0x7; /* Lower 3 bits */
+ *x &= 0x3FF;
+
+ *y = data[2] << 3; /* Higher 7 bits */
+ *y |= data[3] & 0x7; /* Lower 3 bits */
+ *y &= 0x3FF;
+
+ /* Read Z value */
+ reg[0] = result + 28;
+ reg[1] = result + 29;
+
+ err = intel_scu_ipc_readv(reg, data, 4);
+ if (err)
+ goto ipc_error;
+
+ *z = data[0] << 3; /* Higher 7 bits */
+ *z |= data[1] & 0x7; /* Lower 3 bits */
+ *z &= 0x3FF;
+
+ return 0;
+
+ipc_error:
+ dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+ return err;
+}
+
+static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev)
+{
+ int err, count;
+ u16 chan;
+ u16 reg[5];
+ u8 data[5];
+
+ /* Clear all TS channels */
+ chan = PMICADDR0 + tsdev->asr;
+ for (count = 0; count <= 4; count++) {
+ reg[count] = chan++;
+ data[count] = 0;
+ }
+ err = intel_scu_ipc_writev(reg, data, 5);
+ if (err)
+ goto ipc_error;
+
+ for (count = 0; count <= 4; count++) {
+ reg[count] = chan++;
+ data[count] = 0;
+ }
+ err = intel_scu_ipc_writev(reg, data, 5);
+ if (err)
+ goto ipc_error;
+
+ err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000);
+ if (err)
+ goto ipc_error;
+
+ /* Start ADC */
+ err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02);
+ if (err)
+ goto ipc_error;
+
+ return 0;
+
+ipc_error:
+ dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+ return err;
+}
+
+static void mrstouch_report_event(struct input_dev *input,
+ unsigned int x, unsigned int y, unsigned int z)
+{
+ if (z > MRST_PRESSURE_NOMINAL) {
+ /* Pen touched, report button touch and coordinates */
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ } else {
+ input_report_key(input, BTN_TOUCH, 0);
+ }
+
+ input_report_abs(input, ABS_PRESSURE, z);
+ input_sync(input);
+}
+
+/* PENDET interrupt handler */
+static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id)
+{
+ struct mrstouch_dev *tsdev = dev_id;
+ u16 x, y, z;
+
+ /*
+ * Should we lower thread priority? Probably not, since we are
+ * not spinning but sleeping...
+ */
+
+ if (tsdev->read_prepare(tsdev))
+ goto out;
+
+ do {
+ if (tsdev->read(tsdev, &x, &y, &z))
+ break;
+
+ mrstouch_report_event(tsdev->input, x, y, z);
+ } while (z > MRST_PRESSURE_NOMINAL);
+
+ tsdev->read_finish(tsdev);
+
+out:
+ return IRQ_HANDLED;
+}
+
+/* Utility to read PMIC ID */
+static int mrstouch_read_pmic_id(uint *vendor, uint *rev)
+{
+ int err;
+ u8 r;
+
+ err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r);
+ if (err)
+ return err;
+
+ *vendor = r & 0x7;
+ *rev = (r >> 3) & 0x7;
+
+ return 0;
+}
+
+/*
+ * Parse ADC channels to find end of the channel configured by other ADC user
+ * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels
+ */
+static int mrstouch_chan_parse(struct mrstouch_dev *tsdev)
+{
+ int found = 0;
+ int err, i;
+ u8 r8;
+
+ for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {
+ err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8);
+ if (err)
+ return err;
+
+ if (r8 == END_OF_CHANNEL) {
+ found = i;
+ break;
+ }
+ }
+
+ if (tsdev->vendor == PMIC_VENDOR_FS) {
+ if (found > MRSTOUCH_MAX_CHANNELS - 18)
+ return -ENOSPC;
+ } else {
+ if (found > MRSTOUCH_MAX_CHANNELS - 4)
+ return -ENOSPC;
+ }
+
+ return found;
+}
+
+
+/*
+ * Writes touch screen channels to ADC address selection registers
+ */
+static int mrstouch_ts_chan_set(uint offset)
+{
+ u16 chan;
+
+ int ret, count;
+
+ chan = PMICADDR0 + offset;
+ for (count = 0; count <= 3; count++) {
+ ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count);
+ if (ret)
+ return ret;
+ }
+ return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL);
+}
+
+/* Initialize ADC */
+static int mrstouch_adc_init(struct mrstouch_dev *tsdev)
+{
+ int err, start;
+ u8 ra, rm;
+
+ err = mrstouch_read_pmic_id(&tsdev->vendor, &tsdev->rev);
+ if (err) {
+ dev_err(tsdev->dev, "Unable to read PMIC id\n");
+ return err;
+ }
+
+ switch (tsdev->vendor) {
+ case PMIC_VENDOR_NEC:
+ case PMIC_VENDOR_MAXIM:
+ tsdev->read_prepare = mrstouch_nec_adc_read_prepare;
+ tsdev->read = mrstouch_nec_adc_read;
+ tsdev->read_finish = mrstouch_nec_adc_read_finish;
+ break;
+
+ case PMIC_VENDOR_FS:
+ tsdev->read_prepare = mrstouch_fs_adc_read_prepare;
+ tsdev->read = mrstouch_fs_adc_read;
+ tsdev->read_finish = mrstouch_fs_adc_read_finish;
+ break;
+
+ default:
+ dev_err(tsdev->dev,
+ "Unsupported touchscreen: %d\n", tsdev->vendor);
+ return -ENXIO;
+ }
+
+ start = mrstouch_chan_parse(tsdev);
+ if (start < 0) {
+ dev_err(tsdev->dev, "Unable to parse channels\n");
+ return start;
+ }
+
+ tsdev->asr = start;
+
+ /*
+ * ADC power on, start, enable PENDET and set loop delay
+ * ADC loop delay is set to 4.5 ms approximately
+ * Loop delay more than this results in jitter in adc readings
+ * Setting loop delay to 0 (continuous loop) in MAXIM stops PENDET
+ * interrupt generation sometimes.
+ */
+
+ if (tsdev->vendor == PMIC_VENDOR_FS) {
+ ra = 0xE0 | ADC_LOOP_DELAY0;
+ rm = 0x5;
+ } else {
+ /* NEC and MAXIm not consistent with loop delay 0 */
+ ra = 0xE0 | ADC_LOOP_DELAY1;
+ rm = 0x0;
+
+ /* configure touch screen channels */
+ err = mrstouch_ts_chan_set(tsdev->asr);
+ if (err)
+ return err;
+ }
+
+ err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7);
+ if (err)
+ return err;
+
+ err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+
+/* Probe function for touch screen driver */
+static int mrstouch_probe(struct platform_device *pdev)
+{
+ struct mrstouch_dev *tsdev;
+ struct input_dev *input;
+ int err;
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no interrupt assigned\n");
+ return -EINVAL;
+ }
+
+ tsdev = devm_kzalloc(&pdev->dev, sizeof(struct mrstouch_dev),
+ GFP_KERNEL);
+ if (!tsdev) {
+ dev_err(&pdev->dev, "unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ input = devm_input_allocate_device(&pdev->dev);
+ if (!input) {
+ dev_err(&pdev->dev, "unable to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ tsdev->dev = &pdev->dev;
+ tsdev->input = input;
+ tsdev->irq = irq;
+
+ snprintf(tsdev->phys, sizeof(tsdev->phys),
+ "%s/input0", dev_name(tsdev->dev));
+
+ err = mrstouch_adc_init(tsdev);
+ if (err) {
+ dev_err(&pdev->dev, "ADC initialization failed\n");
+ return err;
+ }
+
+ input->name = "mrst_touchscreen";
+ input->phys = tsdev->phys;
+ input->dev.parent = tsdev->dev;
+
+ input->id.vendor = tsdev->vendor;
+ input->id.version = tsdev->rev;
+
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(tsdev->input, ABS_X,
+ MRST_X_MIN, MRST_X_MAX, MRST_X_FUZZ, 0);
+ input_set_abs_params(tsdev->input, ABS_Y,
+ MRST_Y_MIN, MRST_Y_MAX, MRST_Y_FUZZ, 0);
+ input_set_abs_params(tsdev->input, ABS_PRESSURE,
+ MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0);
+
+ err = devm_request_threaded_irq(&pdev->dev, tsdev->irq, NULL,
+ mrstouch_pendet_irq, IRQF_ONESHOT,
+ "mrstouch", tsdev);
+ if (err) {
+ dev_err(tsdev->dev, "unable to allocate irq\n");
+ return err;
+ }
+
+ err = input_register_device(tsdev->input);
+ if (err) {
+ dev_err(tsdev->dev, "unable to register input device\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static struct platform_driver mrstouch_driver = {
+ .driver = {
+ .name = "pmic_touch",
+ },
+ .probe = mrstouch_probe,
+};
+module_platform_driver(mrstouch_driver);
+
+MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com");
+MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ipaq-micro-ts.c b/drivers/input/touchscreen/ipaq-micro-ts.c
new file mode 100644
index 000000000..33c134820
--- /dev/null
+++ b/drivers/input/touchscreen/ipaq-micro-ts.c
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ *
+ * h3600 atmel micro companion support, touchscreen subdevice
+ * Author : Alessandro Gardich <gremlin@gremlin.it>
+ * Author : Dmitry Artamonow <mad_soft@inbox.ru>
+ * Author : Linus Walleij <linus.walleij@linaro.org>
+ *
+ */
+
+#include <asm/byteorder.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/ipaq-micro.h>
+
+struct touchscreen_data {
+ struct input_dev *input;
+ struct ipaq_micro *micro;
+};
+
+static void micro_ts_receive(void *data, int len, unsigned char *msg)
+{
+ struct touchscreen_data *ts = data;
+
+ if (len == 4) {
+ input_report_abs(ts->input, ABS_X,
+ be16_to_cpup((__be16 *) &msg[2]));
+ input_report_abs(ts->input, ABS_Y,
+ be16_to_cpup((__be16 *) &msg[0]));
+ input_report_key(ts->input, BTN_TOUCH, 1);
+ input_sync(ts->input);
+ } else if (len == 0) {
+ input_report_abs(ts->input, ABS_X, 0);
+ input_report_abs(ts->input, ABS_Y, 0);
+ input_report_key(ts->input, BTN_TOUCH, 0);
+ input_sync(ts->input);
+ }
+}
+
+static void micro_ts_toggle_receive(struct touchscreen_data *ts, bool enable)
+{
+ struct ipaq_micro *micro = ts->micro;
+
+ spin_lock_irq(&micro->lock);
+
+ if (enable) {
+ micro->ts = micro_ts_receive;
+ micro->ts_data = ts;
+ } else {
+ micro->ts = NULL;
+ micro->ts_data = NULL;
+ }
+
+ spin_unlock_irq(&ts->micro->lock);
+}
+
+static int micro_ts_open(struct input_dev *input)
+{
+ struct touchscreen_data *ts = input_get_drvdata(input);
+
+ micro_ts_toggle_receive(ts, true);
+
+ return 0;
+}
+
+static void micro_ts_close(struct input_dev *input)
+{
+ struct touchscreen_data *ts = input_get_drvdata(input);
+
+ micro_ts_toggle_receive(ts, false);
+}
+
+static int micro_ts_probe(struct platform_device *pdev)
+{
+ struct ipaq_micro *micro = dev_get_drvdata(pdev->dev.parent);
+ struct touchscreen_data *ts;
+ int error;
+
+ ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ ts->micro = micro;
+
+ ts->input = devm_input_allocate_device(&pdev->dev);
+ if (!ts->input) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ ts->input->name = "ipaq micro ts";
+ ts->input->open = micro_ts_open;
+ ts->input->close = micro_ts_close;
+
+ input_set_drvdata(ts->input, ts);
+
+ input_set_capability(ts->input, EV_KEY, BTN_TOUCH);
+ input_set_capability(ts->input, EV_ABS, ABS_X);
+ input_set_capability(ts->input, EV_ABS, ABS_Y);
+ input_set_abs_params(ts->input, ABS_X, 0, 1023, 0, 0);
+ input_set_abs_params(ts->input, ABS_Y, 0, 1023, 0, 0);
+
+ error = input_register_device(ts->input);
+ if (error) {
+ dev_err(&pdev->dev, "error registering touch input\n");
+ return error;
+ }
+
+ platform_set_drvdata(pdev, ts);
+
+ dev_info(&pdev->dev, "iPAQ micro touchscreen\n");
+
+ return 0;
+}
+
+static int __maybe_unused micro_ts_suspend(struct device *dev)
+{
+ struct touchscreen_data *ts = dev_get_drvdata(dev);
+
+ micro_ts_toggle_receive(ts, false);
+
+ return 0;
+}
+
+static int __maybe_unused micro_ts_resume(struct device *dev)
+{
+ struct touchscreen_data *ts = dev_get_drvdata(dev);
+ struct input_dev *input = ts->input;
+
+ mutex_lock(&input->mutex);
+
+ if (input->users)
+ micro_ts_toggle_receive(ts, true);
+
+ mutex_unlock(&input->mutex);
+
+ return 0;
+}
+
+static const struct dev_pm_ops micro_ts_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(micro_ts_suspend, micro_ts_resume)
+};
+
+static struct platform_driver micro_ts_device_driver = {
+ .driver = {
+ .name = "ipaq-micro-ts",
+ .pm = &micro_ts_dev_pm_ops,
+ },
+ .probe = micro_ts_probe,
+};
+module_platform_driver(micro_ts_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("driver for iPAQ Atmel micro touchscreen");
+MODULE_ALIAS("platform:ipaq-micro-ts");
diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c
new file mode 100644
index 000000000..ea3b6a5b8
--- /dev/null
+++ b/drivers/input/touchscreen/jornada720_ts.c
@@ -0,0 +1,153 @@
+/*
+ * drivers/input/touchscreen/jornada720_ts.c
+ *
+ * Copyright (C) 2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
+ *
+ * Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski@tefnet.pl>
+ * based on HP Jornada 56x touchscreen driver by Alex Lange <chicken@handhelds.org>
+ *
+ * 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.
+ *
+ * HP Jornada 710/720/729 Touchscreen Driver
+ */
+
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/jornada720.h>
+#include <mach/irqs.h>
+
+MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
+MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver");
+MODULE_LICENSE("GPL v2");
+
+struct jornada_ts {
+ struct input_dev *dev;
+ int x_data[4]; /* X sample values */
+ int y_data[4]; /* Y sample values */
+};
+
+static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts)
+{
+ /* 3 low word X samples */
+ jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
+ jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
+ jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
+
+ /* 3 low word Y samples */
+ jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
+ jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
+ jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
+
+ /* combined x samples bits */
+ jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
+
+ /* combined y samples bits */
+ jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
+}
+
+static int jornada720_ts_average(int coords[4])
+{
+ int coord, high_bits = coords[3];
+
+ coord = coords[0] | ((high_bits & 0x03) << 8);
+ coord += coords[1] | ((high_bits & 0x0c) << 6);
+ coord += coords[2] | ((high_bits & 0x30) << 4);
+
+ return coord / 3;
+}
+
+static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
+ struct input_dev *input = jornada_ts->dev;
+ int x, y;
+
+ /* If GPIO_GPIO9 is set to high then report pen up */
+ if (GPLR & GPIO_GPIO(9)) {
+ input_report_key(input, BTN_TOUCH, 0);
+ input_sync(input);
+ } else {
+ jornada_ssp_start();
+
+ /* proper reply to request is always TXDUMMY */
+ if (jornada_ssp_inout(GETTOUCHSAMPLES) == TXDUMMY) {
+ jornada720_ts_collect_data(jornada_ts);
+
+ x = jornada720_ts_average(jornada_ts->x_data);
+ y = jornada720_ts_average(jornada_ts->y_data);
+
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ input_sync(input);
+ }
+
+ jornada_ssp_end();
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int jornada720_ts_probe(struct platform_device *pdev)
+{
+ struct jornada_ts *jornada_ts;
+ struct input_dev *input_dev;
+ int error;
+
+ jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL);
+ if (!jornada_ts)
+ return -ENOMEM;
+
+ input_dev = devm_input_allocate_device(&pdev->dev);
+ if (!input_dev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, jornada_ts);
+
+ jornada_ts->dev = input_dev;
+
+ input_dev->name = "HP Jornada 7xx Touchscreen";
+ input_dev->phys = "jornadats/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0);
+
+ error = devm_request_irq(&pdev->dev, IRQ_GPIO9,
+ jornada720_ts_interrupt,
+ IRQF_TRIGGER_RISING,
+ "HP7XX Touchscreen driver", pdev);
+ if (error) {
+ dev_err(&pdev->dev, "HP7XX TS : Unable to acquire irq!\n");
+ return error;
+ }
+
+ error = input_register_device(jornada_ts->dev);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:jornada_ts");
+
+static struct platform_driver jornada720_ts_driver = {
+ .probe = jornada720_ts_probe,
+ .driver = {
+ .name = "jornada_ts",
+ },
+};
+module_platform_driver(jornada720_ts_driver);
diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c
new file mode 100644
index 000000000..24d704cd9
--- /dev/null
+++ b/drivers/input/touchscreen/lpc32xx_ts.c
@@ -0,0 +1,408 @@
+/*
+ * LPC32xx built-in touchscreen driver
+ *
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+/*
+ * Touchscreen controller register offsets
+ */
+#define LPC32XX_TSC_STAT 0x00
+#define LPC32XX_TSC_SEL 0x04
+#define LPC32XX_TSC_CON 0x08
+#define LPC32XX_TSC_FIFO 0x0C
+#define LPC32XX_TSC_DTR 0x10
+#define LPC32XX_TSC_RTR 0x14
+#define LPC32XX_TSC_UTR 0x18
+#define LPC32XX_TSC_TTR 0x1C
+#define LPC32XX_TSC_DXP 0x20
+#define LPC32XX_TSC_MIN_X 0x24
+#define LPC32XX_TSC_MAX_X 0x28
+#define LPC32XX_TSC_MIN_Y 0x2C
+#define LPC32XX_TSC_MAX_Y 0x30
+#define LPC32XX_TSC_AUX_UTR 0x34
+#define LPC32XX_TSC_AUX_MIN 0x38
+#define LPC32XX_TSC_AUX_MAX 0x3C
+
+#define LPC32XX_TSC_STAT_FIFO_OVRRN (1 << 8)
+#define LPC32XX_TSC_STAT_FIFO_EMPTY (1 << 7)
+
+#define LPC32XX_TSC_SEL_DEFVAL 0x0284
+
+#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 (0x1 << 11)
+#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s) ((10 - (s)) << 7)
+#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s) ((10 - (s)) << 4)
+#define LPC32XX_TSC_ADCCON_POWER_UP (1 << 2)
+#define LPC32XX_TSC_ADCCON_AUTO_EN (1 << 0)
+
+#define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31)
+#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x) (((x) & 0x03FF0000) >> 16)
+#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y) ((y) & 0x000003FF)
+
+#define LPC32XX_TSC_ADCDAT_VALUE_MASK 0x000003FF
+
+#define LPC32XX_TSC_MIN_XY_VAL 0x0
+#define LPC32XX_TSC_MAX_XY_VAL 0x3FF
+
+#define MOD_NAME "ts-lpc32xx"
+
+#define tsc_readl(dev, reg) \
+ __raw_readl((dev)->tsc_base + (reg))
+#define tsc_writel(dev, reg, val) \
+ __raw_writel((val), (dev)->tsc_base + (reg))
+
+struct lpc32xx_tsc {
+ struct input_dev *dev;
+ void __iomem *tsc_base;
+ int irq;
+ struct clk *clk;
+};
+
+static void lpc32xx_fifo_clear(struct lpc32xx_tsc *tsc)
+{
+ while (!(tsc_readl(tsc, LPC32XX_TSC_STAT) &
+ LPC32XX_TSC_STAT_FIFO_EMPTY))
+ tsc_readl(tsc, LPC32XX_TSC_FIFO);
+}
+
+static irqreturn_t lpc32xx_ts_interrupt(int irq, void *dev_id)
+{
+ u32 tmp, rv[4], xs[4], ys[4];
+ int idx;
+ struct lpc32xx_tsc *tsc = dev_id;
+ struct input_dev *input = tsc->dev;
+
+ tmp = tsc_readl(tsc, LPC32XX_TSC_STAT);
+
+ if (tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) {
+ /* FIFO overflow - throw away samples */
+ lpc32xx_fifo_clear(tsc);
+ return IRQ_HANDLED;
+ }
+
+ /*
+ * Gather and normalize 4 samples. Pen-up events may have less
+ * than 4 samples, but its ok to pop 4 and let the last sample
+ * pen status check drop the samples.
+ */
+ idx = 0;
+ while (idx < 4 &&
+ !(tsc_readl(tsc, LPC32XX_TSC_STAT) &
+ LPC32XX_TSC_STAT_FIFO_EMPTY)) {
+ tmp = tsc_readl(tsc, LPC32XX_TSC_FIFO);
+ xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
+ LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp);
+ ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
+ LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp);
+ rv[idx] = tmp;
+ idx++;
+ }
+
+ /* Data is only valid if pen is still down in last sample */
+ if (!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && idx == 4) {
+ /* Use average of 2nd and 3rd sample for position */
+ input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2);
+ input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2);
+ input_report_key(input, BTN_TOUCH, 1);
+ } else {
+ input_report_key(input, BTN_TOUCH, 0);
+ }
+
+ input_sync(input);
+
+ return IRQ_HANDLED;
+}
+
+static void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc)
+{
+ /* Disable auto mode */
+ tsc_writel(tsc, LPC32XX_TSC_CON,
+ tsc_readl(tsc, LPC32XX_TSC_CON) &
+ ~LPC32XX_TSC_ADCCON_AUTO_EN);
+
+ clk_disable(tsc->clk);
+}
+
+static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc)
+{
+ u32 tmp;
+
+ clk_enable(tsc->clk);
+
+ tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP;
+
+ /* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */
+ tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 |
+ LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) |
+ LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10);
+ tsc_writel(tsc, LPC32XX_TSC_CON, tmp);
+
+ /* These values are all preset */
+ tsc_writel(tsc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL);
+ tsc_writel(tsc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL);
+ tsc_writel(tsc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL);
+ tsc_writel(tsc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL);
+ tsc_writel(tsc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL);
+
+ /* Aux support is not used */
+ tsc_writel(tsc, LPC32XX_TSC_AUX_UTR, 0);
+ tsc_writel(tsc, LPC32XX_TSC_AUX_MIN, 0);
+ tsc_writel(tsc, LPC32XX_TSC_AUX_MAX, 0);
+
+ /*
+ * Set sample rate to about 240Hz per X/Y pair. A single measurement
+ * consists of 4 pairs which gives about a 60Hz sample rate based on
+ * a stable 32768Hz clock source. Values are in clocks.
+ * Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4
+ */
+ tsc_writel(tsc, LPC32XX_TSC_RTR, 0x2);
+ tsc_writel(tsc, LPC32XX_TSC_DTR, 0x2);
+ tsc_writel(tsc, LPC32XX_TSC_TTR, 0x10);
+ tsc_writel(tsc, LPC32XX_TSC_DXP, 0x4);
+ tsc_writel(tsc, LPC32XX_TSC_UTR, 88);
+
+ lpc32xx_fifo_clear(tsc);
+
+ /* Enable automatic ts event capture */
+ tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN);
+}
+
+static int lpc32xx_ts_open(struct input_dev *dev)
+{
+ struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
+
+ lpc32xx_setup_tsc(tsc);
+
+ return 0;
+}
+
+static void lpc32xx_ts_close(struct input_dev *dev)
+{
+ struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
+
+ lpc32xx_stop_tsc(tsc);
+}
+
+static int lpc32xx_ts_probe(struct platform_device *pdev)
+{
+ struct lpc32xx_tsc *tsc;
+ struct input_dev *input;
+ struct resource *res;
+ resource_size_t size;
+ int irq;
+ int error;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Can't get memory resource\n");
+ return -ENOENT;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Can't get interrupt resource\n");
+ return irq;
+ }
+
+ tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!tsc || !input) {
+ dev_err(&pdev->dev, "failed allocating memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ tsc->dev = input;
+ tsc->irq = irq;
+
+ size = resource_size(res);
+
+ if (!request_mem_region(res->start, size, pdev->name)) {
+ dev_err(&pdev->dev, "TSC registers are not free\n");
+ error = -EBUSY;
+ goto err_free_mem;
+ }
+
+ tsc->tsc_base = ioremap(res->start, size);
+ if (!tsc->tsc_base) {
+ dev_err(&pdev->dev, "Can't map memory\n");
+ error = -ENOMEM;
+ goto err_release_mem;
+ }
+
+ tsc->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(tsc->clk)) {
+ dev_err(&pdev->dev, "failed getting clock\n");
+ error = PTR_ERR(tsc->clk);
+ goto err_unmap;
+ }
+
+ input->name = MOD_NAME;
+ input->phys = "lpc32xx/input0";
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0002;
+ input->id.version = 0x0100;
+ input->dev.parent = &pdev->dev;
+ input->open = lpc32xx_ts_open;
+ input->close = lpc32xx_ts_close;
+
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL,
+ LPC32XX_TSC_MAX_XY_VAL, 0, 0);
+ input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL,
+ LPC32XX_TSC_MAX_XY_VAL, 0, 0);
+
+ input_set_drvdata(input, tsc);
+
+ error = request_irq(tsc->irq, lpc32xx_ts_interrupt,
+ 0, pdev->name, tsc);
+ if (error) {
+ dev_err(&pdev->dev, "failed requesting interrupt\n");
+ goto err_put_clock;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&pdev->dev, "failed registering input device\n");
+ goto err_free_irq;
+ }
+
+ platform_set_drvdata(pdev, tsc);
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+
+err_free_irq:
+ free_irq(tsc->irq, tsc);
+err_put_clock:
+ clk_put(tsc->clk);
+err_unmap:
+ iounmap(tsc->tsc_base);
+err_release_mem:
+ release_mem_region(res->start, size);
+err_free_mem:
+ input_free_device(input);
+ kfree(tsc);
+
+ return error;
+}
+
+static int lpc32xx_ts_remove(struct platform_device *pdev)
+{
+ struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ device_init_wakeup(&pdev->dev, 0);
+ free_irq(tsc->irq, tsc);
+
+ input_unregister_device(tsc->dev);
+
+ clk_put(tsc->clk);
+
+ iounmap(tsc->tsc_base);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+
+ kfree(tsc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int lpc32xx_ts_suspend(struct device *dev)
+{
+ struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
+ struct input_dev *input = tsc->dev;
+
+ /*
+ * Suspend and resume can be called when the device hasn't been
+ * enabled. If there are no users that have the device open, then
+ * avoid calling the TSC stop and start functions as the TSC
+ * isn't yet clocked.
+ */
+ mutex_lock(&input->mutex);
+
+ if (input->users) {
+ if (device_may_wakeup(dev))
+ enable_irq_wake(tsc->irq);
+ else
+ lpc32xx_stop_tsc(tsc);
+ }
+
+ mutex_unlock(&input->mutex);
+
+ return 0;
+}
+
+static int lpc32xx_ts_resume(struct device *dev)
+{
+ struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
+ struct input_dev *input = tsc->dev;
+
+ mutex_lock(&input->mutex);
+
+ if (input->users) {
+ if (device_may_wakeup(dev))
+ disable_irq_wake(tsc->irq);
+ else
+ lpc32xx_setup_tsc(tsc);
+ }
+
+ mutex_unlock(&input->mutex);
+
+ return 0;
+}
+
+static const struct dev_pm_ops lpc32xx_ts_pm_ops = {
+ .suspend = lpc32xx_ts_suspend,
+ .resume = lpc32xx_ts_resume,
+};
+#define LPC32XX_TS_PM_OPS (&lpc32xx_ts_pm_ops)
+#else
+#define LPC32XX_TS_PM_OPS NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id lpc32xx_tsc_of_match[] = {
+ { .compatible = "nxp,lpc3220-tsc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_tsc_of_match);
+#endif
+
+static struct platform_driver lpc32xx_ts_driver = {
+ .probe = lpc32xx_ts_probe,
+ .remove = lpc32xx_ts_remove,
+ .driver = {
+ .name = MOD_NAME,
+ .pm = LPC32XX_TS_PM_OPS,
+ .of_match_table = of_match_ptr(lpc32xx_tsc_of_match),
+ },
+};
+module_platform_driver(lpc32xx_ts_driver);
+
+MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com");
+MODULE_DESCRIPTION("LPC32XX TSC Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lpc32xx_ts");
diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c
new file mode 100644
index 000000000..0786010d7
--- /dev/null
+++ b/drivers/input/touchscreen/mainstone-wm97xx.c
@@ -0,0 +1,309 @@
+/*
+ * mainstone-wm97xx.c -- Mainstone Continuous Touch screen driver for
+ * Wolfson WM97xx AC97 Codecs.
+ *
+ * Copyright 2004, 2007 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ * Andrew Zabolotny <zap@homelink.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Notes:
+ * This is a wm97xx extended touch driver to capture touch
+ * data in a continuous manner on the Intel XScale architecture
+ *
+ * Features:
+ * - codecs supported:- WM9705, WM9712, WM9713
+ * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/wm97xx.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+
+#include <mach/regs-ac97.h>
+
+#include <asm/mach-types.h>
+
+struct continuous {
+ u16 id; /* codec id */
+ u8 code; /* continuous code */
+ u8 reads; /* number of coord reads per read cycle */
+ u32 speed; /* number of coords per second */
+};
+
+#define WM_READS(sp) ((sp / HZ) + 1)
+
+static const struct continuous cinfo[] = {
+ {WM9705_ID2, 0, WM_READS(94), 94},
+ {WM9705_ID2, 1, WM_READS(188), 188},
+ {WM9705_ID2, 2, WM_READS(375), 375},
+ {WM9705_ID2, 3, WM_READS(750), 750},
+ {WM9712_ID2, 0, WM_READS(94), 94},
+ {WM9712_ID2, 1, WM_READS(188), 188},
+ {WM9712_ID2, 2, WM_READS(375), 375},
+ {WM9712_ID2, 3, WM_READS(750), 750},
+ {WM9713_ID2, 0, WM_READS(94), 94},
+ {WM9713_ID2, 1, WM_READS(120), 120},
+ {WM9713_ID2, 2, WM_READS(154), 154},
+ {WM9713_ID2, 3, WM_READS(188), 188},
+};
+
+/* continuous speed index */
+static int sp_idx;
+static u16 last, tries;
+static int irq;
+
+/*
+ * Pen sampling frequency (Hz) in continuous mode.
+ */
+static int cont_rate = 200;
+module_param(cont_rate, int, 0);
+MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
+
+/*
+ * Pen down detection.
+ *
+ * This driver can either poll or use an interrupt to indicate a pen down
+ * event. If the irq request fails then it will fall back to polling mode.
+ */
+static int pen_int;
+module_param(pen_int, int, 0);
+MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)");
+
+/*
+ * Pressure readback.
+ *
+ * Set to 1 to read back pen down pressure
+ */
+static int pressure;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
+
+/*
+ * AC97 touch data slot.
+ *
+ * Touch screen readback data ac97 slot
+ */
+static int ac97_touch_slot = 5;
+module_param(ac97_touch_slot, int, 0);
+MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
+
+
+/* flush AC97 slot 5 FIFO on pxa machines */
+#ifdef CONFIG_PXA27x
+static void wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+ schedule_timeout_uninterruptible(1);
+
+ while (MISR & (1 << 2))
+ MODR;
+}
+#else
+static void wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+ unsigned int count;
+
+ schedule_timeout_uninterruptible(1);
+
+ for (count = 0; count < 16; count++)
+ MODR;
+}
+#endif
+
+static int wm97xx_acc_pen_down(struct wm97xx *wm)
+{
+ u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES;
+ int reads = 0;
+
+ /* When the AC97 queue has been drained we need to allow time
+ * to buffer up samples otherwise we end up spinning polling
+ * for samples. The controller can't have a suitably low
+ * threshold set to use the notifications it gives.
+ */
+ schedule_timeout_uninterruptible(1);
+
+ if (tries > 5) {
+ tries = 0;
+ return RC_PENUP;
+ }
+
+ x = MODR;
+ if (x == last) {
+ tries++;
+ return RC_AGAIN;
+ }
+ last = x;
+ do {
+ if (reads)
+ x = MODR;
+ y = MODR;
+ if (pressure)
+ p = MODR;
+
+ dev_dbg(wm->dev, "Raw coordinates: x=%x, y=%x, p=%x\n",
+ x, y, p);
+
+ /* are samples valid */
+ if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X ||
+ (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y ||
+ (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES)
+ goto up;
+
+ /* coordinate is good */
+ tries = 0;
+ input_report_abs(wm->input_dev, ABS_X, x & 0xfff);
+ input_report_abs(wm->input_dev, ABS_Y, y & 0xfff);
+ input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff);
+ input_report_key(wm->input_dev, BTN_TOUCH, (p != 0));
+ input_sync(wm->input_dev);
+ reads++;
+ } while (reads < cinfo[sp_idx].reads);
+up:
+ return RC_PENDOWN | RC_AGAIN;
+}
+
+static int wm97xx_acc_startup(struct wm97xx *wm)
+{
+ int idx = 0, ret = 0;
+
+ /* check we have a codec */
+ if (wm->ac97 == NULL)
+ return -ENODEV;
+
+ /* Go you big red fire engine */
+ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
+ if (wm->id != cinfo[idx].id)
+ continue;
+ sp_idx = idx;
+ if (cont_rate <= cinfo[idx].speed)
+ break;
+ }
+ wm->acc_rate = cinfo[sp_idx].code;
+ wm->acc_slot = ac97_touch_slot;
+ dev_info(wm->dev,
+ "mainstone accelerated touchscreen driver, %d samples/sec\n",
+ cinfo[sp_idx].speed);
+
+ /* IRQ driven touchscreen is used on Palm hardware */
+ if (machine_is_palmt5() || machine_is_palmtx() || machine_is_palmld()) {
+ pen_int = 1;
+ irq = 27;
+ /* There is some obscure mutant of WM9712 interbred with WM9713
+ * used on Palm HW */
+ wm->variant = WM97xx_WM1613;
+ } else if (machine_is_mainstone() && pen_int)
+ irq = 4;
+
+ if (irq) {
+ ret = gpio_request(irq, "Touchscreen IRQ");
+ if (ret)
+ goto out;
+
+ ret = gpio_direction_input(irq);
+ if (ret) {
+ gpio_free(irq);
+ goto out;
+ }
+
+ wm->pen_irq = gpio_to_irq(irq);
+ irq_set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH);
+ } else /* pen irq not supported */
+ pen_int = 0;
+
+ /* codec specific irq config */
+ if (pen_int) {
+ switch (wm->id) {
+ case WM9705_ID2:
+ break;
+ case WM9712_ID2:
+ case WM9713_ID2:
+ /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */
+ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
+ WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_STICKY,
+ WM97XX_GPIO_WAKE);
+ wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT,
+ WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_NOTSTICKY,
+ WM97XX_GPIO_NOWAKE);
+ break;
+ default:
+ dev_err(wm->dev,
+ "pen down irq not supported on this device\n");
+ pen_int = 0;
+ break;
+ }
+ }
+
+out:
+ return ret;
+}
+
+static void wm97xx_acc_shutdown(struct wm97xx *wm)
+{
+ /* codec specific deconfig */
+ if (pen_int) {
+ if (irq)
+ gpio_free(irq);
+ wm->pen_irq = 0;
+ }
+}
+
+static void wm97xx_irq_enable(struct wm97xx *wm, int enable)
+{
+ if (enable)
+ enable_irq(wm->pen_irq);
+ else
+ disable_irq_nosync(wm->pen_irq);
+}
+
+static struct wm97xx_mach_ops mainstone_mach_ops = {
+ .acc_enabled = 1,
+ .acc_pen_up = wm97xx_acc_pen_up,
+ .acc_pen_down = wm97xx_acc_pen_down,
+ .acc_startup = wm97xx_acc_startup,
+ .acc_shutdown = wm97xx_acc_shutdown,
+ .irq_enable = wm97xx_irq_enable,
+ .irq_gpio = WM97XX_GPIO_2,
+};
+
+static int mainstone_wm97xx_probe(struct platform_device *pdev)
+{
+ struct wm97xx *wm = platform_get_drvdata(pdev);
+
+ return wm97xx_register_mach_ops(wm, &mainstone_mach_ops);
+}
+
+static int mainstone_wm97xx_remove(struct platform_device *pdev)
+{
+ struct wm97xx *wm = platform_get_drvdata(pdev);
+
+ wm97xx_unregister_mach_ops(wm);
+ return 0;
+}
+
+static struct platform_driver mainstone_wm97xx_driver = {
+ .probe = mainstone_wm97xx_probe,
+ .remove = mainstone_wm97xx_remove,
+ .driver = {
+ .name = "wm97xx-touch",
+ },
+};
+module_platform_driver(mainstone_wm97xx_driver);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("wm97xx continuous touch driver for mainstone");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c
new file mode 100644
index 000000000..a68ec142e
--- /dev/null
+++ b/drivers/input/touchscreen/max11801_ts.c
@@ -0,0 +1,242 @@
+/*
+ * Driver for MAXI MAX11801 - A Resistive touch screen controller with
+ * i2c interface
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * Author: Zhang Jiejing <jiejing.zhang@freescale.com>
+ *
+ * Based on mcs5000_ts.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * This driver aims to support the series of MAXI touch chips max11801
+ * through max11803. The main difference between these 4 chips can be
+ * found in the table below:
+ * -----------------------------------------------------
+ * | CHIP | AUTO MODE SUPPORT(FIFO) | INTERFACE |
+ * |----------------------------------------------------|
+ * | max11800 | YES | SPI |
+ * | max11801 | YES | I2C |
+ * | max11802 | NO | SPI |
+ * | max11803 | NO | I2C |
+ * ------------------------------------------------------
+ *
+ * Currently, this driver only supports max11801.
+ *
+ * Data Sheet:
+ * http://www.maxim-ic.com/datasheet/index.mvp/id/5943
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+
+/* Register Address define */
+#define GENERNAL_STATUS_REG 0x00
+#define GENERNAL_CONF_REG 0x01
+#define MESURE_RES_CONF_REG 0x02
+#define MESURE_AVER_CONF_REG 0x03
+#define ADC_SAMPLE_TIME_CONF_REG 0x04
+#define PANEL_SETUPTIME_CONF_REG 0x05
+#define DELAY_CONVERSION_CONF_REG 0x06
+#define TOUCH_DETECT_PULLUP_CONF_REG 0x07
+#define AUTO_MODE_TIME_CONF_REG 0x08 /* only for max11800/max11801 */
+#define APERTURE_CONF_REG 0x09 /* only for max11800/max11801 */
+#define AUX_MESURE_CONF_REG 0x0a
+#define OP_MODE_CONF_REG 0x0b
+
+/* FIFO is found only in max11800 and max11801 */
+#define FIFO_RD_CMD (0x50 << 1)
+#define MAX11801_FIFO_INT (1 << 2)
+#define MAX11801_FIFO_OVERFLOW (1 << 3)
+
+#define XY_BUFSIZE 4
+#define XY_BUF_OFFSET 4
+
+#define MAX11801_MAX_X 0xfff
+#define MAX11801_MAX_Y 0xfff
+
+#define MEASURE_TAG_OFFSET 2
+#define MEASURE_TAG_MASK (3 << MEASURE_TAG_OFFSET)
+#define EVENT_TAG_OFFSET 0
+#define EVENT_TAG_MASK (3 << EVENT_TAG_OFFSET)
+#define MEASURE_X_TAG (0 << MEASURE_TAG_OFFSET)
+#define MEASURE_Y_TAG (1 << MEASURE_TAG_OFFSET)
+
+/* These are the state of touch event state machine */
+enum {
+ EVENT_INIT,
+ EVENT_MIDDLE,
+ EVENT_RELEASE,
+ EVENT_FIFO_END
+};
+
+struct max11801_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+};
+
+static u8 read_register(struct i2c_client *client, int addr)
+{
+ /* XXX: The chip ignores LSB of register address */
+ return i2c_smbus_read_byte_data(client, addr << 1);
+}
+
+static int max11801_write_reg(struct i2c_client *client, int addr, int data)
+{
+ /* XXX: The chip ignores LSB of register address */
+ return i2c_smbus_write_byte_data(client, addr << 1, data);
+}
+
+static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id)
+{
+ struct max11801_data *data = dev_id;
+ struct i2c_client *client = data->client;
+ int status, i, ret;
+ u8 buf[XY_BUFSIZE];
+ int x = -1;
+ int y = -1;
+
+ status = read_register(data->client, GENERNAL_STATUS_REG);
+
+ if (status & (MAX11801_FIFO_INT | MAX11801_FIFO_OVERFLOW)) {
+ status = read_register(data->client, GENERNAL_STATUS_REG);
+
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD,
+ XY_BUFSIZE, buf);
+
+ /*
+ * We should get 4 bytes buffer that contains X,Y
+ * and event tag
+ */
+ if (ret < XY_BUFSIZE)
+ goto out;
+
+ for (i = 0; i < XY_BUFSIZE; i += XY_BUFSIZE / 2) {
+ if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_X_TAG)
+ x = (buf[i] << XY_BUF_OFFSET) +
+ (buf[i + 1] >> XY_BUF_OFFSET);
+ else if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_Y_TAG)
+ y = (buf[i] << XY_BUF_OFFSET) +
+ (buf[i + 1] >> XY_BUF_OFFSET);
+ }
+
+ if ((buf[1] & EVENT_TAG_MASK) != (buf[3] & EVENT_TAG_MASK))
+ goto out;
+
+ switch (buf[1] & EVENT_TAG_MASK) {
+ case EVENT_INIT:
+ /* fall through */
+ case EVENT_MIDDLE:
+ input_report_abs(data->input_dev, ABS_X, x);
+ input_report_abs(data->input_dev, ABS_Y, y);
+ input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1);
+ input_sync(data->input_dev);
+ break;
+
+ case EVENT_RELEASE:
+ input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0);
+ input_sync(data->input_dev);
+ break;
+
+ case EVENT_FIFO_END:
+ break;
+ }
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+static void max11801_ts_phy_init(struct max11801_data *data)
+{
+ struct i2c_client *client = data->client;
+
+ /* Average X,Y, take 16 samples, average eight media sample */
+ max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff);
+ /* X,Y panel setup time set to 20us */
+ max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11);
+ /* Rough pullup time (2uS), Fine pullup time (10us) */
+ max11801_write_reg(client, TOUCH_DETECT_PULLUP_CONF_REG, 0x10);
+ /* Auto mode init period = 5ms , scan period = 5ms*/
+ max11801_write_reg(client, AUTO_MODE_TIME_CONF_REG, 0xaa);
+ /* Aperture X,Y set to +- 4LSB */
+ max11801_write_reg(client, APERTURE_CONF_REG, 0x33);
+ /* Enable Power, enable Automode, enable Aperture, enable Average X,Y */
+ max11801_write_reg(client, OP_MODE_CONF_REG, 0x36);
+}
+
+static int max11801_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max11801_data *data;
+ struct input_dev *input_dev;
+ int error;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!data || !input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ data->client = client;
+ data->input_dev = input_dev;
+
+ input_dev->name = "max11801_ts";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_abs_params(input_dev, ABS_X, 0, MAX11801_MAX_X, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX11801_MAX_Y, 0, 0);
+ input_set_drvdata(input_dev, data);
+
+ max11801_ts_phy_init(data);
+
+ error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ max11801_ts_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "max11801_ts", data);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ return error;
+ }
+
+ error = input_register_device(data->input_dev);
+ if (error)
+ return error;
+
+ i2c_set_clientdata(client, data);
+ return 0;
+}
+
+static const struct i2c_device_id max11801_ts_id[] = {
+ {"max11801", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max11801_ts_id);
+
+static struct i2c_driver max11801_ts_driver = {
+ .driver = {
+ .name = "max11801_ts",
+ .owner = THIS_MODULE,
+ },
+ .id_table = max11801_ts_id,
+ .probe = max11801_ts_probe,
+};
+
+module_i2c_driver(max11801_ts_driver);
+
+MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>");
+MODULE_DESCRIPTION("Touchscreen driver for MAXI MAX11801 controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c
new file mode 100644
index 000000000..913e25a99
--- /dev/null
+++ b/drivers/input/touchscreen/mc13783_ts.c
@@ -0,0 +1,255 @@
+/*
+ * Driver for the Freescale Semiconductor MC13783 touchscreen.
+ *
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2009 Sascha Hauer, Pengutronix
+ *
+ * Initial development of this code was funded by
+ * Phytec Messtechnik GmbH, http://www.phytec.de/
+ *
+ * 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/platform_device.h>
+#include <linux/mfd/mc13783.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#define MC13783_TS_NAME "mc13783-ts"
+
+#define DEFAULT_SAMPLE_TOLERANCE 300
+
+static unsigned int sample_tolerance = DEFAULT_SAMPLE_TOLERANCE;
+module_param(sample_tolerance, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(sample_tolerance,
+ "If the minimal and maximal value read out for one axis (out "
+ "of three) differ by this value (default: "
+ __stringify(DEFAULT_SAMPLE_TOLERANCE) ") or more, the reading "
+ "is supposed to be wrong and is discarded. Set to 0 to "
+ "disable this check.");
+
+struct mc13783_ts_priv {
+ struct input_dev *idev;
+ struct mc13xxx *mc13xxx;
+ struct delayed_work work;
+ struct workqueue_struct *workq;
+ unsigned int sample[4];
+ struct mc13xxx_ts_platform_data *touch;
+};
+
+static irqreturn_t mc13783_ts_handler(int irq, void *data)
+{
+ struct mc13783_ts_priv *priv = data;
+
+ mc13xxx_irq_ack(priv->mc13xxx, irq);
+
+ /*
+ * Kick off reading coordinates. Note that if work happens already
+ * be queued for future execution (it rearms itself) it will not
+ * be rescheduled for immediate execution here. However the rearm
+ * delay is HZ / 50 which is acceptable.
+ */
+ queue_delayed_work(priv->workq, &priv->work, 0);
+
+ return IRQ_HANDLED;
+}
+
+#define sort3(a0, a1, a2) ({ \
+ if (a0 > a1) \
+ swap(a0, a1); \
+ if (a1 > a2) \
+ swap(a1, a2); \
+ if (a0 > a1) \
+ swap(a0, a1); \
+ })
+
+static void mc13783_ts_report_sample(struct mc13783_ts_priv *priv)
+{
+ struct input_dev *idev = priv->idev;
+ int x0, x1, x2, y0, y1, y2;
+ int cr0, cr1;
+
+ /*
+ * the values are 10-bit wide only, but the two least significant
+ * bits are for future 12 bit use and reading yields 0
+ */
+ x0 = priv->sample[0] & 0xfff;
+ x1 = priv->sample[1] & 0xfff;
+ x2 = priv->sample[2] & 0xfff;
+ y0 = priv->sample[3] & 0xfff;
+ y1 = (priv->sample[0] >> 12) & 0xfff;
+ y2 = (priv->sample[1] >> 12) & 0xfff;
+ cr0 = (priv->sample[2] >> 12) & 0xfff;
+ cr1 = (priv->sample[3] >> 12) & 0xfff;
+
+ dev_dbg(&idev->dev,
+ "x: (% 4d,% 4d,% 4d) y: (% 4d, % 4d,% 4d) cr: (% 4d, % 4d)\n",
+ x0, x1, x2, y0, y1, y2, cr0, cr1);
+
+ sort3(x0, x1, x2);
+ sort3(y0, y1, y2);
+
+ cr0 = (cr0 + cr1) / 2;
+
+ if (!cr0 || !sample_tolerance ||
+ (x2 - x0 < sample_tolerance &&
+ y2 - y0 < sample_tolerance)) {
+ /* report the median coordinate and average pressure */
+ if (cr0) {
+ input_report_abs(idev, ABS_X, x1);
+ input_report_abs(idev, ABS_Y, y1);
+
+ dev_dbg(&idev->dev, "report (%d, %d, %d)\n",
+ x1, y1, 0x1000 - cr0);
+ queue_delayed_work(priv->workq, &priv->work, HZ / 50);
+ } else
+ dev_dbg(&idev->dev, "report release\n");
+
+ input_report_abs(idev, ABS_PRESSURE,
+ cr0 ? 0x1000 - cr0 : cr0);
+ input_report_key(idev, BTN_TOUCH, cr0);
+ input_sync(idev);
+ } else
+ dev_dbg(&idev->dev, "discard event\n");
+}
+
+static void mc13783_ts_work(struct work_struct *work)
+{
+ struct mc13783_ts_priv *priv =
+ container_of(work, struct mc13783_ts_priv, work.work);
+ unsigned int mode = MC13XXX_ADC_MODE_TS;
+ unsigned int channel = 12;
+
+ if (mc13xxx_adc_do_conversion(priv->mc13xxx,
+ mode, channel,
+ priv->touch->ato, priv->touch->atox,
+ priv->sample) == 0)
+ mc13783_ts_report_sample(priv);
+}
+
+static int mc13783_ts_open(struct input_dev *dev)
+{
+ struct mc13783_ts_priv *priv = input_get_drvdata(dev);
+ int ret;
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TS);
+
+ ret = mc13xxx_irq_request(priv->mc13xxx, MC13XXX_IRQ_TS,
+ mc13783_ts_handler, MC13783_TS_NAME, priv);
+ if (ret)
+ goto out;
+
+ ret = mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0,
+ MC13XXX_ADC0_TSMOD_MASK, MC13XXX_ADC0_TSMOD0);
+ if (ret)
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv);
+out:
+ mc13xxx_unlock(priv->mc13xxx);
+ return ret;
+}
+
+static void mc13783_ts_close(struct input_dev *dev)
+{
+ struct mc13783_ts_priv *priv = input_get_drvdata(dev);
+
+ mc13xxx_lock(priv->mc13xxx);
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0,
+ MC13XXX_ADC0_TSMOD_MASK, 0);
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv);
+ mc13xxx_unlock(priv->mc13xxx);
+
+ cancel_delayed_work_sync(&priv->work);
+}
+
+static int __init mc13783_ts_probe(struct platform_device *pdev)
+{
+ struct mc13783_ts_priv *priv;
+ struct input_dev *idev;
+ int ret = -ENOMEM;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ idev = input_allocate_device();
+ if (!priv || !idev)
+ goto err_free_mem;
+
+ INIT_DELAYED_WORK(&priv->work, mc13783_ts_work);
+ priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
+ priv->idev = idev;
+ priv->touch = dev_get_platdata(&pdev->dev);
+ if (!priv->touch) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ ret = -ENODEV;
+ goto err_free_mem;
+ }
+
+ /*
+ * We need separate workqueue because mc13783_adc_do_conversion
+ * uses keventd and thus would deadlock.
+ */
+ priv->workq = create_singlethread_workqueue("mc13783_ts");
+ if (!priv->workq)
+ goto err_free_mem;
+
+ idev->name = MC13783_TS_NAME;
+ idev->dev.parent = &pdev->dev;
+
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0, 0xfff, 0, 0);
+
+ idev->open = mc13783_ts_open;
+ idev->close = mc13783_ts_close;
+
+ input_set_drvdata(idev, priv);
+
+ ret = input_register_device(priv->idev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "register input device failed with %d\n", ret);
+ goto err_destroy_wq;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ return 0;
+
+err_destroy_wq:
+ destroy_workqueue(priv->workq);
+err_free_mem:
+ input_free_device(idev);
+ kfree(priv);
+ return ret;
+}
+
+static int mc13783_ts_remove(struct platform_device *pdev)
+{
+ struct mc13783_ts_priv *priv = platform_get_drvdata(pdev);
+
+ destroy_workqueue(priv->workq);
+ input_unregister_device(priv->idev);
+ kfree(priv);
+
+ return 0;
+}
+
+static struct platform_driver mc13783_ts_driver = {
+ .remove = mc13783_ts_remove,
+ .driver = {
+ .name = MC13783_TS_NAME,
+ },
+};
+
+module_platform_driver_probe(mc13783_ts_driver, mc13783_ts_probe);
+
+MODULE_DESCRIPTION("MC13783 input touchscreen driver");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" MC13783_TS_NAME);
diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c
new file mode 100644
index 000000000..8b47e1fec
--- /dev/null
+++ b/drivers/input/touchscreen/mcs5000_ts.c
@@ -0,0 +1,294 @@
+/*
+ * mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * Based on wm97xx-core.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c/mcs.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+
+/* Registers */
+#define MCS5000_TS_STATUS 0x00
+#define STATUS_OFFSET 0
+#define STATUS_NO (0 << STATUS_OFFSET)
+#define STATUS_INIT (1 << STATUS_OFFSET)
+#define STATUS_SENSING (2 << STATUS_OFFSET)
+#define STATUS_COORD (3 << STATUS_OFFSET)
+#define STATUS_GESTURE (4 << STATUS_OFFSET)
+#define ERROR_OFFSET 4
+#define ERROR_NO (0 << ERROR_OFFSET)
+#define ERROR_POWER_ON_RESET (1 << ERROR_OFFSET)
+#define ERROR_INT_RESET (2 << ERROR_OFFSET)
+#define ERROR_EXT_RESET (3 << ERROR_OFFSET)
+#define ERROR_INVALID_REG_ADDRESS (8 << ERROR_OFFSET)
+#define ERROR_INVALID_REG_VALUE (9 << ERROR_OFFSET)
+
+#define MCS5000_TS_OP_MODE 0x01
+#define RESET_OFFSET 0
+#define RESET_NO (0 << RESET_OFFSET)
+#define RESET_EXT_SOFT (1 << RESET_OFFSET)
+#define OP_MODE_OFFSET 1
+#define OP_MODE_SLEEP (0 << OP_MODE_OFFSET)
+#define OP_MODE_ACTIVE (1 << OP_MODE_OFFSET)
+#define GESTURE_OFFSET 4
+#define GESTURE_DISABLE (0 << GESTURE_OFFSET)
+#define GESTURE_ENABLE (1 << GESTURE_OFFSET)
+#define PROXIMITY_OFFSET 5
+#define PROXIMITY_DISABLE (0 << PROXIMITY_OFFSET)
+#define PROXIMITY_ENABLE (1 << PROXIMITY_OFFSET)
+#define SCAN_MODE_OFFSET 6
+#define SCAN_MODE_INTERRUPT (0 << SCAN_MODE_OFFSET)
+#define SCAN_MODE_POLLING (1 << SCAN_MODE_OFFSET)
+#define REPORT_RATE_OFFSET 7
+#define REPORT_RATE_40 (0 << REPORT_RATE_OFFSET)
+#define REPORT_RATE_80 (1 << REPORT_RATE_OFFSET)
+
+#define MCS5000_TS_SENS_CTL 0x02
+#define MCS5000_TS_FILTER_CTL 0x03
+#define PRI_FILTER_OFFSET 0
+#define SEC_FILTER_OFFSET 4
+
+#define MCS5000_TS_X_SIZE_UPPER 0x08
+#define MCS5000_TS_X_SIZE_LOWER 0x09
+#define MCS5000_TS_Y_SIZE_UPPER 0x0A
+#define MCS5000_TS_Y_SIZE_LOWER 0x0B
+
+#define MCS5000_TS_INPUT_INFO 0x10
+#define INPUT_TYPE_OFFSET 0
+#define INPUT_TYPE_NONTOUCH (0 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_SINGLE (1 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_DUAL (2 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_PALM (3 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_PROXIMITY (7 << INPUT_TYPE_OFFSET)
+#define GESTURE_CODE_OFFSET 3
+#define GESTURE_CODE_NO (0 << GESTURE_CODE_OFFSET)
+
+#define MCS5000_TS_X_POS_UPPER 0x11
+#define MCS5000_TS_X_POS_LOWER 0x12
+#define MCS5000_TS_Y_POS_UPPER 0x13
+#define MCS5000_TS_Y_POS_LOWER 0x14
+#define MCS5000_TS_Z_POS 0x15
+#define MCS5000_TS_WIDTH 0x16
+#define MCS5000_TS_GESTURE_VAL 0x17
+#define MCS5000_TS_MODULE_REV 0x20
+#define MCS5000_TS_FIRMWARE_VER 0x21
+
+/* Touchscreen absolute values */
+#define MCS5000_MAX_XC 0x3ff
+#define MCS5000_MAX_YC 0x3ff
+
+enum mcs5000_ts_read_offset {
+ READ_INPUT_INFO,
+ READ_X_POS_UPPER,
+ READ_X_POS_LOWER,
+ READ_Y_POS_UPPER,
+ READ_Y_POS_LOWER,
+ READ_BLOCK_SIZE,
+};
+
+/* Each client has this additional data */
+struct mcs5000_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ const struct mcs_platform_data *platform_data;
+};
+
+static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id)
+{
+ struct mcs5000_ts_data *data = dev_id;
+ struct i2c_client *client = data->client;
+ u8 buffer[READ_BLOCK_SIZE];
+ int err;
+ int x;
+ int y;
+
+ err = i2c_smbus_read_i2c_block_data(client, MCS5000_TS_INPUT_INFO,
+ READ_BLOCK_SIZE, buffer);
+ if (err < 0) {
+ dev_err(&client->dev, "%s, err[%d]\n", __func__, err);
+ goto out;
+ }
+
+ switch (buffer[READ_INPUT_INFO]) {
+ case INPUT_TYPE_NONTOUCH:
+ input_report_key(data->input_dev, BTN_TOUCH, 0);
+ input_sync(data->input_dev);
+ break;
+
+ case INPUT_TYPE_SINGLE:
+ x = (buffer[READ_X_POS_UPPER] << 8) | buffer[READ_X_POS_LOWER];
+ y = (buffer[READ_Y_POS_UPPER] << 8) | buffer[READ_Y_POS_LOWER];
+
+ input_report_key(data->input_dev, BTN_TOUCH, 1);
+ input_report_abs(data->input_dev, ABS_X, x);
+ input_report_abs(data->input_dev, ABS_Y, y);
+ input_sync(data->input_dev);
+ break;
+
+ case INPUT_TYPE_DUAL:
+ /* TODO */
+ break;
+
+ case INPUT_TYPE_PALM:
+ /* TODO */
+ break;
+
+ case INPUT_TYPE_PROXIMITY:
+ /* TODO */
+ break;
+
+ default:
+ dev_err(&client->dev, "Unknown ts input type %d\n",
+ buffer[READ_INPUT_INFO]);
+ break;
+ }
+
+ out:
+ return IRQ_HANDLED;
+}
+
+static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data,
+ const struct mcs_platform_data *platform_data)
+{
+ struct i2c_client *client = data->client;
+
+ /* Touch reset & sleep mode */
+ i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE,
+ RESET_EXT_SOFT | OP_MODE_SLEEP);
+
+ /* Touch size */
+ i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER,
+ platform_data->x_size >> 8);
+ i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER,
+ platform_data->x_size & 0xff);
+ i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER,
+ platform_data->y_size >> 8);
+ i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER,
+ platform_data->y_size & 0xff);
+
+ /* Touch active mode & 80 report rate */
+ i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE,
+ OP_MODE_ACTIVE | REPORT_RATE_80);
+}
+
+static int mcs5000_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct mcs_platform_data *pdata;
+ struct mcs5000_ts_data *data;
+ struct input_dev *input_dev;
+ int error;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata)
+ return -EINVAL;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ data->client = client;
+
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!input_dev) {
+ dev_err(&client->dev, "Failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ input_dev->name = "MELFAS MCS-5000 Touchscreen";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0);
+
+ input_set_drvdata(input_dev, data);
+ data->input_dev = input_dev;
+
+ if (pdata->cfg_pin)
+ pdata->cfg_pin();
+
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, mcs5000_ts_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "mcs5000_ts", data);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ return error;
+ }
+
+ error = input_register_device(data->input_dev);
+ if (error) {
+ dev_err(&client->dev, "Failed to register input device\n");
+ return error;
+ }
+
+ mcs5000_ts_phys_init(data, pdata);
+ i2c_set_clientdata(client, data);
+
+ return 0;
+}
+
+static int __maybe_unused mcs5000_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ /* Touch sleep mode */
+ i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP);
+
+ return 0;
+}
+
+static int __maybe_unused mcs5000_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mcs5000_ts_data *data = i2c_get_clientdata(client);
+ const struct mcs_platform_data *pdata = dev_get_platdata(dev);
+
+ mcs5000_ts_phys_init(data, pdata);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume);
+
+static const struct i2c_device_id mcs5000_ts_id[] = {
+ { "mcs5000_ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id);
+
+static struct i2c_driver mcs5000_ts_driver = {
+ .probe = mcs5000_ts_probe,
+ .driver = {
+ .name = "mcs5000_ts",
+ .pm = &mcs5000_ts_pm,
+ },
+ .id_table = mcs5000_ts_id,
+};
+
+module_i2c_driver(mcs5000_ts_driver);
+
+/* Module information */
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/migor_ts.c b/drivers/input/touchscreen/migor_ts.c
new file mode 100644
index 000000000..c038db93e
--- /dev/null
+++ b/drivers/input/touchscreen/migor_ts.c
@@ -0,0 +1,249 @@
+/*
+ * Touch Screen driver for Renesas MIGO-R Platform
+ *
+ * Copyright (c) 2008 Magnus Damm
+ * Copyright (c) 2007 Ujjwal Pande <ujjwal@kenati.com>,
+ * Kenati Technologies Pvt Ltd.
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <linux/i2c.h>
+#include <linux/timer.h>
+
+#define EVENT_PENDOWN 1
+#define EVENT_REPEAT 2
+#define EVENT_PENUP 3
+
+struct migor_ts_priv {
+ struct i2c_client *client;
+ struct input_dev *input;
+ int irq;
+};
+
+static const u_int8_t migor_ts_ena_seq[17] = { 0x33, 0x22, 0x11,
+ 0x01, 0x06, 0x07, };
+static const u_int8_t migor_ts_dis_seq[17] = { };
+
+static irqreturn_t migor_ts_isr(int irq, void *dev_id)
+{
+ struct migor_ts_priv *priv = dev_id;
+ unsigned short xpos, ypos;
+ unsigned char event;
+ u_int8_t buf[16];
+
+ /*
+ * The touch screen controller chip is hooked up to the CPU
+ * using I2C and a single interrupt line. The interrupt line
+ * is pulled low whenever someone taps the screen. To deassert
+ * the interrupt line we need to acknowledge the interrupt by
+ * communicating with the controller over the slow i2c bus.
+ *
+ * Since I2C bus controller may sleep we are using threaded
+ * IRQ here.
+ */
+
+ memset(buf, 0, sizeof(buf));
+
+ /* Set Index 0 */
+ buf[0] = 0;
+ if (i2c_master_send(priv->client, buf, 1) != 1) {
+ dev_err(&priv->client->dev, "Unable to write i2c index\n");
+ goto out;
+ }
+
+ /* Now do Page Read */
+ if (i2c_master_recv(priv->client, buf, sizeof(buf)) != sizeof(buf)) {
+ dev_err(&priv->client->dev, "Unable to read i2c page\n");
+ goto out;
+ }
+
+ ypos = ((buf[9] & 0x03) << 8 | buf[8]);
+ xpos = ((buf[11] & 0x03) << 8 | buf[10]);
+ event = buf[12];
+
+ switch (event) {
+ case EVENT_PENDOWN:
+ case EVENT_REPEAT:
+ input_report_key(priv->input, BTN_TOUCH, 1);
+ input_report_abs(priv->input, ABS_X, ypos); /*X-Y swap*/
+ input_report_abs(priv->input, ABS_Y, xpos);
+ input_sync(priv->input);
+ break;
+
+ case EVENT_PENUP:
+ input_report_key(priv->input, BTN_TOUCH, 0);
+ input_sync(priv->input);
+ break;
+ }
+
+ out:
+ return IRQ_HANDLED;
+}
+
+static int migor_ts_open(struct input_dev *dev)
+{
+ struct migor_ts_priv *priv = input_get_drvdata(dev);
+ struct i2c_client *client = priv->client;
+ int count;
+
+ /* enable controller */
+ count = i2c_master_send(client, migor_ts_ena_seq,
+ sizeof(migor_ts_ena_seq));
+ if (count != sizeof(migor_ts_ena_seq)) {
+ dev_err(&client->dev, "Unable to enable touchscreen.\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static void migor_ts_close(struct input_dev *dev)
+{
+ struct migor_ts_priv *priv = input_get_drvdata(dev);
+ struct i2c_client *client = priv->client;
+
+ disable_irq(priv->irq);
+
+ /* disable controller */
+ i2c_master_send(client, migor_ts_dis_seq, sizeof(migor_ts_dis_seq));
+
+ enable_irq(priv->irq);
+}
+
+static int migor_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *idp)
+{
+ struct migor_ts_priv *priv;
+ struct input_dev *input;
+ int error;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!priv || !input) {
+ dev_err(&client->dev, "failed to allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ priv->client = client;
+ priv->input = input;
+ priv->irq = client->irq;
+
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ __set_bit(BTN_TOUCH, input->keybit);
+
+ input_set_abs_params(input, ABS_X, 95, 955, 0, 0);
+ input_set_abs_params(input, ABS_Y, 85, 935, 0, 0);
+
+ input->name = client->name;
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &client->dev;
+
+ input->open = migor_ts_open;
+ input->close = migor_ts_close;
+
+ input_set_drvdata(input, priv);
+
+ error = request_threaded_irq(priv->irq, NULL, migor_ts_isr,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ client->name, priv);
+ if (error) {
+ dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+ goto err_free_mem;
+ }
+
+ error = input_register_device(input);
+ if (error)
+ goto err_free_irq;
+
+ i2c_set_clientdata(client, priv);
+ device_init_wakeup(&client->dev, 1);
+
+ return 0;
+
+ err_free_irq:
+ free_irq(priv->irq, priv);
+ err_free_mem:
+ input_free_device(input);
+ kfree(priv);
+ return error;
+}
+
+static int migor_ts_remove(struct i2c_client *client)
+{
+ struct migor_ts_priv *priv = i2c_get_clientdata(client);
+
+ free_irq(priv->irq, priv);
+ input_unregister_device(priv->input);
+ kfree(priv);
+
+ dev_set_drvdata(&client->dev, NULL);
+
+ return 0;
+}
+
+static int migor_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct migor_ts_priv *priv = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(priv->irq);
+
+ return 0;
+}
+
+static int migor_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct migor_ts_priv *priv = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(priv->irq);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(migor_ts_pm, migor_ts_suspend, migor_ts_resume);
+
+static const struct i2c_device_id migor_ts_id[] = {
+ { "migor_ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, migor_ts);
+
+static struct i2c_driver migor_ts_driver = {
+ .driver = {
+ .name = "migor_ts",
+ .pm = &migor_ts_pm,
+ },
+ .probe = migor_ts_probe,
+ .remove = migor_ts_remove,
+ .id_table = migor_ts_id,
+};
+
+module_i2c_driver(migor_ts_driver);
+
+MODULE_DESCRIPTION("MigoR Touchscreen driver");
+MODULE_AUTHOR("Magnus Damm <damm@opensource.se>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c
new file mode 100644
index 000000000..36e57deac
--- /dev/null
+++ b/drivers/input/touchscreen/mk712.c
@@ -0,0 +1,219 @@
+/*
+ * ICS MK712 touchscreen controller driver
+ *
+ * Copyright (c) 1999-2002 Transmeta Corporation
+ * Copyright (c) 2005 Rick Koch <n1gp@hotmail.com>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * This driver supports the ICS MicroClock MK712 TouchScreen controller,
+ * found in Gateway AOL Connected Touchpad computers.
+ *
+ * Documentation for ICS MK712 can be found at:
+ * http://www.idt.com/products/getDoc.cfm?docID=18713923
+ */
+
+/*
+ * 1999-12-18: original version, Daniel Quinlan
+ * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll
+ * to use queue_empty, Nathan Laredo
+ * 1999-12-20: improved random point rejection, Nathan Laredo
+ * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed
+ * queue code, added module options, other fixes, Daniel Quinlan
+ * 2002-03-15: Clean up for kernel merge <alan@redhat.com>
+ * Fixed multi open race, fixed memory checks, fixed resource
+ * allocation, fixed close/powerdown bug, switched to new init
+ * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch
+ * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Daniel Quinlan <quinlan@pathname.com>, Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver");
+MODULE_LICENSE("GPL");
+
+static unsigned int mk712_io = 0x260; /* Also 0x200, 0x208, 0x300 */
+module_param_named(io, mk712_io, uint, 0);
+MODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller");
+
+static unsigned int mk712_irq = 10; /* Also 12, 14, 15 */
+module_param_named(irq, mk712_irq, uint, 0);
+MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller");
+
+/* eight 8-bit registers */
+#define MK712_STATUS 0
+#define MK712_X 2
+#define MK712_Y 4
+#define MK712_CONTROL 6
+#define MK712_RATE 7
+
+/* status */
+#define MK712_STATUS_TOUCH 0x10
+#define MK712_CONVERSION_COMPLETE 0x80
+
+/* control */
+#define MK712_ENABLE_INT 0x01
+#define MK712_INT_ON_CONVERSION_COMPLETE 0x02
+#define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS 0x04
+#define MK712_ENABLE_PERIODIC_CONVERSIONS 0x10
+#define MK712_READ_ONE_POINT 0x20
+#define MK712_POWERUP 0x40
+
+static struct input_dev *mk712_dev;
+static DEFINE_SPINLOCK(mk712_lock);
+
+static irqreturn_t mk712_interrupt(int irq, void *dev_id)
+{
+ unsigned char status;
+ static int debounce = 1;
+ static unsigned short last_x;
+ static unsigned short last_y;
+
+ spin_lock(&mk712_lock);
+
+ status = inb(mk712_io + MK712_STATUS);
+
+ if (~status & MK712_CONVERSION_COMPLETE) {
+ debounce = 1;
+ goto end;
+ }
+
+ if (~status & MK712_STATUS_TOUCH) {
+ debounce = 1;
+ input_report_key(mk712_dev, BTN_TOUCH, 0);
+ goto end;
+ }
+
+ if (debounce) {
+ debounce = 0;
+ goto end;
+ }
+
+ input_report_key(mk712_dev, BTN_TOUCH, 1);
+ input_report_abs(mk712_dev, ABS_X, last_x);
+ input_report_abs(mk712_dev, ABS_Y, last_y);
+
+ end:
+ last_x = inw(mk712_io + MK712_X) & 0x0fff;
+ last_y = inw(mk712_io + MK712_Y) & 0x0fff;
+ input_sync(mk712_dev);
+ spin_unlock(&mk712_lock);
+ return IRQ_HANDLED;
+}
+
+static int mk712_open(struct input_dev *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mk712_lock, flags);
+
+ outb(0, mk712_io + MK712_CONTROL); /* Reset */
+
+ outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE |
+ MK712_INT_ON_CHANGE_IN_TOUCH_STATUS |
+ MK712_ENABLE_PERIODIC_CONVERSIONS |
+ MK712_POWERUP, mk712_io + MK712_CONTROL);
+
+ outb(10, mk712_io + MK712_RATE); /* 187 points per second */
+
+ spin_unlock_irqrestore(&mk712_lock, flags);
+
+ return 0;
+}
+
+static void mk712_close(struct input_dev *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mk712_lock, flags);
+
+ outb(0, mk712_io + MK712_CONTROL);
+
+ spin_unlock_irqrestore(&mk712_lock, flags);
+}
+
+static int __init mk712_init(void)
+{
+ int err;
+
+ if (!request_region(mk712_io, 8, "mk712")) {
+ printk(KERN_WARNING "mk712: unable to get IO region\n");
+ return -ENODEV;
+ }
+
+ outb(0, mk712_io + MK712_CONTROL);
+
+ if ((inw(mk712_io + MK712_X) & 0xf000) || /* Sanity check */
+ (inw(mk712_io + MK712_Y) & 0xf000) ||
+ (inw(mk712_io + MK712_STATUS) & 0xf333)) {
+ printk(KERN_WARNING "mk712: device not present\n");
+ err = -ENODEV;
+ goto fail1;
+ }
+
+ mk712_dev = input_allocate_device();
+ if (!mk712_dev) {
+ printk(KERN_ERR "mk712: not enough memory\n");
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ mk712_dev->name = "ICS MicroClock MK712 TouchScreen";
+ mk712_dev->phys = "isa0260/input0";
+ mk712_dev->id.bustype = BUS_ISA;
+ mk712_dev->id.vendor = 0x0005;
+ mk712_dev->id.product = 0x0001;
+ mk712_dev->id.version = 0x0100;
+
+ mk712_dev->open = mk712_open;
+ mk712_dev->close = mk712_close;
+
+ mk712_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ mk712_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(mk712_dev, ABS_X, 0, 0xfff, 88, 0);
+ input_set_abs_params(mk712_dev, ABS_Y, 0, 0xfff, 88, 0);
+
+ if (request_irq(mk712_irq, mk712_interrupt, 0, "mk712", mk712_dev)) {
+ printk(KERN_WARNING "mk712: unable to get IRQ\n");
+ err = -EBUSY;
+ goto fail1;
+ }
+
+ err = input_register_device(mk712_dev);
+ if (err)
+ goto fail2;
+
+ return 0;
+
+ fail2: free_irq(mk712_irq, mk712_dev);
+ fail1: input_free_device(mk712_dev);
+ release_region(mk712_io, 8);
+ return err;
+}
+
+static void __exit mk712_exit(void)
+{
+ input_unregister_device(mk712_dev);
+ free_irq(mk712_irq, mk712_dev);
+ release_region(mk712_io, 8);
+}
+
+module_init(mk712_init);
+module_exit(mk712_exit);
diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c
new file mode 100644
index 000000000..67c0d3161
--- /dev/null
+++ b/drivers/input/touchscreen/mms114.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * 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/module.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/i2c/mms114.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+/* Write only registers */
+#define MMS114_MODE_CONTROL 0x01
+#define MMS114_OPERATION_MODE_MASK 0xE
+#define MMS114_ACTIVE (1 << 1)
+
+#define MMS114_XY_RESOLUTION_H 0x02
+#define MMS114_X_RESOLUTION 0x03
+#define MMS114_Y_RESOLUTION 0x04
+#define MMS114_CONTACT_THRESHOLD 0x05
+#define MMS114_MOVING_THRESHOLD 0x06
+
+/* Read only registers */
+#define MMS114_PACKET_SIZE 0x0F
+#define MMS114_INFOMATION 0x10
+#define MMS114_TSP_REV 0xF0
+
+/* Minimum delay time is 50us between stop and start signal of i2c */
+#define MMS114_I2C_DELAY 50
+
+/* 200ms needs after power on */
+#define MMS114_POWERON_DELAY 200
+
+/* Touchscreen absolute values */
+#define MMS114_MAX_AREA 0xff
+
+#define MMS114_MAX_TOUCH 10
+#define MMS114_PACKET_NUM 8
+
+/* Touch type */
+#define MMS114_TYPE_NONE 0
+#define MMS114_TYPE_TOUCHSCREEN 1
+#define MMS114_TYPE_TOUCHKEY 2
+
+struct mms114_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct regulator *core_reg;
+ struct regulator *io_reg;
+ const struct mms114_platform_data *pdata;
+
+ /* Use cache data for mode control register(write only) */
+ u8 cache_mode_control;
+};
+
+struct mms114_touch {
+ u8 id:4, reserved_bit4:1, type:2, pressed:1;
+ u8 x_hi:4, y_hi:4;
+ u8 x_lo;
+ u8 y_lo;
+ u8 width;
+ u8 strength;
+ u8 reserved[2];
+} __packed;
+
+static int __mms114_read_reg(struct mms114_data *data, unsigned int reg,
+ unsigned int len, u8 *val)
+{
+ struct i2c_client *client = data->client;
+ struct i2c_msg xfer[2];
+ u8 buf = reg & 0xff;
+ int error;
+
+ if (reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL)
+ BUG();
+
+ /* Write register: use repeated start */
+ xfer[0].addr = client->addr;
+ xfer[0].flags = I2C_M_TEN | I2C_M_NOSTART;
+ xfer[0].len = 1;
+ xfer[0].buf = &buf;
+
+ /* Read data */
+ xfer[1].addr = client->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = len;
+ xfer[1].buf = val;
+
+ error = i2c_transfer(client->adapter, xfer, 2);
+ if (error != 2) {
+ dev_err(&client->dev,
+ "%s: i2c transfer failed (%d)\n", __func__, error);
+ return error < 0 ? error : -EIO;
+ }
+ udelay(MMS114_I2C_DELAY);
+
+ return 0;
+}
+
+static int mms114_read_reg(struct mms114_data *data, unsigned int reg)
+{
+ u8 val;
+ int error;
+
+ if (reg == MMS114_MODE_CONTROL)
+ return data->cache_mode_control;
+
+ error = __mms114_read_reg(data, reg, 1, &val);
+ return error < 0 ? error : val;
+}
+
+static int mms114_write_reg(struct mms114_data *data, unsigned int reg,
+ unsigned int val)
+{
+ struct i2c_client *client = data->client;
+ u8 buf[2];
+ int error;
+
+ buf[0] = reg & 0xff;
+ buf[1] = val & 0xff;
+
+ error = i2c_master_send(client, buf, 2);
+ if (error != 2) {
+ dev_err(&client->dev,
+ "%s: i2c send failed (%d)\n", __func__, error);
+ return error < 0 ? error : -EIO;
+ }
+ udelay(MMS114_I2C_DELAY);
+
+ if (reg == MMS114_MODE_CONTROL)
+ data->cache_mode_control = val;
+
+ return 0;
+}
+
+static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *touch)
+{
+ const struct mms114_platform_data *pdata = data->pdata;
+ struct i2c_client *client = data->client;
+ struct input_dev *input_dev = data->input_dev;
+ unsigned int id;
+ unsigned int x;
+ unsigned int y;
+
+ if (touch->id > MMS114_MAX_TOUCH) {
+ dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id);
+ return;
+ }
+
+ if (touch->type != MMS114_TYPE_TOUCHSCREEN) {
+ dev_err(&client->dev, "Wrong touch type (%d)\n", touch->type);
+ return;
+ }
+
+ id = touch->id - 1;
+ x = touch->x_lo | touch->x_hi << 8;
+ y = touch->y_lo | touch->y_hi << 8;
+ if (x > pdata->x_size || y > pdata->y_size) {
+ dev_dbg(&client->dev,
+ "Wrong touch coordinates (%d, %d)\n", x, y);
+ return;
+ }
+
+ if (pdata->x_invert)
+ x = pdata->x_size - x;
+ if (pdata->y_invert)
+ y = pdata->y_size - y;
+
+ dev_dbg(&client->dev,
+ "id: %d, type: %d, pressed: %d, x: %d, y: %d, width: %d, strength: %d\n",
+ id, touch->type, touch->pressed,
+ x, y, touch->width, touch->strength);
+
+ input_mt_slot(input_dev, id);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, touch->pressed);
+
+ if (touch->pressed) {
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, touch->width);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, touch->strength);
+ }
+}
+
+static irqreturn_t mms114_interrupt(int irq, void *dev_id)
+{
+ struct mms114_data *data = dev_id;
+ struct input_dev *input_dev = data->input_dev;
+ struct mms114_touch touch[MMS114_MAX_TOUCH];
+ int packet_size;
+ int touch_size;
+ int index;
+ int error;
+
+ mutex_lock(&input_dev->mutex);
+ if (!input_dev->users) {
+ mutex_unlock(&input_dev->mutex);
+ goto out;
+ }
+ mutex_unlock(&input_dev->mutex);
+
+ packet_size = mms114_read_reg(data, MMS114_PACKET_SIZE);
+ if (packet_size <= 0)
+ goto out;
+
+ touch_size = packet_size / MMS114_PACKET_NUM;
+
+ error = __mms114_read_reg(data, MMS114_INFOMATION, packet_size,
+ (u8 *)touch);
+ if (error < 0)
+ goto out;
+
+ for (index = 0; index < touch_size; index++)
+ mms114_process_mt(data, touch + index);
+
+ input_mt_report_pointer_emulation(data->input_dev, true);
+ input_sync(data->input_dev);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int mms114_set_active(struct mms114_data *data, bool active)
+{
+ int val;
+
+ val = mms114_read_reg(data, MMS114_MODE_CONTROL);
+ if (val < 0)
+ return val;
+
+ val &= ~MMS114_OPERATION_MODE_MASK;
+
+ /* If active is false, sleep mode */
+ if (active)
+ val |= MMS114_ACTIVE;
+
+ return mms114_write_reg(data, MMS114_MODE_CONTROL, val);
+}
+
+static int mms114_get_version(struct mms114_data *data)
+{
+ struct device *dev = &data->client->dev;
+ u8 buf[6];
+ int error;
+
+ error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf);
+ if (error < 0)
+ return error;
+
+ dev_info(dev, "TSP Rev: 0x%x, HW Rev: 0x%x, Firmware Ver: 0x%x\n",
+ buf[0], buf[1], buf[3]);
+
+ return 0;
+}
+
+static int mms114_setup_regs(struct mms114_data *data)
+{
+ const struct mms114_platform_data *pdata = data->pdata;
+ int val;
+ int error;
+
+ error = mms114_get_version(data);
+ if (error < 0)
+ return error;
+
+ error = mms114_set_active(data, true);
+ if (error < 0)
+ return error;
+
+ val = (pdata->x_size >> 8) & 0xf;
+ val |= ((pdata->y_size >> 8) & 0xf) << 4;
+ error = mms114_write_reg(data, MMS114_XY_RESOLUTION_H, val);
+ if (error < 0)
+ return error;
+
+ val = pdata->x_size & 0xff;
+ error = mms114_write_reg(data, MMS114_X_RESOLUTION, val);
+ if (error < 0)
+ return error;
+
+ val = pdata->y_size & 0xff;
+ error = mms114_write_reg(data, MMS114_Y_RESOLUTION, val);
+ if (error < 0)
+ return error;
+
+ if (pdata->contact_threshold) {
+ error = mms114_write_reg(data, MMS114_CONTACT_THRESHOLD,
+ pdata->contact_threshold);
+ if (error < 0)
+ return error;
+ }
+
+ if (pdata->moving_threshold) {
+ error = mms114_write_reg(data, MMS114_MOVING_THRESHOLD,
+ pdata->moving_threshold);
+ if (error < 0)
+ return error;
+ }
+
+ return 0;
+}
+
+static int mms114_start(struct mms114_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+
+ error = regulator_enable(data->core_reg);
+ if (error) {
+ dev_err(&client->dev, "Failed to enable avdd: %d\n", error);
+ return error;
+ }
+
+ error = regulator_enable(data->io_reg);
+ if (error) {
+ dev_err(&client->dev, "Failed to enable vdd: %d\n", error);
+ regulator_disable(data->core_reg);
+ return error;
+ }
+
+ mdelay(MMS114_POWERON_DELAY);
+
+ error = mms114_setup_regs(data);
+ if (error < 0) {
+ regulator_disable(data->io_reg);
+ regulator_disable(data->core_reg);
+ return error;
+ }
+
+ if (data->pdata->cfg_pin)
+ data->pdata->cfg_pin(true);
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static void mms114_stop(struct mms114_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+
+ disable_irq(client->irq);
+
+ if (data->pdata->cfg_pin)
+ data->pdata->cfg_pin(false);
+
+ error = regulator_disable(data->io_reg);
+ if (error)
+ dev_warn(&client->dev, "Failed to disable vdd: %d\n", error);
+
+ error = regulator_disable(data->core_reg);
+ if (error)
+ dev_warn(&client->dev, "Failed to disable avdd: %d\n", error);
+}
+
+static int mms114_input_open(struct input_dev *dev)
+{
+ struct mms114_data *data = input_get_drvdata(dev);
+
+ return mms114_start(data);
+}
+
+static void mms114_input_close(struct input_dev *dev)
+{
+ struct mms114_data *data = input_get_drvdata(dev);
+
+ mms114_stop(data);
+}
+
+#ifdef CONFIG_OF
+static struct mms114_platform_data *mms114_parse_dt(struct device *dev)
+{
+ struct mms114_platform_data *pdata;
+ struct device_node *np = dev->of_node;
+
+ if (!np)
+ return NULL;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "failed to allocate platform data\n");
+ return NULL;
+ }
+
+ if (of_property_read_u32(np, "x-size", &pdata->x_size)) {
+ dev_err(dev, "failed to get x-size property\n");
+ return NULL;
+ };
+
+ if (of_property_read_u32(np, "y-size", &pdata->y_size)) {
+ dev_err(dev, "failed to get y-size property\n");
+ return NULL;
+ };
+
+ of_property_read_u32(np, "contact-threshold",
+ &pdata->contact_threshold);
+ of_property_read_u32(np, "moving-threshold",
+ &pdata->moving_threshold);
+
+ if (of_find_property(np, "x-invert", NULL))
+ pdata->x_invert = true;
+ if (of_find_property(np, "y-invert", NULL))
+ pdata->y_invert = true;
+
+ return pdata;
+}
+#else
+static inline struct mms114_platform_data *mms114_parse_dt(struct device *dev)
+{
+ return NULL;
+}
+#endif
+
+static int mms114_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct mms114_platform_data *pdata;
+ struct mms114_data *data;
+ struct input_dev *input_dev;
+ int error;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata)
+ pdata = mms114_parse_dt(&client->dev);
+
+ if (!pdata) {
+ dev_err(&client->dev, "Need platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_PROTOCOL_MANGLING)) {
+ dev_err(&client->dev,
+ "Need i2c bus that supports protocol mangling\n");
+ return -ENODEV;
+ }
+
+ data = devm_kzalloc(&client->dev, sizeof(struct mms114_data),
+ GFP_KERNEL);
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!data || !input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ data->client = client;
+ data->input_dev = input_dev;
+ data->pdata = pdata;
+
+ input_dev->name = "MELFAS MMS114 Touchscreen";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+ input_dev->open = mms114_input_open;
+ input_dev->close = mms114_input_close;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_abs_params(input_dev, ABS_X, 0, data->pdata->x_size, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0);
+
+ /* For multi touch */
+ input_mt_init_slots(input_dev, MMS114_MAX_TOUCH, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, MMS114_MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, data->pdata->x_size, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, data->pdata->y_size, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+ input_set_drvdata(input_dev, data);
+ i2c_set_clientdata(client, data);
+
+ data->core_reg = devm_regulator_get(&client->dev, "avdd");
+ if (IS_ERR(data->core_reg)) {
+ error = PTR_ERR(data->core_reg);
+ dev_err(&client->dev,
+ "Unable to get the Core regulator (%d)\n", error);
+ return error;
+ }
+
+ data->io_reg = devm_regulator_get(&client->dev, "vdd");
+ if (IS_ERR(data->io_reg)) {
+ error = PTR_ERR(data->io_reg);
+ dev_err(&client->dev,
+ "Unable to get the IO regulator (%d)\n", error);
+ return error;
+ }
+
+ error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ mms114_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(&client->dev), data);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ return error;
+ }
+ disable_irq(client->irq);
+
+ error = input_register_device(data->input_dev);
+ if (error) {
+ dev_err(&client->dev, "Failed to register input device\n");
+ return error;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused mms114_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mms114_data *data = i2c_get_clientdata(client);
+ struct input_dev *input_dev = data->input_dev;
+ int id;
+
+ /* Release all touch */
+ for (id = 0; id < MMS114_MAX_TOUCH; id++) {
+ input_mt_slot(input_dev, id);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
+ }
+
+ input_mt_report_pointer_emulation(input_dev, true);
+ input_sync(input_dev);
+
+ mutex_lock(&input_dev->mutex);
+ if (input_dev->users)
+ mms114_stop(data);
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static int __maybe_unused mms114_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mms114_data *data = i2c_get_clientdata(client);
+ struct input_dev *input_dev = data->input_dev;
+ int error;
+
+ mutex_lock(&input_dev->mutex);
+ if (input_dev->users) {
+ error = mms114_start(data);
+ if (error < 0) {
+ mutex_unlock(&input_dev->mutex);
+ return error;
+ }
+ }
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mms114_pm_ops, mms114_suspend, mms114_resume);
+
+static const struct i2c_device_id mms114_id[] = {
+ { "mms114", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mms114_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id mms114_dt_match[] = {
+ { .compatible = "melfas,mms114" },
+ { }
+};
+#endif
+
+static struct i2c_driver mms114_driver = {
+ .driver = {
+ .name = "mms114",
+ .owner = THIS_MODULE,
+ .pm = &mms114_pm_ops,
+ .of_match_table = of_match_ptr(mms114_dt_match),
+ },
+ .probe = mms114_probe,
+ .id_table = mms114_id,
+};
+
+module_i2c_driver(mms114_driver);
+
+/* Module information */
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_DESCRIPTION("MELFAS mms114 Touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mtouch.c b/drivers/input/touchscreen/mtouch.c
new file mode 100644
index 000000000..9b5552a26
--- /dev/null
+++ b/drivers/input/touchscreen/mtouch.c
@@ -0,0 +1,204 @@
+/*
+ * MicroTouch (3M) serial touchscreen driver
+ *
+ * Copyright (c) 2004 Vojtech Pavlik
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2005/02/19 Dan Streetman <ddstreet@ieee.org>
+ * Copied elo.c and edited for MicroTouch protocol
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "MicroTouch serial touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define MTOUCH_FORMAT_TABLET_STATUS_BIT 0x80
+#define MTOUCH_FORMAT_TABLET_TOUCH_BIT 0x40
+#define MTOUCH_FORMAT_TABLET_LENGTH 5
+#define MTOUCH_RESPONSE_BEGIN_BYTE 0x01
+#define MTOUCH_RESPONSE_END_BYTE 0x0d
+
+/* todo: check specs for max length of all responses */
+#define MTOUCH_MAX_LENGTH 16
+
+#define MTOUCH_MIN_XC 0
+#define MTOUCH_MAX_XC 0x3fff
+#define MTOUCH_MIN_YC 0
+#define MTOUCH_MAX_YC 0x3fff
+
+#define MTOUCH_GET_XC(data) (((data[2])<<7) | data[1])
+#define MTOUCH_GET_YC(data) (((data[4])<<7) | data[3])
+#define MTOUCH_GET_TOUCHED(data) (MTOUCH_FORMAT_TABLET_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct mtouch {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[MTOUCH_MAX_LENGTH];
+ char phys[32];
+};
+
+static void mtouch_process_format_tablet(struct mtouch *mtouch)
+{
+ struct input_dev *dev = mtouch->dev;
+
+ if (MTOUCH_FORMAT_TABLET_LENGTH == ++mtouch->idx) {
+ input_report_abs(dev, ABS_X, MTOUCH_GET_XC(mtouch->data));
+ input_report_abs(dev, ABS_Y, MTOUCH_MAX_YC - MTOUCH_GET_YC(mtouch->data));
+ input_report_key(dev, BTN_TOUCH, MTOUCH_GET_TOUCHED(mtouch->data));
+ input_sync(dev);
+
+ mtouch->idx = 0;
+ }
+}
+
+static void mtouch_process_response(struct mtouch *mtouch)
+{
+ if (MTOUCH_RESPONSE_END_BYTE == mtouch->data[mtouch->idx++]) {
+ /* FIXME - process response */
+ mtouch->idx = 0;
+ } else if (MTOUCH_MAX_LENGTH == mtouch->idx) {
+ printk(KERN_ERR "mtouch.c: too many response bytes\n");
+ mtouch->idx = 0;
+ }
+}
+
+static irqreturn_t mtouch_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct mtouch* mtouch = serio_get_drvdata(serio);
+
+ mtouch->data[mtouch->idx] = data;
+
+ if (MTOUCH_FORMAT_TABLET_STATUS_BIT & mtouch->data[0])
+ mtouch_process_format_tablet(mtouch);
+ else if (MTOUCH_RESPONSE_BEGIN_BYTE == mtouch->data[0])
+ mtouch_process_response(mtouch);
+ else
+ printk(KERN_DEBUG "mtouch.c: unknown/unsynchronized data from device, byte %x\n",mtouch->data[0]);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * mtouch_disconnect() is the opposite of mtouch_connect()
+ */
+
+static void mtouch_disconnect(struct serio *serio)
+{
+ struct mtouch* mtouch = serio_get_drvdata(serio);
+
+ input_get_device(mtouch->dev);
+ input_unregister_device(mtouch->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(mtouch->dev);
+ kfree(mtouch);
+}
+
+/*
+ * mtouch_connect() is the routine that is called when someone adds a
+ * new serio device that supports MicroTouch (Format Tablet) protocol and registers it as
+ * an input device.
+ */
+
+static int mtouch_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct mtouch *mtouch;
+ struct input_dev *input_dev;
+ int err;
+
+ mtouch = kzalloc(sizeof(struct mtouch), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!mtouch || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ mtouch->serio = serio;
+ mtouch->dev = input_dev;
+ snprintf(mtouch->phys, sizeof(mtouch->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "MicroTouch Serial TouchScreen";
+ input_dev->phys = mtouch->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_MICROTOUCH;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(mtouch->dev, ABS_X, MTOUCH_MIN_XC, MTOUCH_MAX_XC, 0, 0);
+ input_set_abs_params(mtouch->dev, ABS_Y, MTOUCH_MIN_YC, MTOUCH_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, mtouch);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(mtouch->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(mtouch);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id mtouch_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_MICROTOUCH,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, mtouch_serio_ids);
+
+static struct serio_driver mtouch_drv = {
+ .driver = {
+ .name = "mtouch",
+ },
+ .description = DRIVER_DESC,
+ .id_table = mtouch_serio_ids,
+ .interrupt = mtouch_interrupt,
+ .connect = mtouch_connect,
+ .disconnect = mtouch_disconnect,
+};
+
+module_serio_driver(mtouch_drv);
diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c
new file mode 100644
index 000000000..b82b5207c
--- /dev/null
+++ b/drivers/input/touchscreen/of_touchscreen.c
@@ -0,0 +1,87 @@
+/*
+ * Generic DT helper functions for touchscreen devices
+ *
+ * Copyright (c) 2014 Sebastian Reichel <sre@kernel.org>
+ *
+ * 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/of.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+
+static u32 of_get_optional_u32(struct device_node *np,
+ const char *property)
+{
+ u32 val = 0;
+
+ of_property_read_u32(np, property, &val);
+
+ return val;
+}
+
+static void touchscreen_set_params(struct input_dev *dev,
+ unsigned long axis,
+ int max, int fuzz)
+{
+ struct input_absinfo *absinfo;
+
+ if (!test_bit(axis, dev->absbit)) {
+ /*
+ * Emit a warning only if the axis is not a multitouch
+ * axis, which might not be set by the driver.
+ */
+ if (!input_is_mt_axis(axis))
+ dev_warn(&dev->dev,
+ "DT specifies parameters but the axis is not set up\n");
+ return;
+ }
+
+ absinfo = &dev->absinfo[axis];
+ absinfo->maximum = max;
+ absinfo->fuzz = fuzz;
+}
+
+/**
+ * touchscreen_parse_of_params - parse common touchscreen DT properties
+ * @dev: device that should be parsed
+ *
+ * This function parses common DT properties for touchscreens and setups the
+ * input device accordingly. The function keeps previously setuped default
+ * values if no value is specified via DT.
+ */
+void touchscreen_parse_of_params(struct input_dev *dev)
+{
+ struct device_node *np = dev->dev.parent->of_node;
+ u32 maximum, fuzz;
+
+ input_alloc_absinfo(dev);
+ if (!dev->absinfo)
+ return;
+
+ maximum = of_get_optional_u32(np, "touchscreen-size-x");
+ fuzz = of_get_optional_u32(np, "touchscreen-fuzz-x");
+ if (maximum || fuzz) {
+ touchscreen_set_params(dev, ABS_X, maximum, fuzz);
+ touchscreen_set_params(dev, ABS_MT_POSITION_X, maximum, fuzz);
+ }
+
+ maximum = of_get_optional_u32(np, "touchscreen-size-y");
+ fuzz = of_get_optional_u32(np, "touchscreen-fuzz-y");
+ if (maximum || fuzz) {
+ touchscreen_set_params(dev, ABS_Y, maximum, fuzz);
+ touchscreen_set_params(dev, ABS_MT_POSITION_Y, maximum, fuzz);
+ }
+
+ maximum = of_get_optional_u32(np, "touchscreen-max-pressure");
+ fuzz = of_get_optional_u32(np, "touchscreen-fuzz-pressure");
+ if (maximum || fuzz) {
+ touchscreen_set_params(dev, ABS_PRESSURE, maximum, fuzz);
+ touchscreen_set_params(dev, ABS_MT_PRESSURE, maximum, fuzz);
+ }
+}
+EXPORT_SYMBOL(touchscreen_parse_of_params);
diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c
new file mode 100644
index 000000000..23a354a39
--- /dev/null
+++ b/drivers/input/touchscreen/pcap_ts.c
@@ -0,0 +1,258 @@
+/*
+ * Driver for Motorola PCAP2 touchscreen as found in the EZX phone platform.
+ *
+ * Copyright (C) 2006 Harald Welte <laforge@openezx.org>
+ * Copyright (C) 2009 Daniel Ribeiro <drwyrm@gmail.com>
+ *
+ * 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/module.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/mfd/ezx-pcap.h>
+
+struct pcap_ts {
+ struct pcap_chip *pcap;
+ struct input_dev *input;
+ struct delayed_work work;
+ u16 x, y;
+ u16 pressure;
+ u8 read_state;
+};
+
+#define SAMPLE_DELAY 20 /* msecs */
+
+#define X_AXIS_MIN 0
+#define X_AXIS_MAX 1023
+#define Y_AXIS_MAX X_AXIS_MAX
+#define Y_AXIS_MIN X_AXIS_MIN
+#define PRESSURE_MAX X_AXIS_MAX
+#define PRESSURE_MIN X_AXIS_MIN
+
+static void pcap_ts_read_xy(void *data, u16 res[2])
+{
+ struct pcap_ts *pcap_ts = data;
+
+ switch (pcap_ts->read_state) {
+ case PCAP_ADC_TS_M_PRESSURE:
+ /* pressure reading is unreliable */
+ if (res[0] > PRESSURE_MIN && res[0] < PRESSURE_MAX)
+ pcap_ts->pressure = res[0];
+ pcap_ts->read_state = PCAP_ADC_TS_M_XY;
+ schedule_delayed_work(&pcap_ts->work, 0);
+ break;
+ case PCAP_ADC_TS_M_XY:
+ pcap_ts->y = res[0];
+ pcap_ts->x = res[1];
+ if (pcap_ts->x <= X_AXIS_MIN || pcap_ts->x >= X_AXIS_MAX ||
+ pcap_ts->y <= Y_AXIS_MIN || pcap_ts->y >= Y_AXIS_MAX) {
+ /* pen has been released */
+ input_report_abs(pcap_ts->input, ABS_PRESSURE, 0);
+ input_report_key(pcap_ts->input, BTN_TOUCH, 0);
+
+ pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY;
+ schedule_delayed_work(&pcap_ts->work, 0);
+ } else {
+ /* pen is touching the screen */
+ input_report_abs(pcap_ts->input, ABS_X, pcap_ts->x);
+ input_report_abs(pcap_ts->input, ABS_Y, pcap_ts->y);
+ input_report_key(pcap_ts->input, BTN_TOUCH, 1);
+ input_report_abs(pcap_ts->input, ABS_PRESSURE,
+ pcap_ts->pressure);
+
+ /* switch back to pressure read mode */
+ pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE;
+ schedule_delayed_work(&pcap_ts->work,
+ msecs_to_jiffies(SAMPLE_DELAY));
+ }
+ input_sync(pcap_ts->input);
+ break;
+ default:
+ dev_warn(&pcap_ts->input->dev,
+ "pcap_ts: Warning, unhandled read_state %d\n",
+ pcap_ts->read_state);
+ break;
+ }
+}
+
+static void pcap_ts_work(struct work_struct *work)
+{
+ struct delayed_work *dw = container_of(work, struct delayed_work, work);
+ struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work);
+ u8 ch[2];
+
+ pcap_set_ts_bits(pcap_ts->pcap,
+ pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+
+ if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY)
+ return;
+
+ /* start adc conversion */
+ ch[0] = PCAP_ADC_CH_TS_X1;
+ ch[1] = PCAP_ADC_CH_TS_Y1;
+ pcap_adc_async(pcap_ts->pcap, PCAP_ADC_BANK_1, 0, ch,
+ pcap_ts_read_xy, pcap_ts);
+}
+
+static irqreturn_t pcap_ts_event_touch(int pirq, void *data)
+{
+ struct pcap_ts *pcap_ts = data;
+
+ if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) {
+ pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE;
+ schedule_delayed_work(&pcap_ts->work, 0);
+ }
+ return IRQ_HANDLED;
+}
+
+static int pcap_ts_open(struct input_dev *dev)
+{
+ struct pcap_ts *pcap_ts = input_get_drvdata(dev);
+
+ pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY;
+ schedule_delayed_work(&pcap_ts->work, 0);
+
+ return 0;
+}
+
+static void pcap_ts_close(struct input_dev *dev)
+{
+ struct pcap_ts *pcap_ts = input_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&pcap_ts->work);
+
+ pcap_ts->read_state = PCAP_ADC_TS_M_NONTS;
+ pcap_set_ts_bits(pcap_ts->pcap,
+ pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+}
+
+static int pcap_ts_probe(struct platform_device *pdev)
+{
+ struct input_dev *input_dev;
+ struct pcap_ts *pcap_ts;
+ int err = -ENOMEM;
+
+ pcap_ts = kzalloc(sizeof(*pcap_ts), GFP_KERNEL);
+ if (!pcap_ts)
+ return err;
+
+ pcap_ts->pcap = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, pcap_ts);
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto fail;
+
+ INIT_DELAYED_WORK(&pcap_ts->work, pcap_ts_work);
+
+ pcap_ts->read_state = PCAP_ADC_TS_M_NONTS;
+ pcap_set_ts_bits(pcap_ts->pcap,
+ pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+
+ pcap_ts->input = input_dev;
+ input_set_drvdata(input_dev, pcap_ts);
+
+ input_dev->name = "pcap-touchscreen";
+ input_dev->phys = "pcap_ts/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0002;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->open = pcap_ts_open;
+ input_dev->close = pcap_ts_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN,
+ PRESSURE_MAX, 0, 0);
+
+ err = input_register_device(pcap_ts->input);
+ if (err)
+ goto fail_allocate;
+
+ err = request_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS),
+ pcap_ts_event_touch, 0, "Touch Screen", pcap_ts);
+ if (err)
+ goto fail_register;
+
+ return 0;
+
+fail_register:
+ input_unregister_device(input_dev);
+ goto fail;
+fail_allocate:
+ input_free_device(input_dev);
+fail:
+ kfree(pcap_ts);
+
+ return err;
+}
+
+static int pcap_ts_remove(struct platform_device *pdev)
+{
+ struct pcap_ts *pcap_ts = platform_get_drvdata(pdev);
+
+ free_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), pcap_ts);
+ cancel_delayed_work_sync(&pcap_ts->work);
+
+ input_unregister_device(pcap_ts->input);
+
+ kfree(pcap_ts);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pcap_ts_suspend(struct device *dev)
+{
+ struct pcap_ts *pcap_ts = dev_get_drvdata(dev);
+
+ pcap_set_ts_bits(pcap_ts->pcap, PCAP_ADC_TS_REF_LOWPWR);
+ return 0;
+}
+
+static int pcap_ts_resume(struct device *dev)
+{
+ struct pcap_ts *pcap_ts = dev_get_drvdata(dev);
+
+ pcap_set_ts_bits(pcap_ts->pcap,
+ pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+ return 0;
+}
+
+static const struct dev_pm_ops pcap_ts_pm_ops = {
+ .suspend = pcap_ts_suspend,
+ .resume = pcap_ts_resume,
+};
+#define PCAP_TS_PM_OPS (&pcap_ts_pm_ops)
+#else
+#define PCAP_TS_PM_OPS NULL
+#endif
+
+static struct platform_driver pcap_ts_driver = {
+ .probe = pcap_ts_probe,
+ .remove = pcap_ts_remove,
+ .driver = {
+ .name = "pcap-ts",
+ .pm = PCAP_TS_PM_OPS,
+ },
+};
+module_platform_driver(pcap_ts_driver);
+
+MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver");
+MODULE_AUTHOR("Daniel Ribeiro / Harald Welte");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcap_ts");
diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c
new file mode 100644
index 000000000..417d87379
--- /dev/null
+++ b/drivers/input/touchscreen/penmount.c
@@ -0,0 +1,319 @@
+/*
+ * Penmount serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ * Copyright (c) 2011 John Sung <penmount.touch@gmail.com>
+ *
+ * Based on ELO driver (drivers/input/touchscreen/elo.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ */
+
+/*
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "PenMount serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_AUTHOR("John Sung <penmount.touch@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define PM_MAX_LENGTH 6
+#define PM_MAX_MTSLOT 16
+#define PM_3000_MTSLOT 2
+#define PM_6250_MTSLOT 12
+
+/*
+ * Multi-touch slot
+ */
+
+struct mt_slot {
+ unsigned short x, y;
+ bool active; /* is the touch valid? */
+};
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct pm {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[PM_MAX_LENGTH];
+ char phys[32];
+ unsigned char packetsize;
+ unsigned char maxcontacts;
+ struct mt_slot slots[PM_MAX_MTSLOT];
+ void (*parse_packet)(struct pm *);
+};
+
+/*
+ * pm_mtevent() sends mt events and also emulates pointer movement
+ */
+
+static void pm_mtevent(struct pm *pm, struct input_dev *input)
+{
+ int i;
+
+ for (i = 0; i < pm->maxcontacts; ++i) {
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER,
+ pm->slots[i].active);
+ if (pm->slots[i].active) {
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, pm->slots[i].x);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, pm->slots[i].y);
+ }
+ }
+
+ input_mt_report_pointer_emulation(input, true);
+ input_sync(input);
+}
+
+/*
+ * pm_checkpacket() checks if data packet is valid
+ */
+
+static bool pm_checkpacket(unsigned char *packet)
+{
+ int total = 0;
+ int i;
+
+ for (i = 0; i < 5; i++)
+ total += packet[i];
+
+ return packet[5] == (unsigned char)~(total & 0xff);
+}
+
+static void pm_parse_9000(struct pm *pm)
+{
+ struct input_dev *dev = pm->dev;
+
+ if ((pm->data[0] & 0x80) && pm->packetsize == ++pm->idx) {
+ input_report_abs(dev, ABS_X, pm->data[1] * 128 + pm->data[2]);
+ input_report_abs(dev, ABS_Y, pm->data[3] * 128 + pm->data[4]);
+ input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40));
+ input_sync(dev);
+ pm->idx = 0;
+ }
+}
+
+static void pm_parse_6000(struct pm *pm)
+{
+ struct input_dev *dev = pm->dev;
+
+ if ((pm->data[0] & 0xbf) == 0x30 && pm->packetsize == ++pm->idx) {
+ if (pm_checkpacket(pm->data)) {
+ input_report_abs(dev, ABS_X,
+ pm->data[2] * 256 + pm->data[1]);
+ input_report_abs(dev, ABS_Y,
+ pm->data[4] * 256 + pm->data[3]);
+ input_report_key(dev, BTN_TOUCH, pm->data[0] & 0x40);
+ input_sync(dev);
+ }
+ pm->idx = 0;
+ }
+}
+
+static void pm_parse_3000(struct pm *pm)
+{
+ struct input_dev *dev = pm->dev;
+
+ if ((pm->data[0] & 0xce) == 0x40 && pm->packetsize == ++pm->idx) {
+ if (pm_checkpacket(pm->data)) {
+ int slotnum = pm->data[0] & 0x0f;
+ pm->slots[slotnum].active = pm->data[0] & 0x30;
+ pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1];
+ pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3];
+ pm_mtevent(pm, dev);
+ }
+ pm->idx = 0;
+ }
+}
+
+static void pm_parse_6250(struct pm *pm)
+{
+ struct input_dev *dev = pm->dev;
+
+ if ((pm->data[0] & 0xb0) == 0x30 && pm->packetsize == ++pm->idx) {
+ if (pm_checkpacket(pm->data)) {
+ int slotnum = pm->data[0] & 0x0f;
+ pm->slots[slotnum].active = pm->data[0] & 0x40;
+ pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1];
+ pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3];
+ pm_mtevent(pm, dev);
+ }
+ pm->idx = 0;
+ }
+}
+
+static irqreturn_t pm_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct pm *pm = serio_get_drvdata(serio);
+
+ pm->data[pm->idx] = data;
+
+ pm->parse_packet(pm);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * pm_disconnect() is the opposite of pm_connect()
+ */
+
+static void pm_disconnect(struct serio *serio)
+{
+ struct pm *pm = serio_get_drvdata(serio);
+
+ serio_close(serio);
+
+ input_unregister_device(pm->dev);
+ kfree(pm);
+
+ serio_set_drvdata(serio, NULL);
+}
+
+/*
+ * pm_connect() is the routine that is called when someone adds a
+ * new serio device that supports PenMount protocol and registers it as
+ * an input device.
+ */
+
+static int pm_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct pm *pm;
+ struct input_dev *input_dev;
+ int max_x, max_y;
+ int err;
+
+ pm = kzalloc(sizeof(struct pm), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!pm || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ pm->serio = serio;
+ pm->dev = input_dev;
+ snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys);
+ pm->maxcontacts = 1;
+
+ input_dev->name = "PenMount Serial TouchScreen";
+ input_dev->phys = pm->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_PENMOUNT;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ switch (serio->id.id) {
+ default:
+ case 0:
+ pm->packetsize = 5;
+ pm->parse_packet = pm_parse_9000;
+ input_dev->id.product = 0x9000;
+ max_x = max_y = 0x3ff;
+ break;
+
+ case 1:
+ pm->packetsize = 6;
+ pm->parse_packet = pm_parse_6000;
+ input_dev->id.product = 0x6000;
+ max_x = max_y = 0x3ff;
+ break;
+
+ case 2:
+ pm->packetsize = 6;
+ pm->parse_packet = pm_parse_3000;
+ input_dev->id.product = 0x3000;
+ max_x = max_y = 0x7ff;
+ pm->maxcontacts = PM_3000_MTSLOT;
+ break;
+
+ case 3:
+ pm->packetsize = 6;
+ pm->parse_packet = pm_parse_6250;
+ input_dev->id.product = 0x6250;
+ max_x = max_y = 0x3ff;
+ pm->maxcontacts = PM_6250_MTSLOT;
+ break;
+ }
+
+ input_set_abs_params(pm->dev, ABS_X, 0, max_x, 0, 0);
+ input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0);
+
+ if (pm->maxcontacts > 1) {
+ input_mt_init_slots(pm->dev, pm->maxcontacts, 0);
+ input_set_abs_params(pm->dev,
+ ABS_MT_POSITION_X, 0, max_x, 0, 0);
+ input_set_abs_params(pm->dev,
+ ABS_MT_POSITION_Y, 0, max_y, 0, 0);
+ }
+
+ serio_set_drvdata(serio, pm);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(pm->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(pm);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id pm_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_PENMOUNT,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, pm_serio_ids);
+
+static struct serio_driver pm_drv = {
+ .driver = {
+ .name = "serio-penmount",
+ },
+ .description = DRIVER_DESC,
+ .id_table = pm_serio_ids,
+ .interrupt = pm_interrupt,
+ .connect = pm_connect,
+ .disconnect = pm_disconnect,
+};
+
+module_serio_driver(pm_drv);
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
new file mode 100644
index 000000000..8f3e243a6
--- /dev/null
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -0,0 +1,619 @@
+/*
+ * Driver for Pixcir I2C touchscreen controllers.
+ *
+ * Copyright (C) 2010-2011 Pixcir, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/pixcir_ts.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+
+#define PIXCIR_MAX_SLOTS 5 /* Max fingers supported by driver */
+
+struct pixcir_i2c_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ const struct pixcir_ts_platform_data *pdata;
+ bool running;
+ int max_fingers; /* Max fingers supported in this instance */
+};
+
+struct pixcir_touch {
+ int x;
+ int y;
+ int id;
+};
+
+struct pixcir_report_data {
+ int num_touches;
+ struct pixcir_touch touches[PIXCIR_MAX_SLOTS];
+};
+
+static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
+ struct pixcir_report_data *report)
+{
+ u8 rdbuf[2 + PIXCIR_MAX_SLOTS * 5];
+ u8 wrbuf[1] = { 0 };
+ u8 *bufptr;
+ u8 touch;
+ int ret, i;
+ int readsize;
+ const struct pixcir_i2c_chip_data *chip = &tsdata->pdata->chip;
+
+ memset(report, 0, sizeof(struct pixcir_report_data));
+
+ i = chip->has_hw_ids ? 1 : 0;
+ readsize = 2 + tsdata->max_fingers * (4 + i);
+ if (readsize > sizeof(rdbuf))
+ readsize = sizeof(rdbuf);
+
+ ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf));
+ if (ret != sizeof(wrbuf)) {
+ dev_err(&tsdata->client->dev,
+ "%s: i2c_master_send failed(), ret=%d\n",
+ __func__, ret);
+ return;
+ }
+
+ ret = i2c_master_recv(tsdata->client, rdbuf, readsize);
+ if (ret != readsize) {
+ dev_err(&tsdata->client->dev,
+ "%s: i2c_master_recv failed(), ret=%d\n",
+ __func__, ret);
+ return;
+ }
+
+ touch = rdbuf[0] & 0x7;
+ if (touch > tsdata->max_fingers)
+ touch = tsdata->max_fingers;
+
+ report->num_touches = touch;
+ bufptr = &rdbuf[2];
+
+ for (i = 0; i < touch; i++) {
+ report->touches[i].x = (bufptr[1] << 8) | bufptr[0];
+ report->touches[i].y = (bufptr[3] << 8) | bufptr[2];
+
+ if (chip->has_hw_ids) {
+ report->touches[i].id = bufptr[4];
+ bufptr = bufptr + 5;
+ } else {
+ bufptr = bufptr + 4;
+ }
+ }
+}
+
+static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
+ struct pixcir_report_data *report)
+{
+ struct input_mt_pos pos[PIXCIR_MAX_SLOTS];
+ int slots[PIXCIR_MAX_SLOTS];
+ struct pixcir_touch *touch;
+ int n, i, slot;
+ struct device *dev = &ts->client->dev;
+ const struct pixcir_i2c_chip_data *chip = &ts->pdata->chip;
+
+ n = report->num_touches;
+ if (n > PIXCIR_MAX_SLOTS)
+ n = PIXCIR_MAX_SLOTS;
+
+ if (!chip->has_hw_ids) {
+ for (i = 0; i < n; i++) {
+ touch = &report->touches[i];
+ pos[i].x = touch->x;
+ pos[i].y = touch->y;
+ }
+
+ input_mt_assign_slots(ts->input, slots, pos, n, 0);
+ }
+
+ for (i = 0; i < n; i++) {
+ touch = &report->touches[i];
+
+ if (chip->has_hw_ids) {
+ slot = input_mt_get_slot_by_key(ts->input, touch->id);
+ if (slot < 0) {
+ dev_dbg(dev, "no free slot for id 0x%x\n",
+ touch->id);
+ continue;
+ }
+ } else {
+ slot = slots[i];
+ }
+
+ input_mt_slot(ts->input, slot);
+ input_mt_report_slot_state(ts->input,
+ MT_TOOL_FINGER, true);
+
+ input_event(ts->input, EV_ABS, ABS_MT_POSITION_X, touch->x);
+ input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y, touch->y);
+
+ dev_dbg(dev, "%d: slot %d, x %d, y %d\n",
+ i, slot, touch->x, touch->y);
+ }
+
+ input_mt_sync_frame(ts->input);
+ input_sync(ts->input);
+}
+
+static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
+{
+ struct pixcir_i2c_ts_data *tsdata = dev_id;
+ const struct pixcir_ts_platform_data *pdata = tsdata->pdata;
+ struct pixcir_report_data report;
+
+ while (tsdata->running) {
+ /* parse packet */
+ pixcir_ts_parse(tsdata, &report);
+
+ /* report it */
+ pixcir_ts_report(tsdata, &report);
+
+ if (gpio_get_value(pdata->gpio_attb)) {
+ if (report.num_touches) {
+ /*
+ * Last report with no finger up?
+ * Do it now then.
+ */
+ input_mt_sync_frame(tsdata->input);
+ input_sync(tsdata->input);
+ }
+ break;
+ }
+
+ msleep(20);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts,
+ enum pixcir_power_mode mode)
+{
+ struct device *dev = &ts->client->dev;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE);
+ if (ret < 0) {
+ dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+ __func__, PIXCIR_REG_POWER_MODE, ret);
+ return ret;
+ }
+
+ ret &= ~PIXCIR_POWER_MODE_MASK;
+ ret |= mode;
+
+ /* Always AUTO_IDLE */
+ ret |= PIXCIR_POWER_ALLOW_IDLE;
+
+ ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_POWER_MODE, ret);
+ if (ret < 0) {
+ dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+ __func__, PIXCIR_REG_POWER_MODE, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Set the interrupt mode for the device i.e. ATTB line behaviour
+ *
+ * @polarity : 1 for active high, 0 for active low.
+ */
+static int pixcir_set_int_mode(struct pixcir_i2c_ts_data *ts,
+ enum pixcir_int_mode mode, bool polarity)
+{
+ struct device *dev = &ts->client->dev;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE);
+ if (ret < 0) {
+ dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+ __func__, PIXCIR_REG_INT_MODE, ret);
+ return ret;
+ }
+
+ ret &= ~PIXCIR_INT_MODE_MASK;
+ ret |= mode;
+
+ if (polarity)
+ ret |= PIXCIR_INT_POL_HIGH;
+ else
+ ret &= ~PIXCIR_INT_POL_HIGH;
+
+ ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret);
+ if (ret < 0) {
+ dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+ __func__, PIXCIR_REG_INT_MODE, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Enable/disable interrupt generation
+ */
+static int pixcir_int_enable(struct pixcir_i2c_ts_data *ts, bool enable)
+{
+ struct device *dev = &ts->client->dev;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE);
+ if (ret < 0) {
+ dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+ __func__, PIXCIR_REG_INT_MODE, ret);
+ return ret;
+ }
+
+ if (enable)
+ ret |= PIXCIR_INT_ENABLE;
+ else
+ ret &= ~PIXCIR_INT_ENABLE;
+
+ ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret);
+ if (ret < 0) {
+ dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+ __func__, PIXCIR_REG_INT_MODE, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pixcir_start(struct pixcir_i2c_ts_data *ts)
+{
+ struct device *dev = &ts->client->dev;
+ int error;
+
+ /* LEVEL_TOUCH interrupt with active low polarity */
+ error = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0);
+ if (error) {
+ dev_err(dev, "Failed to set interrupt mode: %d\n", error);
+ return error;
+ }
+
+ ts->running = true;
+ mb(); /* Update status before IRQ can fire */
+
+ /* enable interrupt generation */
+ error = pixcir_int_enable(ts, true);
+ if (error) {
+ dev_err(dev, "Failed to enable interrupt generation: %d\n",
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int pixcir_stop(struct pixcir_i2c_ts_data *ts)
+{
+ int error;
+
+ /* Disable interrupt generation */
+ error = pixcir_int_enable(ts, false);
+ if (error) {
+ dev_err(&ts->client->dev,
+ "Failed to disable interrupt generation: %d\n",
+ error);
+ return error;
+ }
+
+ /* Exit ISR if running, no more report parsing */
+ ts->running = false;
+ mb(); /* update status before we synchronize irq */
+
+ /* Wait till running ISR is complete */
+ synchronize_irq(ts->client->irq);
+
+ return 0;
+}
+
+static int pixcir_input_open(struct input_dev *dev)
+{
+ struct pixcir_i2c_ts_data *ts = input_get_drvdata(dev);
+
+ return pixcir_start(ts);
+}
+
+static void pixcir_input_close(struct input_dev *dev)
+{
+ struct pixcir_i2c_ts_data *ts = input_get_drvdata(dev);
+
+ pixcir_stop(ts);
+}
+
+static int __maybe_unused pixcir_i2c_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+ int ret = 0;
+
+ mutex_lock(&input->mutex);
+
+ if (device_may_wakeup(&client->dev)) {
+ if (!input->users) {
+ ret = pixcir_start(ts);
+ if (ret) {
+ dev_err(dev, "Failed to start\n");
+ goto unlock;
+ }
+ }
+
+ enable_irq_wake(client->irq);
+ } else if (input->users) {
+ ret = pixcir_stop(ts);
+ }
+
+unlock:
+ mutex_unlock(&input->mutex);
+
+ return ret;
+}
+
+static int __maybe_unused pixcir_i2c_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+ int ret = 0;
+
+ mutex_lock(&input->mutex);
+
+ if (device_may_wakeup(&client->dev)) {
+ disable_irq_wake(client->irq);
+
+ if (!input->users) {
+ ret = pixcir_stop(ts);
+ if (ret) {
+ dev_err(dev, "Failed to stop\n");
+ goto unlock;
+ }
+ }
+ } else if (input->users) {
+ ret = pixcir_start(ts);
+ }
+
+unlock:
+ mutex_unlock(&input->mutex);
+
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops,
+ pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pixcir_of_match[];
+
+static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev)
+{
+ struct pixcir_ts_platform_data *pdata;
+ struct device_node *np = dev->of_node;
+ const struct of_device_id *match;
+
+ match = of_match_device(of_match_ptr(pixcir_of_match), dev);
+ if (!match)
+ return ERR_PTR(-EINVAL);
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->chip = *(const struct pixcir_i2c_chip_data *)match->data;
+
+ pdata->gpio_attb = of_get_named_gpio(np, "attb-gpio", 0);
+ /* gpio_attb validity is checked in probe */
+
+ if (of_property_read_u32(np, "touchscreen-size-x", &pdata->x_max)) {
+ dev_err(dev, "Failed to get touchscreen-size-x property\n");
+ return ERR_PTR(-EINVAL);
+ }
+ pdata->x_max -= 1;
+
+ if (of_property_read_u32(np, "touchscreen-size-y", &pdata->y_max)) {
+ dev_err(dev, "Failed to get touchscreen-size-y property\n");
+ return ERR_PTR(-EINVAL);
+ }
+ pdata->y_max -= 1;
+
+ dev_dbg(dev, "%s: x %d, y %d, gpio %d\n", __func__,
+ pdata->x_max + 1, pdata->y_max + 1, pdata->gpio_attb);
+
+ return pdata;
+}
+#else
+static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev)
+{
+ return ERR_PTR(-EINVAL);
+}
+#endif
+
+static int pixcir_i2c_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct pixcir_ts_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+ struct device *dev = &client->dev;
+ struct device_node *np = dev->of_node;
+ struct pixcir_i2c_ts_data *tsdata;
+ struct input_dev *input;
+ int error;
+
+ if (np && !pdata) {
+ pdata = pixcir_parse_dt(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
+
+ if (!pdata) {
+ dev_err(&client->dev, "platform data not defined\n");
+ return -EINVAL;
+ }
+
+ if (!gpio_is_valid(pdata->gpio_attb)) {
+ dev_err(dev, "Invalid gpio_attb in pdata\n");
+ return -EINVAL;
+ }
+
+ if (!pdata->chip.max_fingers) {
+ dev_err(dev, "Invalid max_fingers in pdata\n");
+ return -EINVAL;
+ }
+
+ tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL);
+ if (!tsdata)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(dev);
+ if (!input) {
+ dev_err(dev, "Failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ tsdata->client = client;
+ tsdata->input = input;
+ tsdata->pdata = pdata;
+
+ input->name = client->name;
+ input->id.bustype = BUS_I2C;
+ input->open = pixcir_input_open;
+ input->close = pixcir_input_close;
+ input->dev.parent = &client->dev;
+
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ input_set_abs_params(input, ABS_X, 0, pdata->x_max, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, pdata->y_max, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0);
+
+ tsdata->max_fingers = tsdata->pdata->chip.max_fingers;
+ if (tsdata->max_fingers > PIXCIR_MAX_SLOTS) {
+ tsdata->max_fingers = PIXCIR_MAX_SLOTS;
+ dev_info(dev, "Limiting maximum fingers to %d\n",
+ tsdata->max_fingers);
+ }
+
+ error = input_mt_init_slots(input, tsdata->max_fingers,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+ if (error) {
+ dev_err(dev, "Error initializing Multi-Touch slots\n");
+ return error;
+ }
+
+ input_set_drvdata(input, tsdata);
+
+ error = devm_gpio_request_one(dev, pdata->gpio_attb,
+ GPIOF_DIR_IN, "pixcir_i2c_attb");
+ if (error) {
+ dev_err(dev, "Failed to request ATTB gpio\n");
+ return error;
+ }
+
+ error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ client->name, tsdata);
+ if (error) {
+ dev_err(dev, "failed to request irq %d\n", client->irq);
+ return error;
+ }
+
+ /* Always be in IDLE mode to save power, device supports auto wake */
+ error = pixcir_set_power_mode(tsdata, PIXCIR_POWER_IDLE);
+ if (error) {
+ dev_err(dev, "Failed to set IDLE mode\n");
+ return error;
+ }
+
+ /* Stop device till opened */
+ error = pixcir_stop(tsdata);
+ if (error)
+ return error;
+
+ error = input_register_device(input);
+ if (error)
+ return error;
+
+ i2c_set_clientdata(client, tsdata);
+ device_init_wakeup(&client->dev, 1);
+
+ return 0;
+}
+
+static int pixcir_i2c_ts_remove(struct i2c_client *client)
+{
+ device_init_wakeup(&client->dev, 0);
+
+ return 0;
+}
+
+static const struct i2c_device_id pixcir_i2c_ts_id[] = {
+ { "pixcir_ts", 0 },
+ { "pixcir_tangoc", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id);
+
+#ifdef CONFIG_OF
+static const struct pixcir_i2c_chip_data pixcir_ts_data = {
+ .max_fingers = 2,
+ /* no hw id support */
+};
+
+static const struct pixcir_i2c_chip_data pixcir_tangoc_data = {
+ .max_fingers = 5,
+ .has_hw_ids = true,
+};
+
+static const struct of_device_id pixcir_of_match[] = {
+ { .compatible = "pixcir,pixcir_ts", .data = &pixcir_ts_data },
+ { .compatible = "pixcir,pixcir_tangoc", .data = &pixcir_tangoc_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pixcir_of_match);
+#endif
+
+static struct i2c_driver pixcir_i2c_ts_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "pixcir_ts",
+ .pm = &pixcir_dev_pm_ops,
+ .of_match_table = of_match_ptr(pixcir_of_match),
+ },
+ .probe = pixcir_i2c_ts_probe,
+ .remove = pixcir_i2c_ts_remove,
+ .id_table = pixcir_i2c_ts_id,
+};
+
+module_i2c_driver(pixcir_i2c_ts_driver);
+
+MODULE_AUTHOR("Jianchun Bian <jcbian@pixcir.com.cn>, Dequan Meng <dqmeng@pixcir.com.cn>");
+MODULE_DESCRIPTION("Pixcir I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
new file mode 100644
index 000000000..bdfa27dc0
--- /dev/null
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -0,0 +1,439 @@
+/*
+ * Samsung S3C24XX touchscreen driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the term of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
+ * Copyright 2008 Ben Dooks <ben-linux@fluff.org>
+ * Copyright 2009 Simtec Electronics <linux@simtec.co.uk>
+ *
+ * Additional work by Herbert Pötzl <herbert@13thfloor.at> and
+ * Harald Welte <laforge@openmoko.org>
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <plat/adc.h>
+#include <plat/regs-adc.h>
+#include <linux/platform_data/touchscreen-s3c2410.h>
+
+#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))
+
+#define INT_DOWN (0)
+#define INT_UP (1 << 8)
+
+#define WAIT4INT (S3C2410_ADCTSC_YM_SEN | \
+ S3C2410_ADCTSC_YP_SEN | \
+ S3C2410_ADCTSC_XP_SEN | \
+ S3C2410_ADCTSC_XY_PST(3))
+
+#define AUTOPST (S3C2410_ADCTSC_YM_SEN | \
+ S3C2410_ADCTSC_YP_SEN | \
+ S3C2410_ADCTSC_XP_SEN | \
+ S3C2410_ADCTSC_AUTO_PST | \
+ S3C2410_ADCTSC_XY_PST(0))
+
+#define FEAT_PEN_IRQ (1 << 0) /* HAS ADCCLRINTPNDNUP */
+
+/* Per-touchscreen data. */
+
+/**
+ * struct s3c2410ts - driver touchscreen state.
+ * @client: The ADC client we registered with the core driver.
+ * @dev: The device we are bound to.
+ * @input: The input device we registered with the input subsystem.
+ * @clock: The clock for the adc.
+ * @io: Pointer to the IO base.
+ * @xp: The accumulated X position data.
+ * @yp: The accumulated Y position data.
+ * @irq_tc: The interrupt number for pen up/down interrupt
+ * @count: The number of samples collected.
+ * @shift: The log2 of the maximum count to read in one go.
+ * @features: The features supported by the TSADC MOdule.
+ */
+struct s3c2410ts {
+ struct s3c_adc_client *client;
+ struct device *dev;
+ struct input_dev *input;
+ struct clk *clock;
+ void __iomem *io;
+ unsigned long xp;
+ unsigned long yp;
+ int irq_tc;
+ int count;
+ int shift;
+ int features;
+};
+
+static struct s3c2410ts ts;
+
+/**
+ * get_down - return the down state of the pen
+ * @data0: The data read from ADCDAT0 register.
+ * @data1: The data read from ADCDAT1 register.
+ *
+ * Return non-zero if both readings show that the pen is down.
+ */
+static inline bool get_down(unsigned long data0, unsigned long data1)
+{
+ /* returns true if both data values show stylus down */
+ return (!(data0 & S3C2410_ADCDAT0_UPDOWN) &&
+ !(data1 & S3C2410_ADCDAT0_UPDOWN));
+}
+
+static void touch_timer_fire(unsigned long data)
+{
+ unsigned long data0;
+ unsigned long data1;
+ bool down;
+
+ data0 = readl(ts.io + S3C2410_ADCDAT0);
+ data1 = readl(ts.io + S3C2410_ADCDAT1);
+
+ down = get_down(data0, data1);
+
+ if (down) {
+ if (ts.count == (1 << ts.shift)) {
+ ts.xp >>= ts.shift;
+ ts.yp >>= ts.shift;
+
+ dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n",
+ __func__, ts.xp, ts.yp, ts.count);
+
+ input_report_abs(ts.input, ABS_X, ts.xp);
+ input_report_abs(ts.input, ABS_Y, ts.yp);
+
+ input_report_key(ts.input, BTN_TOUCH, 1);
+ input_sync(ts.input);
+
+ ts.xp = 0;
+ ts.yp = 0;
+ ts.count = 0;
+ }
+
+ s3c_adc_start(ts.client, 0, 1 << ts.shift);
+ } else {
+ ts.xp = 0;
+ ts.yp = 0;
+ ts.count = 0;
+
+ input_report_key(ts.input, BTN_TOUCH, 0);
+ input_sync(ts.input);
+
+ writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+ }
+}
+
+static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);
+
+/**
+ * stylus_irq - touchscreen stylus event interrupt
+ * @irq: The interrupt number
+ * @dev_id: The device ID.
+ *
+ * Called when the IRQ_TC is fired for a pen up or down event.
+ */
+static irqreturn_t stylus_irq(int irq, void *dev_id)
+{
+ unsigned long data0;
+ unsigned long data1;
+ bool down;
+
+ data0 = readl(ts.io + S3C2410_ADCDAT0);
+ data1 = readl(ts.io + S3C2410_ADCDAT1);
+
+ down = get_down(data0, data1);
+
+ /* TODO we should never get an interrupt with down set while
+ * the timer is running, but maybe we ought to verify that the
+ * timer isn't running anyways. */
+
+ if (down)
+ s3c_adc_start(ts.client, 0, 1 << ts.shift);
+ else
+ dev_dbg(ts.dev, "%s: count=%d\n", __func__, ts.count);
+
+ if (ts.features & FEAT_PEN_IRQ) {
+ /* Clear pen down/up interrupt */
+ writel(0x0, ts.io + S3C64XX_ADCCLRINTPNDNUP);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * s3c24xx_ts_conversion - ADC conversion callback
+ * @client: The client that was registered with the ADC core.
+ * @data0: The reading from ADCDAT0.
+ * @data1: The reading from ADCDAT1.
+ * @left: The number of samples left.
+ *
+ * Called when a conversion has finished.
+ */
+static void s3c24xx_ts_conversion(struct s3c_adc_client *client,
+ unsigned data0, unsigned data1,
+ unsigned *left)
+{
+ dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1);
+
+ ts.xp += data0;
+ ts.yp += data1;
+
+ ts.count++;
+
+ /* From tests, it seems that it is unlikely to get a pen-up
+ * event during the conversion process which means we can
+ * ignore any pen-up events with less than the requisite
+ * count done.
+ *
+ * In several thousand conversions, no pen-ups where detected
+ * before count completed.
+ */
+}
+
+/**
+ * s3c24xx_ts_select - ADC selection callback.
+ * @client: The client that was registered with the ADC core.
+ * @select: The reason for select.
+ *
+ * Called when the ADC core selects (or deslects) us as a client.
+ */
+static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select)
+{
+ if (select) {
+ writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
+ ts.io + S3C2410_ADCTSC);
+ } else {
+ mod_timer(&touch_timer, jiffies+1);
+ writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC);
+ }
+}
+
+/**
+ * s3c2410ts_probe - device core probe entry point
+ * @pdev: The device we are being bound to.
+ *
+ * Initialise, find and allocate any resources we need to run and then
+ * register with the ADC and input systems.
+ */
+static int s3c2410ts_probe(struct platform_device *pdev)
+{
+ struct s3c2410_ts_mach_info *info;
+ struct device *dev = &pdev->dev;
+ struct input_dev *input_dev;
+ struct resource *res;
+ int ret = -EINVAL;
+
+ /* Initialise input stuff */
+ memset(&ts, 0, sizeof(struct s3c2410ts));
+
+ ts.dev = dev;
+
+ info = dev_get_platdata(&pdev->dev);
+ if (!info) {
+ dev_err(dev, "no platform data, cannot attach\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "initialising touchscreen\n");
+
+ ts.clock = clk_get(dev, "adc");
+ if (IS_ERR(ts.clock)) {
+ dev_err(dev, "cannot get adc clock source\n");
+ return -ENOENT;
+ }
+
+ clk_prepare_enable(ts.clock);
+ dev_dbg(dev, "got and enabled clocks\n");
+
+ ts.irq_tc = ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(dev, "no resource for interrupt\n");
+ goto err_clk;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no resource for registers\n");
+ ret = -ENOENT;
+ goto err_clk;
+ }
+
+ ts.io = ioremap(res->start, resource_size(res));
+ if (ts.io == NULL) {
+ dev_err(dev, "cannot map registers\n");
+ ret = -ENOMEM;
+ goto err_clk;
+ }
+
+ /* inititalise the gpio */
+ if (info->cfg_gpio)
+ info->cfg_gpio(to_platform_device(ts.dev));
+
+ ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,
+ s3c24xx_ts_conversion, 1);
+ if (IS_ERR(ts.client)) {
+ dev_err(dev, "failed to register adc client\n");
+ ret = PTR_ERR(ts.client);
+ goto err_iomap;
+ }
+
+ /* Initialise registers */
+ if ((info->delay & 0xffff) > 0)
+ writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
+
+ writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(dev, "Unable to allocate the input device !!\n");
+ ret = -ENOMEM;
+ goto err_iomap;
+ }
+
+ ts.input = input_dev;
+ ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
+ input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);
+
+ ts.input->name = "S3C24XX TouchScreen";
+ ts.input->id.bustype = BUS_HOST;
+ ts.input->id.vendor = 0xDEAD;
+ ts.input->id.product = 0xBEEF;
+ ts.input->id.version = 0x0102;
+
+ ts.shift = info->oversampling_shift;
+ ts.features = platform_get_device_id(pdev)->driver_data;
+
+ ret = request_irq(ts.irq_tc, stylus_irq, 0,
+ "s3c2410_ts_pen", ts.input);
+ if (ret) {
+ dev_err(dev, "cannot get TC interrupt\n");
+ goto err_inputdev;
+ }
+
+ dev_info(dev, "driver attached, registering input device\n");
+
+ /* All went ok, so register to the input system */
+ ret = input_register_device(ts.input);
+ if (ret < 0) {
+ dev_err(dev, "failed to register input device\n");
+ ret = -EIO;
+ goto err_tcirq;
+ }
+
+ return 0;
+
+ err_tcirq:
+ free_irq(ts.irq_tc, ts.input);
+ err_inputdev:
+ input_free_device(ts.input);
+ err_iomap:
+ iounmap(ts.io);
+ err_clk:
+ del_timer_sync(&touch_timer);
+ clk_put(ts.clock);
+ return ret;
+}
+
+/**
+ * s3c2410ts_remove - device core removal entry point
+ * @pdev: The device we are being removed from.
+ *
+ * Free up our state ready to be removed.
+ */
+static int s3c2410ts_remove(struct platform_device *pdev)
+{
+ free_irq(ts.irq_tc, ts.input);
+ del_timer_sync(&touch_timer);
+
+ clk_disable_unprepare(ts.clock);
+ clk_put(ts.clock);
+
+ input_unregister_device(ts.input);
+ iounmap(ts.io);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c2410ts_suspend(struct device *dev)
+{
+ writel(TSC_SLEEP, ts.io + S3C2410_ADCTSC);
+ disable_irq(ts.irq_tc);
+ clk_disable(ts.clock);
+
+ return 0;
+}
+
+static int s3c2410ts_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s3c2410_ts_mach_info *info = dev_get_platdata(&pdev->dev);
+
+ clk_enable(ts.clock);
+ enable_irq(ts.irq_tc);
+
+ /* Initialise registers */
+ if ((info->delay & 0xffff) > 0)
+ writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
+
+ writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+
+ return 0;
+}
+
+static const struct dev_pm_ops s3c_ts_pmops = {
+ .suspend = s3c2410ts_suspend,
+ .resume = s3c2410ts_resume,
+};
+#endif
+
+static struct platform_device_id s3cts_driver_ids[] = {
+ { "s3c2410-ts", 0 },
+ { "s3c2440-ts", 0 },
+ { "s3c64xx-ts", FEAT_PEN_IRQ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, s3cts_driver_ids);
+
+static struct platform_driver s3c_ts_driver = {
+ .driver = {
+ .name = "samsung-ts",
+#ifdef CONFIG_PM
+ .pm = &s3c_ts_pmops,
+#endif
+ },
+ .id_table = s3cts_driver_ids,
+ .probe = s3c2410ts_probe,
+ .remove = s3c2410ts_remove,
+};
+module_platform_driver(s3c_ts_driver);
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, "
+ "Ben Dooks <ben@simtec.co.uk>, "
+ "Simtec Electronics <linux@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C24XX Touchscreen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c
new file mode 100644
index 000000000..697e26e52
--- /dev/null
+++ b/drivers/input/touchscreen/st1232.c
@@ -0,0 +1,309 @@
+/*
+ * ST1232 Touchscreen Controller Driver
+ *
+ * Copyright (C) 2010 Renesas Solutions Corp.
+ * Tony SIM <chinyeow.sim.xt@renesas.com>
+ *
+ * Using code from:
+ * - android.git.kernel.org: projects/kernel/common.git: synaptics_i2c_rmi.c
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.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/platform_data/st1232_pdata.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
+
+struct st1232_ts_finger {
+ u16 x;
+ u16 y;
+ u8 t;
+ bool is_valid;
+};
+
+struct st1232_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct st1232_ts_finger finger[MAX_FINGERS];
+ struct dev_pm_qos_request low_latency_req;
+ int reset_gpio;
+};
+
+static int st1232_ts_read_data(struct st1232_ts_data *ts)
+{
+ struct st1232_ts_finger *finger = ts->finger;
+ struct i2c_client *client = ts->client;
+ struct i2c_msg msg[2];
+ int error;
+ u8 start_reg;
+ u8 buf[10];
+
+ /* read touchscreen data from ST1232 */
+ 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].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;
+
+ /* 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];
+ }
+
+ return 0;
+}
+
+static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
+{
+ struct st1232_ts_data *ts = dev_id;
+ struct st1232_ts_finger *finger = ts->finger;
+ struct input_dev *input_dev = ts->input_dev;
+ int count = 0;
+ int i, ret;
+
+ ret = st1232_ts_read_data(ts);
+ if (ret < 0)
+ goto end;
+
+ /* multi touch protocol */
+ for (i = 0; i < 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);
+ input_mt_sync(input_dev);
+ count++;
+ }
+
+ /* SYN_MT_REPORT only if no contact */
+ if (!count) {
+ input_mt_sync(input_dev);
+ if (ts->low_latency_req.dev) {
+ dev_pm_qos_remove_request(&ts->low_latency_req);
+ ts->low_latency_req.dev = NULL;
+ }
+ } else if (!ts->low_latency_req.dev) {
+ /* First contact, request 100 us latency. */
+ dev_pm_qos_add_ancestor_request(&ts->client->dev,
+ &ts->low_latency_req,
+ DEV_PM_QOS_RESUME_LATENCY, 100);
+ }
+
+ /* SYN_REPORT */
+ input_sync(input_dev);
+
+end:
+ return IRQ_HANDLED;
+}
+
+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);
+}
+
+static int st1232_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct st1232_ts_data *ts;
+ struct st1232_pdata *pdata = dev_get_platdata(&client->dev);
+ struct input_dev *input_dev;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "need I2C_FUNC_I2C\n");
+ return -EIO;
+ }
+
+ if (!client->irq) {
+ dev_err(&client->dev, "no IRQ?\n");
+ return -EINVAL;
+ }
+
+ ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!input_dev)
+ return -ENOMEM;
+
+ ts->client = client;
+ ts->input_dev = input_dev;
+
+ if (pdata)
+ ts->reset_gpio = pdata->reset_gpio;
+ else if (client->dev.of_node)
+ ts->reset_gpio = of_get_gpio(client->dev.of_node, 0);
+ else
+ ts->reset_gpio = -ENODEV;
+
+ 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;
+ }
+ }
+
+ st1232_ts_power(ts, true);
+
+ input_dev->name = "st1232-touchscreen";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_SYN, input_dev->evbit);
+ __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);
+
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, st1232_ts_irq_handler,
+ IRQF_ONESHOT,
+ client->name, ts);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ return error;
+ }
+
+ error = input_register_device(ts->input_dev);
+ if (error) {
+ dev_err(&client->dev, "Unable to register %s input device\n",
+ input_dev->name);
+ return error;
+ }
+
+ i2c_set_clientdata(client, ts);
+ device_init_wakeup(&client->dev, 1);
+
+ return 0;
+}
+
+static int st1232_ts_remove(struct i2c_client *client)
+{
+ struct st1232_ts_data *ts = i2c_get_clientdata(client);
+
+ device_init_wakeup(&client->dev, 0);
+ st1232_ts_power(ts, false);
+
+ return 0;
+}
+
+static int __maybe_unused st1232_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct st1232_ts_data *ts = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev)) {
+ enable_irq_wake(client->irq);
+ } else {
+ disable_irq(client->irq);
+ st1232_ts_power(ts, false);
+ }
+
+ return 0;
+}
+
+static int __maybe_unused st1232_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct st1232_ts_data *ts = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev)) {
+ disable_irq_wake(client->irq);
+ } else {
+ st1232_ts_power(ts, true);
+ enable_irq(client->irq);
+ }
+
+ return 0;
+}
+
+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 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, st1232_ts_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id st1232_ts_dt_ids[] = {
+ { .compatible = "sitronix,st1232", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids);
+#endif
+
+static struct i2c_driver st1232_ts_driver = {
+ .probe = st1232_ts_probe,
+ .remove = st1232_ts_remove,
+ .id_table = st1232_ts_id,
+ .driver = {
+ .name = ST1232_TS_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(st1232_ts_dt_ids),
+ .pm = &st1232_ts_pm_ops,
+ },
+};
+
+module_i2c_driver(st1232_ts_driver);
+
+MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>");
+MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c
new file mode 100644
index 000000000..e4c31256a
--- /dev/null
+++ b/drivers/input/touchscreen/stmpe-ts.c
@@ -0,0 +1,396 @@
+/*
+ * STMicroelectronics STMPE811 Touchscreen Driver
+ *
+ * (C) 2010 Luotao Fu <l.fu@pengutronix.de>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/stmpe.h>
+
+/* Register layouts and functionalities are identical on all stmpexxx variants
+ * with touchscreen controller
+ */
+#define STMPE_REG_INT_STA 0x0B
+#define STMPE_REG_ADC_CTRL1 0x20
+#define STMPE_REG_ADC_CTRL2 0x21
+#define STMPE_REG_TSC_CTRL 0x40
+#define STMPE_REG_TSC_CFG 0x41
+#define STMPE_REG_FIFO_TH 0x4A
+#define STMPE_REG_FIFO_STA 0x4B
+#define STMPE_REG_FIFO_SIZE 0x4C
+#define STMPE_REG_TSC_DATA_XYZ 0x52
+#define STMPE_REG_TSC_FRACTION_Z 0x56
+#define STMPE_REG_TSC_I_DRIVE 0x58
+
+#define OP_MOD_XYZ 0
+
+#define STMPE_TSC_CTRL_TSC_EN (1<<0)
+
+#define STMPE_FIFO_STA_RESET (1<<0)
+
+#define STMPE_IRQ_TOUCH_DET 0
+
+#define SAMPLE_TIME(x) ((x & 0xf) << 4)
+#define MOD_12B(x) ((x & 0x1) << 3)
+#define REF_SEL(x) ((x & 0x1) << 1)
+#define ADC_FREQ(x) (x & 0x3)
+#define AVE_CTRL(x) ((x & 0x3) << 6)
+#define DET_DELAY(x) ((x & 0x7) << 3)
+#define SETTLING(x) (x & 0x7)
+#define FRACTION_Z(x) (x & 0x7)
+#define I_DRIVE(x) (x & 0x1)
+#define OP_MODE(x) ((x & 0x7) << 1)
+
+#define STMPE_TS_NAME "stmpe-ts"
+#define XY_MASK 0xfff
+
+struct stmpe_touch {
+ struct stmpe *stmpe;
+ struct input_dev *idev;
+ struct delayed_work work;
+ struct device *dev;
+ u8 sample_time;
+ u8 mod_12b;
+ u8 ref_sel;
+ u8 adc_freq;
+ u8 ave_ctrl;
+ u8 touch_det_delay;
+ u8 settling;
+ u8 fraction_z;
+ u8 i_drive;
+};
+
+static int __stmpe_reset_fifo(struct stmpe *stmpe)
+{
+ int ret;
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA,
+ STMPE_FIFO_STA_RESET, STMPE_FIFO_STA_RESET);
+ if (ret)
+ return ret;
+
+ return stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA,
+ STMPE_FIFO_STA_RESET, 0);
+}
+
+static void stmpe_work(struct work_struct *work)
+{
+ int int_sta;
+ u32 timeout = 40;
+
+ struct stmpe_touch *ts =
+ container_of(work, struct stmpe_touch, work.work);
+
+ int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA);
+
+ /*
+ * touch_det sometimes get desasserted or just get stuck. This appears
+ * to be a silicon bug, We still have to clearify this with the
+ * manufacture. As a workaround We release the key anyway if the
+ * touch_det keeps coming in after 4ms, while the FIFO contains no value
+ * during the whole time.
+ */
+ while ((int_sta & (1 << STMPE_IRQ_TOUCH_DET)) && (timeout > 0)) {
+ timeout--;
+ int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA);
+ udelay(100);
+ }
+
+ /* reset the FIFO before we report release event */
+ __stmpe_reset_fifo(ts->stmpe);
+
+ input_report_abs(ts->idev, ABS_PRESSURE, 0);
+ input_report_key(ts->idev, BTN_TOUCH, 0);
+ input_sync(ts->idev);
+}
+
+static irqreturn_t stmpe_ts_handler(int irq, void *data)
+{
+ u8 data_set[4];
+ int x, y, z;
+ struct stmpe_touch *ts = data;
+
+ /*
+ * Cancel scheduled polling for release if we have new value
+ * available. Wait if the polling is already running.
+ */
+ cancel_delayed_work_sync(&ts->work);
+
+ /*
+ * The FIFO sometimes just crashes and stops generating interrupts. This
+ * appears to be a silicon bug. We still have to clearify this with
+ * the manufacture. As a workaround we disable the TSC while we are
+ * collecting data and flush the FIFO after reading
+ */
+ stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, 0);
+
+ stmpe_block_read(ts->stmpe, STMPE_REG_TSC_DATA_XYZ, 4, data_set);
+
+ x = (data_set[0] << 4) | (data_set[1] >> 4);
+ y = ((data_set[1] & 0xf) << 8) | data_set[2];
+ z = data_set[3];
+
+ input_report_abs(ts->idev, ABS_X, x);
+ input_report_abs(ts->idev, ABS_Y, y);
+ input_report_abs(ts->idev, ABS_PRESSURE, z);
+ input_report_key(ts->idev, BTN_TOUCH, 1);
+ input_sync(ts->idev);
+
+ /* flush the FIFO after we have read out our values. */
+ __stmpe_reset_fifo(ts->stmpe);
+
+ /* reenable the tsc */
+ stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
+
+ /* start polling for touch_det to detect release */
+ schedule_delayed_work(&ts->work, msecs_to_jiffies(50));
+
+ return IRQ_HANDLED;
+}
+
+static int stmpe_init_hw(struct stmpe_touch *ts)
+{
+ int ret;
+ u8 adc_ctrl1, adc_ctrl1_mask, tsc_cfg, tsc_cfg_mask;
+ struct stmpe *stmpe = ts->stmpe;
+ struct device *dev = ts->dev;
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_TOUCHSCREEN | STMPE_BLOCK_ADC);
+ if (ret) {
+ dev_err(dev, "Could not enable clock for ADC and TS\n");
+ return ret;
+ }
+
+ adc_ctrl1 = SAMPLE_TIME(ts->sample_time) | MOD_12B(ts->mod_12b) |
+ REF_SEL(ts->ref_sel);
+ adc_ctrl1_mask = SAMPLE_TIME(0xff) | MOD_12B(0xff) | REF_SEL(0xff);
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL1,
+ adc_ctrl1_mask, adc_ctrl1);
+ if (ret) {
+ dev_err(dev, "Could not setup ADC\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL2,
+ ADC_FREQ(0xff), ADC_FREQ(ts->adc_freq));
+ if (ret) {
+ dev_err(dev, "Could not setup ADC\n");
+ return ret;
+ }
+
+ tsc_cfg = AVE_CTRL(ts->ave_ctrl) | DET_DELAY(ts->touch_det_delay) |
+ SETTLING(ts->settling);
+ tsc_cfg_mask = AVE_CTRL(0xff) | DET_DELAY(0xff) | SETTLING(0xff);
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CFG, tsc_cfg_mask, tsc_cfg);
+ if (ret) {
+ dev_err(dev, "Could not config touch\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_FRACTION_Z,
+ FRACTION_Z(0xff), FRACTION_Z(ts->fraction_z));
+ if (ret) {
+ dev_err(dev, "Could not config touch\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_I_DRIVE,
+ I_DRIVE(0xff), I_DRIVE(ts->i_drive));
+ if (ret) {
+ dev_err(dev, "Could not config touch\n");
+ return ret;
+ }
+
+ /* set FIFO to 1 for single point reading */
+ ret = stmpe_reg_write(stmpe, STMPE_REG_FIFO_TH, 1);
+ if (ret) {
+ dev_err(dev, "Could not set FIFO\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CTRL,
+ OP_MODE(0xff), OP_MODE(OP_MOD_XYZ));
+ if (ret) {
+ dev_err(dev, "Could not set mode\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stmpe_ts_open(struct input_dev *dev)
+{
+ struct stmpe_touch *ts = input_get_drvdata(dev);
+ int ret = 0;
+
+ ret = __stmpe_reset_fifo(ts->stmpe);
+ if (ret)
+ return ret;
+
+ return stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
+}
+
+static void stmpe_ts_close(struct input_dev *dev)
+{
+ struct stmpe_touch *ts = input_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&ts->work);
+
+ stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, 0);
+}
+
+static void stmpe_ts_get_platform_info(struct platform_device *pdev,
+ struct stmpe_touch *ts)
+{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+ struct device_node *np = pdev->dev.of_node;
+ struct stmpe_ts_platform_data *ts_pdata = NULL;
+
+ ts->stmpe = stmpe;
+
+ if (stmpe->pdata && stmpe->pdata->ts) {
+ ts_pdata = stmpe->pdata->ts;
+
+ ts->sample_time = ts_pdata->sample_time;
+ ts->mod_12b = ts_pdata->mod_12b;
+ ts->ref_sel = ts_pdata->ref_sel;
+ ts->adc_freq = ts_pdata->adc_freq;
+ ts->ave_ctrl = ts_pdata->ave_ctrl;
+ ts->touch_det_delay = ts_pdata->touch_det_delay;
+ ts->settling = ts_pdata->settling;
+ ts->fraction_z = ts_pdata->fraction_z;
+ ts->i_drive = ts_pdata->i_drive;
+ } else if (np) {
+ u32 val;
+
+ if (!of_property_read_u32(np, "st,sample-time", &val))
+ ts->sample_time = val;
+ if (!of_property_read_u32(np, "st,mod-12b", &val))
+ ts->mod_12b = val;
+ if (!of_property_read_u32(np, "st,ref-sel", &val))
+ ts->ref_sel = val;
+ if (!of_property_read_u32(np, "st,adc-freq", &val))
+ ts->adc_freq = val;
+ if (!of_property_read_u32(np, "st,ave-ctrl", &val))
+ ts->ave_ctrl = val;
+ if (!of_property_read_u32(np, "st,touch-det-delay", &val))
+ ts->touch_det_delay = val;
+ if (!of_property_read_u32(np, "st,settling", &val))
+ ts->settling = val;
+ if (!of_property_read_u32(np, "st,fraction-z", &val))
+ ts->fraction_z = val;
+ if (!of_property_read_u32(np, "st,i-drive", &val))
+ ts->i_drive = val;
+ }
+}
+
+static int stmpe_input_probe(struct platform_device *pdev)
+{
+ struct stmpe_touch *ts;
+ struct input_dev *idev;
+ int error;
+ int ts_irq;
+
+ ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");
+ if (ts_irq < 0)
+ return ts_irq;
+
+ ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ idev = devm_input_allocate_device(&pdev->dev);
+ if (!idev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ts);
+ ts->idev = idev;
+ ts->dev = &pdev->dev;
+
+ stmpe_ts_get_platform_info(pdev, ts);
+
+ INIT_DELAYED_WORK(&ts->work, stmpe_work);
+
+ error = devm_request_threaded_irq(&pdev->dev, ts_irq,
+ NULL, stmpe_ts_handler,
+ IRQF_ONESHOT, STMPE_TS_NAME, ts);
+ if (error) {
+ dev_err(&pdev->dev, "Failed to request IRQ %d\n", ts_irq);
+ return error;
+ }
+
+ error = stmpe_init_hw(ts);
+ if (error)
+ return error;
+
+ idev->name = STMPE_TS_NAME;
+ idev->phys = STMPE_TS_NAME"/input0";
+ idev->id.bustype = BUS_I2C;
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ idev->open = stmpe_ts_open;
+ idev->close = stmpe_ts_close;
+
+ input_set_drvdata(idev, ts);
+
+ input_set_abs_params(idev, ABS_X, 0, XY_MASK, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, XY_MASK, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0x0, 0xff, 0, 0);
+
+ error = input_register_device(idev);
+ if (error) {
+ dev_err(&pdev->dev, "Could not register input device\n");
+ return error;
+ }
+
+ return 0;
+}
+
+static int stmpe_ts_remove(struct platform_device *pdev)
+{
+ struct stmpe_touch *ts = platform_get_drvdata(pdev);
+
+ stmpe_disable(ts->stmpe, STMPE_BLOCK_TOUCHSCREEN);
+
+ return 0;
+}
+
+static struct platform_driver stmpe_ts_driver = {
+ .driver = {
+ .name = STMPE_TS_NAME,
+ },
+ .probe = stmpe_input_probe,
+ .remove = stmpe_ts_remove,
+};
+module_platform_driver(stmpe_ts_driver);
+
+MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
+MODULE_DESCRIPTION("STMPEXXX touchscreen driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" STMPE_TS_NAME);
diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c
new file mode 100644
index 000000000..c01169940
--- /dev/null
+++ b/drivers/input/touchscreen/sun4i-ts.c
@@ -0,0 +1,426 @@
+/*
+ * Allwinner sunxi resistive touchscreen controller driver
+ *
+ * Copyright (C) 2013 - 2014 Hans de Goede <hdegoede@redhat.com>
+ *
+ * The hwmon parts are based on work by Corentin LABBE which is:
+ * Copyright (C) 2013 Corentin LABBE <clabbe.montjoie@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * The sun4i-ts controller is capable of detecting a second touch, but when a
+ * second touch is present then the accuracy becomes so bad the reported touch
+ * location is not useable.
+ *
+ * The original android driver contains some complicated heuristics using the
+ * aprox. distance between the 2 touches to see if the user is making a pinch
+ * open / close movement, and then reports emulated multi-touch events around
+ * the last touch coordinate (as the dual-touch coordinates are worthless).
+ *
+ * These kinds of heuristics are just asking for trouble (and don't belong
+ * in the kernel). So this driver offers straight forward, reliable single
+ * touch functionality only.
+ *
+ * s.a. A20 User Manual "1.15 TP" (Documentation/arm/sunxi/README)
+ * (looks like the description in the A20 User Manual v1.3 is better
+ * than the one in the A10 User Manual v.1.5)
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/thermal.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define TP_CTRL0 0x00
+#define TP_CTRL1 0x04
+#define TP_CTRL2 0x08
+#define TP_CTRL3 0x0c
+#define TP_INT_FIFOC 0x10
+#define TP_INT_FIFOS 0x14
+#define TP_TPR 0x18
+#define TP_CDAT 0x1c
+#define TEMP_DATA 0x20
+#define TP_DATA 0x24
+
+/* TP_CTRL0 bits */
+#define ADC_FIRST_DLY(x) ((x) << 24) /* 8 bits */
+#define ADC_FIRST_DLY_MODE(x) ((x) << 23)
+#define ADC_CLK_SEL(x) ((x) << 22)
+#define ADC_CLK_DIV(x) ((x) << 20) /* 3 bits */
+#define FS_DIV(x) ((x) << 16) /* 4 bits */
+#define T_ACQ(x) ((x) << 0) /* 16 bits */
+
+/* TP_CTRL1 bits */
+#define STYLUS_UP_DEBOUN(x) ((x) << 12) /* 8 bits */
+#define STYLUS_UP_DEBOUN_EN(x) ((x) << 9)
+#define TOUCH_PAN_CALI_EN(x) ((x) << 6)
+#define TP_DUAL_EN(x) ((x) << 5)
+#define TP_MODE_EN(x) ((x) << 4)
+#define TP_ADC_SELECT(x) ((x) << 3)
+#define ADC_CHAN_SELECT(x) ((x) << 0) /* 3 bits */
+
+/* on sun6i, bits 3~6 are left shifted by 1 to 4~7 */
+#define SUN6I_TP_MODE_EN(x) ((x) << 5)
+
+/* TP_CTRL2 bits */
+#define TP_SENSITIVE_ADJUST(x) ((x) << 28) /* 4 bits */
+#define TP_MODE_SELECT(x) ((x) << 26) /* 2 bits */
+#define PRE_MEA_EN(x) ((x) << 24)
+#define PRE_MEA_THRE_CNT(x) ((x) << 0) /* 24 bits */
+
+/* TP_CTRL3 bits */
+#define FILTER_EN(x) ((x) << 2)
+#define FILTER_TYPE(x) ((x) << 0) /* 2 bits */
+
+/* TP_INT_FIFOC irq and fifo mask / control bits */
+#define TEMP_IRQ_EN(x) ((x) << 18)
+#define OVERRUN_IRQ_EN(x) ((x) << 17)
+#define DATA_IRQ_EN(x) ((x) << 16)
+#define TP_DATA_XY_CHANGE(x) ((x) << 13)
+#define FIFO_TRIG(x) ((x) << 8) /* 5 bits */
+#define DATA_DRQ_EN(x) ((x) << 7)
+#define FIFO_FLUSH(x) ((x) << 4)
+#define TP_UP_IRQ_EN(x) ((x) << 1)
+#define TP_DOWN_IRQ_EN(x) ((x) << 0)
+
+/* TP_INT_FIFOS irq and fifo status bits */
+#define TEMP_DATA_PENDING BIT(18)
+#define FIFO_OVERRUN_PENDING BIT(17)
+#define FIFO_DATA_PENDING BIT(16)
+#define TP_IDLE_FLG BIT(2)
+#define TP_UP_PENDING BIT(1)
+#define TP_DOWN_PENDING BIT(0)
+
+/* TP_TPR bits */
+#define TEMP_ENABLE(x) ((x) << 16)
+#define TEMP_PERIOD(x) ((x) << 0) /* t = x * 256 * 16 / clkin */
+
+struct sun4i_ts_data {
+ struct device *dev;
+ struct input_dev *input;
+ struct thermal_zone_device *tz;
+ void __iomem *base;
+ unsigned int irq;
+ bool ignore_fifo_data;
+ int temp_data;
+ int temp_offset;
+ int temp_step;
+};
+
+static void sun4i_ts_irq_handle_input(struct sun4i_ts_data *ts, u32 reg_val)
+{
+ u32 x, y;
+
+ if (reg_val & FIFO_DATA_PENDING) {
+ x = readl(ts->base + TP_DATA);
+ y = readl(ts->base + TP_DATA);
+ /* The 1st location reported after an up event is unreliable */
+ if (!ts->ignore_fifo_data) {
+ input_report_abs(ts->input, ABS_X, x);
+ input_report_abs(ts->input, ABS_Y, y);
+ /*
+ * The hardware has a separate down status bit, but
+ * that gets set before we get the first location,
+ * resulting in reporting a click on the old location.
+ */
+ input_report_key(ts->input, BTN_TOUCH, 1);
+ input_sync(ts->input);
+ } else {
+ ts->ignore_fifo_data = false;
+ }
+ }
+
+ if (reg_val & TP_UP_PENDING) {
+ ts->ignore_fifo_data = true;
+ input_report_key(ts->input, BTN_TOUCH, 0);
+ input_sync(ts->input);
+ }
+}
+
+static irqreturn_t sun4i_ts_irq(int irq, void *dev_id)
+{
+ struct sun4i_ts_data *ts = dev_id;
+ u32 reg_val;
+
+ reg_val = readl(ts->base + TP_INT_FIFOS);
+
+ if (reg_val & TEMP_DATA_PENDING)
+ ts->temp_data = readl(ts->base + TEMP_DATA);
+
+ if (ts->input)
+ sun4i_ts_irq_handle_input(ts, reg_val);
+
+ writel(reg_val, ts->base + TP_INT_FIFOS);
+
+ return IRQ_HANDLED;
+}
+
+static int sun4i_ts_open(struct input_dev *dev)
+{
+ struct sun4i_ts_data *ts = input_get_drvdata(dev);
+
+ /* Flush, set trig level to 1, enable temp, data and up irqs */
+ writel(TEMP_IRQ_EN(1) | DATA_IRQ_EN(1) | FIFO_TRIG(1) | FIFO_FLUSH(1) |
+ TP_UP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
+
+ return 0;
+}
+
+static void sun4i_ts_close(struct input_dev *dev)
+{
+ struct sun4i_ts_data *ts = input_get_drvdata(dev);
+
+ /* Deactivate all input IRQs */
+ writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
+}
+
+static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp)
+{
+ /* No temp_data until the first irq */
+ if (ts->temp_data == -1)
+ return -EAGAIN;
+
+ *temp = ts->temp_data * ts->temp_step - ts->temp_offset;
+
+ return 0;
+}
+
+static int sun4i_get_tz_temp(void *data, long *temp)
+{
+ return sun4i_get_temp(data, temp);
+}
+
+static struct thermal_zone_of_device_ops sun4i_ts_tz_ops = {
+ .get_temp = sun4i_get_tz_temp,
+};
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sun4i_ts_data *ts = dev_get_drvdata(dev);
+ long temp;
+ int error;
+
+ error = sun4i_get_temp(ts, &temp);
+ if (error)
+ return error;
+
+ return sprintf(buf, "%ld\n", temp);
+}
+
+static ssize_t show_temp_label(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "SoC temperature\n");
+}
+
+static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
+static DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL);
+
+static struct attribute *sun4i_ts_attrs[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_label.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(sun4i_ts);
+
+static int sun4i_ts_probe(struct platform_device *pdev)
+{
+ struct sun4i_ts_data *ts;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device *hwmon;
+ int error;
+ u32 reg;
+ bool ts_attached;
+ u32 tp_sensitive_adjust = 15;
+ u32 filter_type = 1;
+
+ ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ ts->dev = dev;
+ ts->ignore_fifo_data = true;
+ ts->temp_data = -1;
+ if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) {
+ /* Allwinner SDK has temperature (C) = (value / 6) - 271 */
+ ts->temp_offset = 271000;
+ ts->temp_step = 167;
+ } else if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) {
+ /*
+ * The A10 temperature sensor has quite a wide spread, these
+ * parameters are based on the averaging of the calibration
+ * results of 4 completely different boards, with a spread of
+ * temp_step from 0.096 - 0.170 and temp_offset from 176 - 331.
+ */
+ ts->temp_offset = 257000;
+ ts->temp_step = 133;
+ } else {
+ /*
+ * The user manuals do not contain the formula for calculating
+ * the temperature. The formula used here is from the AXP209,
+ * which is designed by X-Powers, an affiliate of Allwinner:
+ *
+ * temperature (C) = (value * 0.1) - 144.7
+ *
+ * Allwinner does not have any documentation whatsoever for
+ * this hardware. Moreover, it is claimed that the sensor
+ * is inaccurate and cannot work properly.
+ */
+ ts->temp_offset = 144700;
+ ts->temp_step = 100;
+ }
+
+ ts_attached = of_property_read_bool(np, "allwinner,ts-attached");
+ if (ts_attached) {
+ ts->input = devm_input_allocate_device(dev);
+ if (!ts->input)
+ return -ENOMEM;
+
+ ts->input->name = pdev->name;
+ ts->input->phys = "sun4i_ts/input0";
+ ts->input->open = sun4i_ts_open;
+ ts->input->close = sun4i_ts_close;
+ ts->input->id.bustype = BUS_HOST;
+ ts->input->id.vendor = 0x0001;
+ ts->input->id.product = 0x0001;
+ ts->input->id.version = 0x0100;
+ ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
+ __set_bit(BTN_TOUCH, ts->input->keybit);
+ input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0);
+ input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0);
+ input_set_drvdata(ts->input, ts);
+ }
+
+ ts->base = devm_ioremap_resource(dev,
+ platform_get_resource(pdev, IORESOURCE_MEM, 0));
+ if (IS_ERR(ts->base))
+ return PTR_ERR(ts->base);
+
+ ts->irq = platform_get_irq(pdev, 0);
+ error = devm_request_irq(dev, ts->irq, sun4i_ts_irq, 0, "sun4i-ts", ts);
+ if (error)
+ return error;
+
+ /*
+ * Select HOSC clk, clkin = clk / 6, adc samplefreq = clkin / 8192,
+ * t_acq = clkin / (16 * 64)
+ */
+ writel(ADC_CLK_SEL(0) | ADC_CLK_DIV(2) | FS_DIV(7) | T_ACQ(63),
+ ts->base + TP_CTRL0);
+
+ /*
+ * tp_sensitive_adjust is an optional property
+ * tp_mode = 0 : only x and y coordinates, as we don't use dual touch
+ */
+ of_property_read_u32(np, "allwinner,tp-sensitive-adjust",
+ &tp_sensitive_adjust);
+ writel(TP_SENSITIVE_ADJUST(tp_sensitive_adjust) | TP_MODE_SELECT(0),
+ ts->base + TP_CTRL2);
+
+ /*
+ * Enable median and averaging filter, optional property for
+ * filter type.
+ */
+ of_property_read_u32(np, "allwinner,filter-type", &filter_type);
+ writel(FILTER_EN(1) | FILTER_TYPE(filter_type), ts->base + TP_CTRL3);
+
+ /* Enable temperature measurement, period 1953 (2 seconds) */
+ writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR);
+
+ /*
+ * Set stylus up debounce to aprox 10 ms, enable debounce, and
+ * finally enable tp mode.
+ */
+ reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1);
+ if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts"))
+ reg |= SUN6I_TP_MODE_EN(1);
+ else
+ reg |= TP_MODE_EN(1);
+ writel(reg, ts->base + TP_CTRL1);
+
+ /*
+ * The thermal core does not register hwmon devices for DT-based
+ * thermal zone sensors, such as this one.
+ */
+ hwmon = devm_hwmon_device_register_with_groups(ts->dev, "sun4i_ts",
+ ts, sun4i_ts_groups);
+ if (IS_ERR(hwmon))
+ return PTR_ERR(hwmon);
+
+ ts->tz = thermal_zone_of_sensor_register(ts->dev, 0, ts,
+ &sun4i_ts_tz_ops);
+ if (IS_ERR(ts->tz))
+ ts->tz = NULL;
+
+ writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
+
+ if (ts_attached) {
+ error = input_register_device(ts->input);
+ if (error) {
+ writel(0, ts->base + TP_INT_FIFOC);
+ thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
+ return error;
+ }
+ }
+
+ platform_set_drvdata(pdev, ts);
+ return 0;
+}
+
+static int sun4i_ts_remove(struct platform_device *pdev)
+{
+ struct sun4i_ts_data *ts = platform_get_drvdata(pdev);
+
+ /* Explicit unregister to avoid open/close changing the imask later */
+ if (ts->input)
+ input_unregister_device(ts->input);
+
+ thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
+
+ /* Deactivate all IRQs */
+ writel(0, ts->base + TP_INT_FIFOC);
+
+ return 0;
+}
+
+static const struct of_device_id sun4i_ts_of_match[] = {
+ { .compatible = "allwinner,sun4i-a10-ts", },
+ { .compatible = "allwinner,sun5i-a13-ts", },
+ { .compatible = "allwinner,sun6i-a31-ts", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sun4i_ts_of_match);
+
+static struct platform_driver sun4i_ts_driver = {
+ .driver = {
+ .name = "sun4i-ts",
+ .of_match_table = of_match_ptr(sun4i_ts_of_match),
+ },
+ .probe = sun4i_ts_probe,
+ .remove = sun4i_ts_remove,
+};
+
+module_platform_driver(sun4i_ts_driver);
+
+MODULE_DESCRIPTION("Allwinner sun4i resistive touchscreen controller driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
new file mode 100644
index 000000000..a24eba5ea
--- /dev/null
+++ b/drivers/input/touchscreen/sur40.c
@@ -0,0 +1,876 @@
+/*
+ * Surface2.0/SUR40/PixelSense input driver
+ *
+ * Copyright (c) 2014 by Florian 'floe' Echtler <floe@butterbrot.org>
+ *
+ * Derived from the USB Skeleton driver 1.1,
+ * Copyright (c) 2003 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * and from the Apple USB BCM5974 multitouch driver,
+ * Copyright (c) 2008 Henrik Rydberg (rydberg@euromail.se)
+ *
+ * and from the generic hid-multitouch driver,
+ * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr>
+ *
+ * and from the v4l2-pci-skeleton driver,
+ * Copyright (c) Copyright 2014 Cisco Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/printk.h>
+#include <linux/input-polldev.h>
+#include <linux/input/mt.h>
+#include <linux/usb/input.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-sg.h>
+
+/* read 512 bytes from endpoint 0x86 -> get header + blobs */
+struct sur40_header {
+
+ __le16 type; /* always 0x0001 */
+ __le16 count; /* count of blobs (if 0: continue prev. packet) */
+
+ __le32 packet_id; /* unique ID for all packets in one frame */
+
+ __le32 timestamp; /* milliseconds (inc. by 16 or 17 each frame) */
+ __le32 unknown; /* "epoch?" always 02/03 00 00 00 */
+
+} __packed;
+
+struct sur40_blob {
+
+ __le16 blob_id;
+
+ u8 action; /* 0x02 = enter/exit, 0x03 = update (?) */
+ u8 unknown; /* always 0x01 or 0x02 (no idea what this is?) */
+
+ __le16 bb_pos_x; /* upper left corner of bounding box */
+ __le16 bb_pos_y;
+
+ __le16 bb_size_x; /* size of bounding box */
+ __le16 bb_size_y;
+
+ __le16 pos_x; /* finger tip position */
+ __le16 pos_y;
+
+ __le16 ctr_x; /* centroid position */
+ __le16 ctr_y;
+
+ __le16 axis_x; /* somehow related to major/minor axis, mostly: */
+ __le16 axis_y; /* axis_x == bb_size_y && axis_y == bb_size_x */
+
+ __le32 angle; /* orientation in radians relative to x axis -
+ actually an IEEE754 float, don't use in kernel */
+
+ __le32 area; /* size in pixels/pressure (?) */
+
+ u8 padding[32];
+
+} __packed;
+
+/* combined header/blob data */
+struct sur40_data {
+ struct sur40_header header;
+ struct sur40_blob blobs[];
+} __packed;
+
+/* read 512 bytes from endpoint 0x82 -> get header below
+ * continue reading 16k blocks until header.size bytes read */
+struct sur40_image_header {
+ __le32 magic; /* "SUBF" */
+ __le32 packet_id;
+ __le32 size; /* always 0x0007e900 = 960x540 */
+ __le32 timestamp; /* milliseconds (increases by 16 or 17 each frame) */
+ __le32 unknown; /* "epoch?" always 02/03 00 00 00 */
+} __packed;
+
+/* version information */
+#define DRIVER_SHORT "sur40"
+#define DRIVER_LONG "Samsung SUR40"
+#define DRIVER_AUTHOR "Florian 'floe' Echtler <floe@butterbrot.org>"
+#define DRIVER_DESC "Surface2.0/SUR40/PixelSense input driver"
+
+/* vendor and device IDs */
+#define ID_MICROSOFT 0x045e
+#define ID_SUR40 0x0775
+
+/* sensor resolution */
+#define SENSOR_RES_X 1920
+#define SENSOR_RES_Y 1080
+
+/* touch data endpoint */
+#define TOUCH_ENDPOINT 0x86
+
+/* video data endpoint */
+#define VIDEO_ENDPOINT 0x82
+
+/* video header fields */
+#define VIDEO_HEADER_MAGIC 0x46425553
+#define VIDEO_PACKET_SIZE 16384
+
+/* polling interval (ms) */
+#define POLL_INTERVAL 10
+
+/* maximum number of contacts FIXME: this is a guess? */
+#define MAX_CONTACTS 64
+
+/* control commands */
+#define SUR40_GET_VERSION 0xb0 /* 12 bytes string */
+#define SUR40_UNKNOWN1 0xb3 /* 5 bytes */
+#define SUR40_UNKNOWN2 0xc1 /* 24 bytes */
+
+#define SUR40_GET_STATE 0xc5 /* 4 bytes state (?) */
+#define SUR40_GET_SENSORS 0xb1 /* 8 bytes sensors */
+
+/* master device state */
+struct sur40_state {
+
+ struct usb_device *usbdev;
+ struct device *dev;
+ struct input_polled_dev *input;
+
+ struct v4l2_device v4l2;
+ struct video_device vdev;
+ struct mutex lock;
+
+ struct vb2_queue queue;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct list_head buf_list;
+ spinlock_t qlock;
+ int sequence;
+
+ struct sur40_data *bulk_in_buffer;
+ size_t bulk_in_size;
+ u8 bulk_in_epaddr;
+
+ char phys[64];
+};
+
+struct sur40_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+/* forward declarations */
+static const struct video_device sur40_video_device;
+static const struct v4l2_pix_format sur40_video_format;
+static const struct vb2_queue sur40_queue;
+static void sur40_process_video(struct sur40_state *sur40);
+
+/*
+ * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT
+ * here by mistake which is very likely to have corrupted the firmware EEPROM
+ * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug.
+ * Should you ever run into a similar problem, the background story to this
+ * incident and instructions on how to fix the corrupted EEPROM are available
+ * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html
+*/
+
+/* command wrapper */
+static int sur40_command(struct sur40_state *dev,
+ u8 command, u16 index, void *buffer, u16 size)
+{
+ return usb_control_msg(dev->usbdev, usb_rcvctrlpipe(dev->usbdev, 0),
+ command,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00, index, buffer, size, 1000);
+}
+
+/* Initialization routine, called from sur40_open */
+static int sur40_init(struct sur40_state *dev)
+{
+ int result;
+ u8 buffer[24];
+
+ /* stupidly replay the original MS driver init sequence */
+ result = sur40_command(dev, SUR40_GET_VERSION, 0x00, buffer, 12);
+ if (result < 0)
+ return result;
+
+ result = sur40_command(dev, SUR40_GET_VERSION, 0x01, buffer, 12);
+ if (result < 0)
+ return result;
+
+ result = sur40_command(dev, SUR40_GET_VERSION, 0x02, buffer, 12);
+ if (result < 0)
+ return result;
+
+ result = sur40_command(dev, SUR40_UNKNOWN2, 0x00, buffer, 24);
+ if (result < 0)
+ return result;
+
+ result = sur40_command(dev, SUR40_UNKNOWN1, 0x00, buffer, 5);
+ if (result < 0)
+ return result;
+
+ result = sur40_command(dev, SUR40_GET_VERSION, 0x03, buffer, 12);
+
+ /*
+ * Discard the result buffer - no known data inside except
+ * some version strings, maybe extract these sometime...
+ */
+
+ return result;
+}
+
+/*
+ * Callback routines from input_polled_dev
+ */
+
+/* Enable the device, polling will now start. */
+static void sur40_open(struct input_polled_dev *polldev)
+{
+ struct sur40_state *sur40 = polldev->private;
+
+ dev_dbg(sur40->dev, "open\n");
+ sur40_init(sur40);
+}
+
+/* Disable device, polling has stopped. */
+static void sur40_close(struct input_polled_dev *polldev)
+{
+ struct sur40_state *sur40 = polldev->private;
+
+ dev_dbg(sur40->dev, "close\n");
+ /*
+ * There is no known way to stop the device, so we simply
+ * stop polling.
+ */
+}
+
+/*
+ * This function is called when a whole contact has been processed,
+ * so that it can assign it to a slot and store the data there.
+ */
+static void sur40_report_blob(struct sur40_blob *blob, struct input_dev *input)
+{
+ int wide, major, minor;
+
+ int bb_size_x = le16_to_cpu(blob->bb_size_x);
+ int bb_size_y = le16_to_cpu(blob->bb_size_y);
+
+ int pos_x = le16_to_cpu(blob->pos_x);
+ int pos_y = le16_to_cpu(blob->pos_y);
+
+ int ctr_x = le16_to_cpu(blob->ctr_x);
+ int ctr_y = le16_to_cpu(blob->ctr_y);
+
+ int slotnum = input_mt_get_slot_by_key(input, blob->blob_id);
+ if (slotnum < 0 || slotnum >= MAX_CONTACTS)
+ return;
+
+ input_mt_slot(input, slotnum);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, 1);
+ wide = (bb_size_x > bb_size_y);
+ major = max(bb_size_x, bb_size_y);
+ minor = min(bb_size_x, bb_size_y);
+
+ input_report_abs(input, ABS_MT_POSITION_X, pos_x);
+ input_report_abs(input, ABS_MT_POSITION_Y, pos_y);
+ input_report_abs(input, ABS_MT_TOOL_X, ctr_x);
+ input_report_abs(input, ABS_MT_TOOL_Y, ctr_y);
+
+ /* TODO: use a better orientation measure */
+ input_report_abs(input, ABS_MT_ORIENTATION, wide);
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
+ input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
+}
+
+/* core function: poll for new input data */
+static void sur40_poll(struct input_polled_dev *polldev)
+{
+ struct sur40_state *sur40 = polldev->private;
+ struct input_dev *input = polldev->input;
+ int result, bulk_read, need_blobs, packet_blobs, i;
+ u32 uninitialized_var(packet_id);
+
+ struct sur40_header *header = &sur40->bulk_in_buffer->header;
+ struct sur40_blob *inblob = &sur40->bulk_in_buffer->blobs[0];
+
+ dev_dbg(sur40->dev, "poll\n");
+
+ need_blobs = -1;
+
+ do {
+
+ /* perform a blocking bulk read to get data from the device */
+ result = usb_bulk_msg(sur40->usbdev,
+ usb_rcvbulkpipe(sur40->usbdev, sur40->bulk_in_epaddr),
+ sur40->bulk_in_buffer, sur40->bulk_in_size,
+ &bulk_read, 1000);
+
+ dev_dbg(sur40->dev, "received %d bytes\n", bulk_read);
+
+ if (result < 0) {
+ dev_err(sur40->dev, "error in usb_bulk_read\n");
+ return;
+ }
+
+ result = bulk_read - sizeof(struct sur40_header);
+
+ if (result % sizeof(struct sur40_blob) != 0) {
+ dev_err(sur40->dev, "transfer size mismatch\n");
+ return;
+ }
+
+ /* first packet? */
+ if (need_blobs == -1) {
+ need_blobs = le16_to_cpu(header->count);
+ dev_dbg(sur40->dev, "need %d blobs\n", need_blobs);
+ packet_id = le32_to_cpu(header->packet_id);
+ }
+
+ /*
+ * Sanity check. when video data is also being retrieved, the
+ * packet ID will usually increase in the middle of a series
+ * instead of at the end.
+ */
+ if (packet_id != header->packet_id)
+ dev_warn(sur40->dev, "packet ID mismatch\n");
+
+ packet_blobs = result / sizeof(struct sur40_blob);
+ dev_dbg(sur40->dev, "received %d blobs\n", packet_blobs);
+
+ /* packets always contain at least 4 blobs, even if empty */
+ if (packet_blobs > need_blobs)
+ packet_blobs = need_blobs;
+
+ for (i = 0; i < packet_blobs; i++) {
+ need_blobs--;
+ dev_dbg(sur40->dev, "processing blob\n");
+ sur40_report_blob(&(inblob[i]), input);
+ }
+
+ } while (need_blobs > 0);
+
+ input_mt_sync_frame(input);
+ input_sync(input);
+
+ sur40_process_video(sur40);
+}
+
+/* deal with video data */
+static void sur40_process_video(struct sur40_state *sur40)
+{
+
+ struct sur40_image_header *img = (void *)(sur40->bulk_in_buffer);
+ struct sur40_buffer *new_buf;
+ struct usb_sg_request sgr;
+ struct sg_table *sgt;
+ int result, bulk_read;
+
+ if (!vb2_start_streaming_called(&sur40->queue))
+ return;
+
+ /* get a new buffer from the list */
+ spin_lock(&sur40->qlock);
+ if (list_empty(&sur40->buf_list)) {
+ dev_dbg(sur40->dev, "buffer queue empty\n");
+ spin_unlock(&sur40->qlock);
+ return;
+ }
+ new_buf = list_entry(sur40->buf_list.next, struct sur40_buffer, list);
+ list_del(&new_buf->list);
+ spin_unlock(&sur40->qlock);
+
+ /* retrieve data via bulk read */
+ result = usb_bulk_msg(sur40->usbdev,
+ usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT),
+ sur40->bulk_in_buffer, sur40->bulk_in_size,
+ &bulk_read, 1000);
+
+ if (result < 0) {
+ dev_err(sur40->dev, "error in usb_bulk_read\n");
+ goto err_poll;
+ }
+
+ if (bulk_read != sizeof(struct sur40_image_header)) {
+ dev_err(sur40->dev, "received %d bytes (%zd expected)\n",
+ bulk_read, sizeof(struct sur40_image_header));
+ goto err_poll;
+ }
+
+ if (le32_to_cpu(img->magic) != VIDEO_HEADER_MAGIC) {
+ dev_err(sur40->dev, "image magic mismatch\n");
+ goto err_poll;
+ }
+
+ if (le32_to_cpu(img->size) != sur40_video_format.sizeimage) {
+ dev_err(sur40->dev, "image size mismatch\n");
+ goto err_poll;
+ }
+
+ sgt = vb2_dma_sg_plane_desc(&new_buf->vb, 0);
+
+ result = usb_sg_init(&sgr, sur40->usbdev,
+ usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0,
+ sgt->sgl, sgt->nents, sur40_video_format.sizeimage, 0);
+ if (result < 0) {
+ dev_err(sur40->dev, "error %d in usb_sg_init\n", result);
+ goto err_poll;
+ }
+
+ usb_sg_wait(&sgr);
+ if (sgr.status < 0) {
+ dev_err(sur40->dev, "error %d in usb_sg_wait\n", sgr.status);
+ goto err_poll;
+ }
+
+ /* mark as finished */
+ v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp);
+ new_buf->vb.v4l2_buf.sequence = sur40->sequence++;
+ new_buf->vb.v4l2_buf.field = V4L2_FIELD_NONE;
+ vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE);
+ return;
+
+err_poll:
+ vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_ERROR);
+}
+
+/* Initialize input device parameters. */
+static void sur40_input_setup(struct input_dev *input_dev)
+{
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_ABS, input_dev->evbit);
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, SENSOR_RES_X, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, SENSOR_RES_Y, 0, 0);
+
+ input_set_abs_params(input_dev, ABS_MT_TOOL_X,
+ 0, SENSOR_RES_X, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOOL_Y,
+ 0, SENSOR_RES_Y, 0, 0);
+
+ /* max value unknown, but major/minor axis
+ * can never be larger than screen */
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, SENSOR_RES_X, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
+ 0, SENSOR_RES_Y, 0, 0);
+
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+
+ input_mt_init_slots(input_dev, MAX_CONTACTS,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+}
+
+/* Check candidate USB interface. */
+static int sur40_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev = interface_to_usbdev(interface);
+ struct sur40_state *sur40;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ struct input_polled_dev *poll_dev;
+ int error;
+
+ /* Check if we really have the right interface. */
+ iface_desc = &interface->altsetting[0];
+ if (iface_desc->desc.bInterfaceClass != 0xFF)
+ return -ENODEV;
+
+ /* Use endpoint #4 (0x86). */
+ endpoint = &iface_desc->endpoint[4].desc;
+ if (endpoint->bEndpointAddress != TOUCH_ENDPOINT)
+ return -ENODEV;
+
+ /* Allocate memory for our device state and initialize it. */
+ sur40 = kzalloc(sizeof(struct sur40_state), GFP_KERNEL);
+ if (!sur40)
+ return -ENOMEM;
+
+ poll_dev = input_allocate_polled_device();
+ if (!poll_dev) {
+ error = -ENOMEM;
+ goto err_free_dev;
+ }
+
+ /* initialize locks/lists */
+ INIT_LIST_HEAD(&sur40->buf_list);
+ spin_lock_init(&sur40->qlock);
+ mutex_init(&sur40->lock);
+
+ /* Set up polled input device control structure */
+ poll_dev->private = sur40;
+ poll_dev->poll_interval = POLL_INTERVAL;
+ poll_dev->open = sur40_open;
+ poll_dev->poll = sur40_poll;
+ poll_dev->close = sur40_close;
+
+ /* Set up regular input device structure */
+ sur40_input_setup(poll_dev->input);
+
+ poll_dev->input->name = DRIVER_LONG;
+ usb_to_input_id(usbdev, &poll_dev->input->id);
+ usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys));
+ strlcat(sur40->phys, "/input0", sizeof(sur40->phys));
+ poll_dev->input->phys = sur40->phys;
+ poll_dev->input->dev.parent = &interface->dev;
+
+ sur40->usbdev = usbdev;
+ sur40->dev = &interface->dev;
+ sur40->input = poll_dev;
+
+ /* use the bulk-in endpoint tested above */
+ sur40->bulk_in_size = usb_endpoint_maxp(endpoint);
+ sur40->bulk_in_epaddr = endpoint->bEndpointAddress;
+ sur40->bulk_in_buffer = kmalloc(sur40->bulk_in_size, GFP_KERNEL);
+ if (!sur40->bulk_in_buffer) {
+ dev_err(&interface->dev, "Unable to allocate input buffer.");
+ error = -ENOMEM;
+ goto err_free_polldev;
+ }
+
+ /* register the polled input device */
+ error = input_register_polled_device(poll_dev);
+ if (error) {
+ dev_err(&interface->dev,
+ "Unable to register polled input device.");
+ goto err_free_buffer;
+ }
+
+ /* register the video master device */
+ snprintf(sur40->v4l2.name, sizeof(sur40->v4l2.name), "%s", DRIVER_LONG);
+ error = v4l2_device_register(sur40->dev, &sur40->v4l2);
+ if (error) {
+ dev_err(&interface->dev,
+ "Unable to register video master device.");
+ goto err_unreg_v4l2;
+ }
+
+ /* initialize the lock and subdevice */
+ sur40->queue = sur40_queue;
+ sur40->queue.drv_priv = sur40;
+ sur40->queue.lock = &sur40->lock;
+
+ /* initialize the queue */
+ error = vb2_queue_init(&sur40->queue);
+ if (error)
+ goto err_unreg_v4l2;
+
+ sur40->alloc_ctx = vb2_dma_sg_init_ctx(sur40->dev);
+ if (IS_ERR(sur40->alloc_ctx)) {
+ dev_err(sur40->dev, "Can't allocate buffer context");
+ goto err_unreg_v4l2;
+ }
+
+ sur40->vdev = sur40_video_device;
+ sur40->vdev.v4l2_dev = &sur40->v4l2;
+ sur40->vdev.lock = &sur40->lock;
+ sur40->vdev.queue = &sur40->queue;
+ video_set_drvdata(&sur40->vdev, sur40);
+
+ error = video_register_device(&sur40->vdev, VFL_TYPE_GRABBER, -1);
+ if (error) {
+ dev_err(&interface->dev,
+ "Unable to register video subdevice.");
+ goto err_unreg_video;
+ }
+
+ /* we can register the device now, as it is ready */
+ usb_set_intfdata(interface, sur40);
+ dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC);
+
+ return 0;
+
+err_unreg_video:
+ video_unregister_device(&sur40->vdev);
+err_unreg_v4l2:
+ v4l2_device_unregister(&sur40->v4l2);
+err_free_buffer:
+ kfree(sur40->bulk_in_buffer);
+err_free_polldev:
+ input_free_polled_device(sur40->input);
+err_free_dev:
+ kfree(sur40);
+
+ return error;
+}
+
+/* Unregister device & clean up. */
+static void sur40_disconnect(struct usb_interface *interface)
+{
+ struct sur40_state *sur40 = usb_get_intfdata(interface);
+
+ video_unregister_device(&sur40->vdev);
+ v4l2_device_unregister(&sur40->v4l2);
+ vb2_dma_sg_cleanup_ctx(sur40->alloc_ctx);
+
+ input_unregister_polled_device(sur40->input);
+ input_free_polled_device(sur40->input);
+ kfree(sur40->bulk_in_buffer);
+ kfree(sur40);
+
+ usb_set_intfdata(interface, NULL);
+ dev_dbg(&interface->dev, "%s is now disconnected\n", DRIVER_DESC);
+}
+
+/*
+ * Setup the constraints of the queue: besides setting the number of planes
+ * per buffer and the size and allocation context of each plane, it also
+ * checks if sufficient buffers have been allocated. Usually 3 is a good
+ * minimum number: many DMA engines need a minimum of 2 buffers in the
+ * queue and you need to have another available for userspace processing.
+ */
+static int sur40_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct sur40_state *sur40 = vb2_get_drv_priv(q);
+
+ if (q->num_buffers + *nbuffers < 3)
+ *nbuffers = 3 - q->num_buffers;
+
+ if (fmt && fmt->fmt.pix.sizeimage < sur40_video_format.sizeimage)
+ return -EINVAL;
+
+ *nplanes = 1;
+ sizes[0] = fmt ? fmt->fmt.pix.sizeimage : sur40_video_format.sizeimage;
+ alloc_ctxs[0] = sur40->alloc_ctx;
+
+ return 0;
+}
+
+/*
+ * Prepare the buffer for queueing to the DMA engine: check and set the
+ * payload size.
+ */
+static int sur40_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long size = sur40_video_format.sizeimage;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+ return 0;
+}
+
+/*
+ * Queue this buffer to the DMA engine.
+ */
+static void sur40_buffer_queue(struct vb2_buffer *vb)
+{
+ struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue);
+ struct sur40_buffer *buf = (struct sur40_buffer *)vb;
+
+ spin_lock(&sur40->qlock);
+ list_add_tail(&buf->list, &sur40->buf_list);
+ spin_unlock(&sur40->qlock);
+}
+
+static void return_all_buffers(struct sur40_state *sur40,
+ enum vb2_buffer_state state)
+{
+ struct sur40_buffer *buf, *node;
+
+ spin_lock(&sur40->qlock);
+ list_for_each_entry_safe(buf, node, &sur40->buf_list, list) {
+ vb2_buffer_done(&buf->vb, state);
+ list_del(&buf->list);
+ }
+ spin_unlock(&sur40->qlock);
+}
+
+/*
+ * Start streaming. First check if the minimum number of buffers have been
+ * queued. If not, then return -ENOBUFS and the vb2 framework will call
+ * this function again the next time a buffer has been queued until enough
+ * buffers are available to actually start the DMA engine.
+ */
+static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct sur40_state *sur40 = vb2_get_drv_priv(vq);
+
+ sur40->sequence = 0;
+ return 0;
+}
+
+/*
+ * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued
+ * and passed on to the vb2 framework marked as STATE_ERROR.
+ */
+static void sur40_stop_streaming(struct vb2_queue *vq)
+{
+ struct sur40_state *sur40 = vb2_get_drv_priv(vq);
+
+ /* Release all active buffers */
+ return_all_buffers(sur40, VB2_BUF_STATE_ERROR);
+}
+
+/* V4L ioctl */
+static int sur40_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct sur40_state *sur40 = video_drvdata(file);
+
+ strlcpy(cap->driver, DRIVER_SHORT, sizeof(cap->driver));
+ strlcpy(cap->card, DRIVER_LONG, sizeof(cap->card));
+ usb_make_path(sur40->usbdev, cap->bus_info, sizeof(cap->bus_info));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int sur40_vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->std = V4L2_STD_UNKNOWN;
+ strlcpy(i->name, "In-Cell Sensor", sizeof(i->name));
+ i->capabilities = 0;
+ return 0;
+}
+
+static int sur40_vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return (i == 0) ? 0 : -EINVAL;
+}
+
+static int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int sur40_vidioc_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ f->fmt.pix = sur40_video_format;
+ return 0;
+}
+
+static int sur40_vidioc_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
+ strlcpy(f->description, "8-bit greyscale", sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_GREY;
+ f->flags = 0;
+ return 0;
+}
+
+static const struct usb_device_id sur40_table[] = {
+ { USB_DEVICE(ID_MICROSOFT, ID_SUR40) }, /* Samsung SUR40 */
+ { } /* terminating null entry */
+};
+MODULE_DEVICE_TABLE(usb, sur40_table);
+
+/* V4L2 structures */
+static const struct vb2_ops sur40_queue_ops = {
+ .queue_setup = sur40_queue_setup,
+ .buf_prepare = sur40_buffer_prepare,
+ .buf_queue = sur40_buffer_queue,
+ .start_streaming = sur40_start_streaming,
+ .stop_streaming = sur40_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static const struct vb2_queue sur40_queue = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ /*
+ * VB2_USERPTR in currently not enabled: passing a user pointer to
+ * dma-sg will result in segment sizes that are not a multiple of
+ * 512 bytes, which is required by the host controller.
+ */
+ .io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF,
+ .buf_struct_size = sizeof(struct sur40_buffer),
+ .ops = &sur40_queue_ops,
+ .mem_ops = &vb2_dma_sg_memops,
+ .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC,
+ .min_buffers_needed = 3,
+};
+
+static const struct v4l2_file_operations sur40_video_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .unlocked_ioctl = video_ioctl2,
+ .read = vb2_fop_read,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
+};
+
+static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = {
+
+ .vidioc_querycap = sur40_vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = sur40_vidioc_enum_fmt,
+ .vidioc_try_fmt_vid_cap = sur40_vidioc_fmt,
+ .vidioc_s_fmt_vid_cap = sur40_vidioc_fmt,
+ .vidioc_g_fmt_vid_cap = sur40_vidioc_fmt,
+
+ .vidioc_enum_input = sur40_vidioc_enum_input,
+ .vidioc_g_input = sur40_vidioc_g_input,
+ .vidioc_s_input = sur40_vidioc_s_input,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static const struct video_device sur40_video_device = {
+ .name = DRIVER_LONG,
+ .fops = &sur40_video_fops,
+ .ioctl_ops = &sur40_video_ioctl_ops,
+ .release = video_device_release_empty,
+};
+
+static const struct v4l2_pix_format sur40_video_format = {
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ .width = SENSOR_RES_X / 2,
+ .height = SENSOR_RES_Y / 2,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bytesperline = SENSOR_RES_X / 2,
+ .sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),
+};
+
+/* USB-specific object needed to register this driver with the USB subsystem. */
+static struct usb_driver sur40_driver = {
+ .name = DRIVER_SHORT,
+ .probe = sur40_probe,
+ .disconnect = sur40_disconnect,
+ .id_table = sur40_table,
+};
+
+module_usb_driver(sur40_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
new file mode 100644
index 000000000..642f4a53d
--- /dev/null
+++ b/drivers/input/touchscreen/sx8654.c
@@ -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;
+ int 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 (!input)
+ 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");
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
new file mode 100644
index 000000000..191a1b878
--- /dev/null
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -0,0 +1,554 @@
+/*
+ * TI Touch Screen driver
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/sort.h>
+
+#include <linux/mfd/ti_am335x_tscadc.h>
+
+#define ADCFSM_STEPID 0x10
+#define SEQ_SETTLE 275
+#define MAX_12BIT ((1 << 12) - 1)
+
+static const int config_pins[] = {
+ STEPCONFIG_XPP,
+ STEPCONFIG_XNN,
+ STEPCONFIG_YPP,
+ STEPCONFIG_YNN,
+};
+
+struct titsc {
+ struct input_dev *input;
+ struct ti_tscadc_dev *mfd_tscadc;
+ unsigned int irq;
+ unsigned int wires;
+ unsigned int x_plate_resistance;
+ bool pen_down;
+ int coordinate_readouts;
+ u32 config_inp[4];
+ u32 bit_xp, bit_xn, bit_yp, bit_yn;
+ u32 inp_xp, inp_xn, inp_yp, inp_yn;
+ u32 step_mask;
+ u32 charge_delay;
+};
+
+static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
+{
+ return readl(ts->mfd_tscadc->tscadc_base + reg);
+}
+
+static void titsc_writel(struct titsc *tsc, unsigned int reg,
+ unsigned int val)
+{
+ writel(val, tsc->mfd_tscadc->tscadc_base + reg);
+}
+
+static int titsc_config_wires(struct titsc *ts_dev)
+{
+ u32 analog_line[4];
+ u32 wire_order[4];
+ int i, bit_cfg;
+
+ for (i = 0; i < 4; i++) {
+ /*
+ * Get the order in which TSC wires are attached
+ * w.r.t. each of the analog input lines on the EVM.
+ */
+ analog_line[i] = (ts_dev->config_inp[i] & 0xF0) >> 4;
+ wire_order[i] = ts_dev->config_inp[i] & 0x0F;
+ if (WARN_ON(analog_line[i] > 7))
+ return -EINVAL;
+ if (WARN_ON(wire_order[i] > ARRAY_SIZE(config_pins)))
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 4; i++) {
+ int an_line;
+ int wi_order;
+
+ an_line = analog_line[i];
+ wi_order = wire_order[i];
+ bit_cfg = config_pins[wi_order];
+ if (bit_cfg == 0)
+ return -EINVAL;
+ switch (wi_order) {
+ case 0:
+ ts_dev->bit_xp = bit_cfg;
+ ts_dev->inp_xp = an_line;
+ break;
+
+ case 1:
+ ts_dev->bit_xn = bit_cfg;
+ ts_dev->inp_xn = an_line;
+ break;
+
+ case 2:
+ ts_dev->bit_yp = bit_cfg;
+ ts_dev->inp_yp = an_line;
+ break;
+ case 3:
+ ts_dev->bit_yn = bit_cfg;
+ ts_dev->inp_yn = an_line;
+ break;
+ }
+ }
+ return 0;
+}
+
+static void titsc_step_config(struct titsc *ts_dev)
+{
+ unsigned int config;
+ int i;
+ int end_step, first_step, tsc_steps;
+ u32 stepenable;
+
+ config = STEPCONFIG_MODE_HWSYNC |
+ STEPCONFIG_AVG_16 | ts_dev->bit_xp;
+ switch (ts_dev->wires) {
+ case 4:
+ config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn;
+ break;
+ case 5:
+ config |= ts_dev->bit_yn |
+ STEPCONFIG_INP_AN4 | ts_dev->bit_xn |
+ ts_dev->bit_yp;
+ break;
+ case 8:
+ config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn;
+ break;
+ }
+
+ tsc_steps = ts_dev->coordinate_readouts * 2 + 2;
+ first_step = TOTAL_STEPS - tsc_steps;
+ /* Steps 16 to 16-coordinate_readouts is for X */
+ end_step = first_step + tsc_steps;
+ for (i = end_step - ts_dev->coordinate_readouts; i < end_step; i++) {
+ titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
+ titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
+ }
+
+ config = 0;
+ config = STEPCONFIG_MODE_HWSYNC |
+ STEPCONFIG_AVG_16 | ts_dev->bit_yn |
+ STEPCONFIG_INM_ADCREFM;
+ switch (ts_dev->wires) {
+ case 4:
+ config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp);
+ break;
+ case 5:
+ config |= ts_dev->bit_xp | STEPCONFIG_INP_AN4 |
+ ts_dev->bit_xn | ts_dev->bit_yp;
+ break;
+ case 8:
+ config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp);
+ break;
+ }
+
+ /* 1 ... coordinate_readouts is for Y */
+ end_step = first_step + ts_dev->coordinate_readouts;
+ for (i = first_step; i < end_step; i++) {
+ titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
+ titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
+ }
+
+ /* Make CHARGECONFIG same as IDLECONFIG */
+
+ config = titsc_readl(ts_dev, REG_IDLECONFIG);
+ titsc_writel(ts_dev, REG_CHARGECONFIG, config);
+ titsc_writel(ts_dev, REG_CHARGEDELAY, ts_dev->charge_delay);
+
+ /* coordinate_readouts + 1 ... coordinate_readouts + 2 is for Z */
+ config = STEPCONFIG_MODE_HWSYNC |
+ STEPCONFIG_AVG_16 | ts_dev->bit_yp |
+ ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM |
+ STEPCONFIG_INP(ts_dev->inp_xp);
+ titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config);
+ titsc_writel(ts_dev, REG_STEPDELAY(end_step),
+ STEPCONFIG_OPENDLY);
+
+ end_step++;
+ config |= STEPCONFIG_INP(ts_dev->inp_yn);
+ titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config);
+ titsc_writel(ts_dev, REG_STEPDELAY(end_step),
+ STEPCONFIG_OPENDLY);
+
+ /* The steps end ... end - readouts * 2 + 2 and bit 0 for TS_Charge */
+ stepenable = 1;
+ for (i = 0; i < tsc_steps; i++)
+ stepenable |= 1 << (first_step + i + 1);
+
+ ts_dev->step_mask = stepenable;
+ am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
+}
+
+static int titsc_cmp_coord(const void *a, const void *b)
+{
+ return *(int *)a - *(int *)b;
+}
+
+static void titsc_read_coordinates(struct titsc *ts_dev,
+ u32 *x, u32 *y, u32 *z1, u32 *z2)
+{
+ unsigned int yvals[7], xvals[7];
+ unsigned int i, xsum = 0, ysum = 0;
+ unsigned int creads = ts_dev->coordinate_readouts;
+
+ for (i = 0; i < creads; i++) {
+ yvals[i] = titsc_readl(ts_dev, REG_FIFO0);
+ yvals[i] &= 0xfff;
+ }
+
+ *z1 = titsc_readl(ts_dev, REG_FIFO0);
+ *z1 &= 0xfff;
+ *z2 = titsc_readl(ts_dev, REG_FIFO0);
+ *z2 &= 0xfff;
+
+ for (i = 0; i < creads; i++) {
+ xvals[i] = titsc_readl(ts_dev, REG_FIFO0);
+ xvals[i] &= 0xfff;
+ }
+
+ /*
+ * If co-ordinates readouts is less than 4 then
+ * report the average. In case of 4 or more
+ * readouts, sort the co-ordinate samples, drop
+ * min and max values and report the average of
+ * remaining values.
+ */
+ if (creads <= 3) {
+ for (i = 0; i < creads; i++) {
+ ysum += yvals[i];
+ xsum += xvals[i];
+ }
+ ysum /= creads;
+ xsum /= creads;
+ } else {
+ sort(yvals, creads, sizeof(unsigned int),
+ titsc_cmp_coord, NULL);
+ sort(xvals, creads, sizeof(unsigned int),
+ titsc_cmp_coord, NULL);
+ for (i = 1; i < creads - 1; i++) {
+ ysum += yvals[i];
+ xsum += xvals[i];
+ }
+ ysum /= creads - 2;
+ xsum /= creads - 2;
+ }
+ *y = ysum;
+ *x = xsum;
+}
+
+static irqreturn_t titsc_irq(int irq, void *dev)
+{
+ struct titsc *ts_dev = dev;
+ struct input_dev *input_dev = ts_dev->input;
+ unsigned int fsm, status, irqclr = 0;
+ unsigned int x = 0, y = 0;
+ unsigned int z1, z2, z;
+
+ status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
+ if (status & IRQENB_HW_PEN) {
+ ts_dev->pen_down = true;
+ titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00);
+ titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
+ irqclr |= IRQENB_HW_PEN;
+ }
+
+ if (status & IRQENB_PENUP) {
+ fsm = titsc_readl(ts_dev, REG_ADCFSM);
+ if (fsm == ADCFSM_STEPID) {
+ ts_dev->pen_down = false;
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ input_sync(input_dev);
+ } else {
+ ts_dev->pen_down = true;
+ }
+ irqclr |= IRQENB_PENUP;
+ }
+
+ if (status & IRQENB_EOS)
+ irqclr |= IRQENB_EOS;
+
+ /*
+ * ADC and touchscreen share the IRQ line.
+ * FIFO1 interrupts are used by ADC. Handle FIFO0 IRQs here only
+ */
+ if (status & IRQENB_FIFO0THRES) {
+
+ titsc_read_coordinates(ts_dev, &x, &y, &z1, &z2);
+
+ if (ts_dev->pen_down && z1 != 0 && z2 != 0) {
+ /*
+ * Calculate pressure using formula
+ * Resistance(touch) = x plate resistance *
+ * x postion/4096 * ((z2 / z1) - 1)
+ */
+ z = z1 - z2;
+ z *= x;
+ z *= ts_dev->x_plate_resistance;
+ z /= z2;
+ z = (z + 2047) >> 12;
+
+ if (z <= MAX_12BIT) {
+ input_report_abs(input_dev, ABS_X, x);
+ input_report_abs(input_dev, ABS_Y, y);
+ input_report_abs(input_dev, ABS_PRESSURE, z);
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ input_sync(input_dev);
+ }
+ }
+ irqclr |= IRQENB_FIFO0THRES;
+ }
+ if (irqclr) {
+ titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
+ if (status & IRQENB_EOS)
+ am335x_tsc_se_set_cache(ts_dev->mfd_tscadc,
+ ts_dev->step_mask);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static int titsc_parse_dt(struct platform_device *pdev,
+ struct titsc *ts_dev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ int err;
+
+ if (!node)
+ return -EINVAL;
+
+ err = of_property_read_u32(node, "ti,wires", &ts_dev->wires);
+ if (err < 0)
+ return err;
+ switch (ts_dev->wires) {
+ case 4:
+ case 5:
+ case 8:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ err = of_property_read_u32(node, "ti,x-plate-resistance",
+ &ts_dev->x_plate_resistance);
+ if (err < 0)
+ return err;
+
+ /*
+ * Try with the new binding first. If it fails, try again with
+ * bogus, miss-spelled version.
+ */
+ err = of_property_read_u32(node, "ti,coordinate-readouts",
+ &ts_dev->coordinate_readouts);
+ if (err < 0) {
+ dev_warn(&pdev->dev, "please use 'ti,coordinate-readouts' instead\n");
+ err = of_property_read_u32(node, "ti,coordiante-readouts",
+ &ts_dev->coordinate_readouts);
+ }
+
+ if (err < 0)
+ return err;
+
+ if (ts_dev->coordinate_readouts <= 0) {
+ dev_warn(&pdev->dev,
+ "invalid co-ordinate readouts, resetting it to 5\n");
+ ts_dev->coordinate_readouts = 5;
+ }
+
+ err = of_property_read_u32(node, "ti,charge-delay",
+ &ts_dev->charge_delay);
+ /*
+ * If ti,charge-delay value is not specified, then use
+ * CHARGEDLY_OPENDLY as the default value.
+ */
+ if (err < 0) {
+ ts_dev->charge_delay = CHARGEDLY_OPENDLY;
+ dev_warn(&pdev->dev, "ti,charge-delay not specified\n");
+ }
+
+ return of_property_read_u32_array(node, "ti,wire-config",
+ ts_dev->config_inp, ARRAY_SIZE(ts_dev->config_inp));
+}
+
+/*
+ * The functions for inserting/removing driver as a module.
+ */
+
+static int titsc_probe(struct platform_device *pdev)
+{
+ struct titsc *ts_dev;
+ struct input_dev *input_dev;
+ struct ti_tscadc_dev *tscadc_dev = ti_tscadc_dev_get(pdev);
+ int err;
+
+ /* Allocate memory for device */
+ ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts_dev || !input_dev) {
+ dev_err(&pdev->dev, "failed to allocate memory.\n");
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ tscadc_dev->tsc = ts_dev;
+ ts_dev->mfd_tscadc = tscadc_dev;
+ ts_dev->input = input_dev;
+ ts_dev->irq = tscadc_dev->irq;
+
+ err = titsc_parse_dt(pdev, ts_dev);
+ if (err) {
+ dev_err(&pdev->dev, "Could not find valid DT data.\n");
+ goto err_free_mem;
+ }
+
+ err = request_irq(ts_dev->irq, titsc_irq,
+ IRQF_SHARED, pdev->dev.driver->name, ts_dev);
+ if (err) {
+ dev_err(&pdev->dev, "failed to allocate irq.\n");
+ goto err_free_mem;
+ }
+
+ titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
+ titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_EOS);
+ err = titsc_config_wires(ts_dev);
+ if (err) {
+ dev_err(&pdev->dev, "wrong i/p wire configuration\n");
+ goto err_free_irq;
+ }
+ titsc_step_config(ts_dev);
+ titsc_writel(ts_dev, REG_FIFO0THR,
+ ts_dev->coordinate_readouts * 2 + 2 - 1);
+
+ input_dev->name = "ti-tsc";
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
+
+ /* register to the input system */
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_free_irq;
+
+ platform_set_drvdata(pdev, ts_dev);
+ return 0;
+
+err_free_irq:
+ free_irq(ts_dev->irq, ts_dev);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts_dev);
+ return err;
+}
+
+static int titsc_remove(struct platform_device *pdev)
+{
+ struct titsc *ts_dev = platform_get_drvdata(pdev);
+ u32 steps;
+
+ free_irq(ts_dev->irq, ts_dev);
+
+ /* total steps followed by the enable mask */
+ steps = 2 * ts_dev->coordinate_readouts + 2;
+ steps = (1 << steps) - 1;
+ am335x_tsc_se_clr(ts_dev->mfd_tscadc, steps);
+
+ input_unregister_device(ts_dev->input);
+
+ kfree(ts_dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int 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(tscadc_dev->dev)) {
+ idle = titsc_readl(ts_dev, REG_IRQENABLE);
+ titsc_writel(ts_dev, REG_IRQENABLE,
+ (idle | IRQENB_HW_PEN));
+ titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB);
+ }
+ return 0;
+}
+
+static int 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(tscadc_dev->dev)) {
+ titsc_writel(ts_dev, REG_IRQWAKEUP,
+ 0x00);
+ titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
+ }
+ titsc_step_config(ts_dev);
+ titsc_writel(ts_dev, REG_FIFO0THR,
+ ts_dev->coordinate_readouts * 2 + 2 - 1);
+ return 0;
+}
+
+static const struct dev_pm_ops titsc_pm_ops = {
+ .suspend = titsc_suspend,
+ .resume = titsc_resume,
+};
+#define TITSC_PM_OPS (&titsc_pm_ops)
+#else
+#define TITSC_PM_OPS NULL
+#endif
+
+static const struct of_device_id ti_tsc_dt_ids[] = {
+ { .compatible = "ti,am3359-tsc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ti_tsc_dt_ids);
+
+static struct platform_driver ti_tsc_driver = {
+ .probe = titsc_probe,
+ .remove = titsc_remove,
+ .driver = {
+ .name = "TI-am335x-tsc",
+ .pm = TITSC_PM_OPS,
+ .of_match_table = ti_tsc_dt_ids,
+ },
+};
+module_platform_driver(ti_tsc_driver);
+
+MODULE_DESCRIPTION("TI touchscreen controller driver");
+MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/touchit213.c b/drivers/input/touchscreen/touchit213.c
new file mode 100644
index 000000000..c27cf8f3d
--- /dev/null
+++ b/drivers/input/touchscreen/touchit213.c
@@ -0,0 +1,218 @@
+/*
+ * Sahara TouchIT-213 serial touchscreen driver
+ *
+ * Copyright (c) 2007-2008 Claudio Nieder <private@claudio.ch>
+ *
+ * Based on Touchright driver (drivers/input/touchscreen/touchright.c)
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "Sahara TouchIT-213 serial touchscreen driver"
+
+MODULE_AUTHOR("Claudio Nieder <private@claudio.ch>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+/*
+ * Data is received through COM1 at 9600bit/s,8bit,no parity in packets
+ * of 5 byte each.
+ *
+ * +--------+ +--------+ +--------+ +--------+ +--------+
+ * |1000000p| |0xxxxxxx| |0xxxxxxx| |0yyyyyyy| |0yyyyyyy|
+ * +--------+ +--------+ +--------+ +--------+ +--------+
+ * MSB LSB MSB LSB
+ *
+ * The value of p is 1 as long as the screen is touched and 0 when
+ * reporting the location where touching stopped, e.g. where the pen was
+ * lifted from the screen.
+ *
+ * When holding the screen in landscape mode as the BIOS text output is
+ * presented, x is the horizontal axis with values growing from left to
+ * right and y is the vertical axis with values growing from top to
+ * bottom.
+ *
+ * When holding the screen in portrait mode with the Sahara logo in its
+ * correct position, x ist the vertical axis with values growing from
+ * top to bottom and y is the horizontal axis with values growing from
+ * right to left.
+ */
+
+#define T213_FORMAT_TOUCH_BIT 0x01
+#define T213_FORMAT_STATUS_BYTE 0x80
+#define T213_FORMAT_STATUS_MASK ~T213_FORMAT_TOUCH_BIT
+
+/*
+ * On my Sahara Touch-IT 213 I have observed x values from 0 to 0x7f0
+ * and y values from 0x1d to 0x7e9, so the actual measurement is
+ * probably done with an 11 bit precision.
+ */
+#define T213_MIN_XC 0
+#define T213_MAX_XC 0x07ff
+#define T213_MIN_YC 0
+#define T213_MAX_YC 0x07ff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct touchit213 {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char csum;
+ unsigned char data[5];
+ char phys[32];
+};
+
+static irqreturn_t touchit213_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct touchit213 *touchit213 = serio_get_drvdata(serio);
+ struct input_dev *dev = touchit213->dev;
+
+ touchit213->data[touchit213->idx] = data;
+
+ switch (touchit213->idx++) {
+ case 0:
+ if ((touchit213->data[0] & T213_FORMAT_STATUS_MASK) !=
+ T213_FORMAT_STATUS_BYTE) {
+ pr_debug("unsynchronized data: 0x%02x\n", data);
+ touchit213->idx = 0;
+ }
+ break;
+
+ case 4:
+ touchit213->idx = 0;
+ input_report_abs(dev, ABS_X,
+ (touchit213->data[1] << 7) | touchit213->data[2]);
+ input_report_abs(dev, ABS_Y,
+ (touchit213->data[3] << 7) | touchit213->data[4]);
+ input_report_key(dev, BTN_TOUCH,
+ touchit213->data[0] & T213_FORMAT_TOUCH_BIT);
+ input_sync(dev);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * touchit213_disconnect() is the opposite of touchit213_connect()
+ */
+
+static void touchit213_disconnect(struct serio *serio)
+{
+ struct touchit213 *touchit213 = serio_get_drvdata(serio);
+
+ input_get_device(touchit213->dev);
+ input_unregister_device(touchit213->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(touchit213->dev);
+ kfree(touchit213);
+}
+
+/*
+ * touchit213_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchright protocol and registers it as
+ * an input device.
+ */
+
+static int touchit213_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct touchit213 *touchit213;
+ struct input_dev *input_dev;
+ int err;
+
+ touchit213 = kzalloc(sizeof(struct touchit213), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!touchit213 || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ touchit213->serio = serio;
+ touchit213->dev = input_dev;
+ snprintf(touchit213->phys, sizeof(touchit213->phys),
+ "%s/input0", serio->phys);
+
+ input_dev->name = "Sahara Touch-iT213 Serial TouchScreen";
+ input_dev->phys = touchit213->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TOUCHIT213;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(touchit213->dev, ABS_X,
+ T213_MIN_XC, T213_MAX_XC, 0, 0);
+ input_set_abs_params(touchit213->dev, ABS_Y,
+ T213_MIN_YC, T213_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, touchit213);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(touchit213->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(touchit213);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id touchit213_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TOUCHIT213,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, touchit213_serio_ids);
+
+static struct serio_driver touchit213_drv = {
+ .driver = {
+ .name = "touchit213",
+ },
+ .description = DRIVER_DESC,
+ .id_table = touchit213_serio_ids,
+ .interrupt = touchit213_interrupt,
+ .connect = touchit213_connect,
+ .disconnect = touchit213_disconnect,
+};
+
+module_serio_driver(touchit213_drv);
diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c
new file mode 100644
index 000000000..4000e5205
--- /dev/null
+++ b/drivers/input/touchscreen/touchright.c
@@ -0,0 +1,178 @@
+/*
+ * Touchright serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "Touchright serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define TR_FORMAT_TOUCH_BIT 0x01
+#define TR_FORMAT_STATUS_BYTE 0x40
+#define TR_FORMAT_STATUS_MASK ~TR_FORMAT_TOUCH_BIT
+
+#define TR_LENGTH 5
+
+#define TR_MIN_XC 0
+#define TR_MAX_XC 0x1ff
+#define TR_MIN_YC 0
+#define TR_MAX_YC 0x1ff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct tr {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[TR_LENGTH];
+ char phys[32];
+};
+
+static irqreturn_t tr_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct tr *tr = serio_get_drvdata(serio);
+ struct input_dev *dev = tr->dev;
+
+ tr->data[tr->idx] = data;
+
+ if ((tr->data[0] & TR_FORMAT_STATUS_MASK) == TR_FORMAT_STATUS_BYTE) {
+ if (++tr->idx == TR_LENGTH) {
+ input_report_abs(dev, ABS_X,
+ (tr->data[1] << 5) | (tr->data[2] >> 1));
+ input_report_abs(dev, ABS_Y,
+ (tr->data[3] << 5) | (tr->data[4] >> 1));
+ input_report_key(dev, BTN_TOUCH,
+ tr->data[0] & TR_FORMAT_TOUCH_BIT);
+ input_sync(dev);
+ tr->idx = 0;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * tr_disconnect() is the opposite of tr_connect()
+ */
+
+static void tr_disconnect(struct serio *serio)
+{
+ struct tr *tr = serio_get_drvdata(serio);
+
+ input_get_device(tr->dev);
+ input_unregister_device(tr->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(tr->dev);
+ kfree(tr);
+}
+
+/*
+ * tr_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchright protocol and registers it as
+ * an input device.
+ */
+
+static int tr_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct tr *tr;
+ struct input_dev *input_dev;
+ int err;
+
+ tr = kzalloc(sizeof(struct tr), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!tr || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ tr->serio = serio;
+ tr->dev = input_dev;
+ snprintf(tr->phys, sizeof(tr->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "Touchright Serial TouchScreen";
+ input_dev->phys = tr->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TOUCHRIGHT;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(tr->dev, ABS_X, TR_MIN_XC, TR_MAX_XC, 0, 0);
+ input_set_abs_params(tr->dev, ABS_Y, TR_MIN_YC, TR_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, tr);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(tr->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(tr);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id tr_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TOUCHRIGHT,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, tr_serio_ids);
+
+static struct serio_driver tr_drv = {
+ .driver = {
+ .name = "touchright",
+ },
+ .description = DRIVER_DESC,
+ .id_table = tr_serio_ids,
+ .interrupt = tr_interrupt,
+ .connect = tr_connect,
+ .disconnect = tr_disconnect,
+};
+
+module_serio_driver(tr_drv);
diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c
new file mode 100644
index 000000000..ba90f447d
--- /dev/null
+++ b/drivers/input/touchscreen/touchwin.c
@@ -0,0 +1,185 @@
+/*
+ * Touchwindow serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2005/02/19 Rick Koch:
+ * The Touchwindow I used is made by Edmark Corp. and
+ * constantly outputs a stream of 0's unless it is touched.
+ * It then outputs 3 bytes: X, Y, and a copy of Y.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "Touchwindow serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define TW_LENGTH 3
+
+#define TW_MIN_XC 0
+#define TW_MAX_XC 0xff
+#define TW_MIN_YC 0
+#define TW_MAX_YC 0xff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct tw {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ int touched;
+ unsigned char data[TW_LENGTH];
+ char phys[32];
+};
+
+static irqreturn_t tw_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct tw *tw = serio_get_drvdata(serio);
+ struct input_dev *dev = tw->dev;
+
+ if (data) { /* touch */
+ tw->touched = 1;
+ tw->data[tw->idx++] = data;
+ /* verify length and that the two Y's are the same */
+ if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) {
+ input_report_abs(dev, ABS_X, tw->data[0]);
+ input_report_abs(dev, ABS_Y, tw->data[1]);
+ input_report_key(dev, BTN_TOUCH, 1);
+ input_sync(dev);
+ tw->idx = 0;
+ }
+ } else if (tw->touched) { /* untouch */
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_sync(dev);
+ tw->idx = 0;
+ tw->touched = 0;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * tw_disconnect() is the opposite of tw_connect()
+ */
+
+static void tw_disconnect(struct serio *serio)
+{
+ struct tw *tw = serio_get_drvdata(serio);
+
+ input_get_device(tw->dev);
+ input_unregister_device(tw->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(tw->dev);
+ kfree(tw);
+}
+
+/*
+ * tw_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchwin protocol and registers it as
+ * an input device.
+ */
+
+static int tw_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct tw *tw;
+ struct input_dev *input_dev;
+ int err;
+
+ tw = kzalloc(sizeof(struct tw), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!tw || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ tw->serio = serio;
+ tw->dev = input_dev;
+ snprintf(tw->phys, sizeof(tw->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "Touchwindow Serial TouchScreen";
+ input_dev->phys = tw->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TOUCHWIN;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(tw->dev, ABS_X, TW_MIN_XC, TW_MAX_XC, 0, 0);
+ input_set_abs_params(tw->dev, ABS_Y, TW_MIN_YC, TW_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, tw);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(tw->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(tw);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id tw_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TOUCHWIN,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, tw_serio_ids);
+
+static struct serio_driver tw_drv = {
+ .driver = {
+ .name = "touchwin",
+ },
+ .description = DRIVER_DESC,
+ .id_table = tw_serio_ids,
+ .interrupt = tw_interrupt,
+ .connect = tw_connect,
+ .disconnect = tw_disconnect,
+};
+
+module_serio_driver(tw_drv);
diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c
new file mode 100644
index 000000000..4ffd829d1
--- /dev/null
+++ b/drivers/input/touchscreen/tps6507x-ts.c
@@ -0,0 +1,326 @@
+/*
+ * Touchscreen driver for the tps6507x chip.
+ *
+ * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com)
+ *
+ * Credits:
+ *
+ * Using code from tsc2007, MtekVision Co., Ltd.
+ *
+ * For licencing details see kernel-base/COPYING
+ *
+ * TPS65070, TPS65073, TPS650731, and TPS650732 support
+ * 10 bit touch screen interface.
+ */
+
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/tps6507x.h>
+#include <linux/input/tps6507x-ts.h>
+#include <linux/delay.h>
+
+#define TSC_DEFAULT_POLL_PERIOD 30 /* ms */
+#define TPS_DEFAULT_MIN_PRESSURE 0x30
+#define MAX_10BIT ((1 << 10) - 1)
+
+#define TPS6507X_ADCONFIG_CONVERT_TS (TPS6507X_ADCONFIG_AD_ENABLE | \
+ TPS6507X_ADCONFIG_START_CONVERSION | \
+ TPS6507X_ADCONFIG_INPUT_REAL_TSC)
+#define TPS6507X_ADCONFIG_POWER_DOWN_TS (TPS6507X_ADCONFIG_INPUT_REAL_TSC)
+
+struct ts_event {
+ u16 x;
+ u16 y;
+ u16 pressure;
+};
+
+struct tps6507x_ts {
+ struct device *dev;
+ struct input_polled_dev *poll_dev;
+ struct tps6507x_dev *mfd;
+ char phys[32];
+ struct ts_event tc;
+ u16 min_pressure;
+ bool pendown;
+};
+
+static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data)
+{
+ int err;
+
+ err = tsc->mfd->read_dev(tsc->mfd, reg, 1, data);
+
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int tps6507x_write_u8(struct tps6507x_ts *tsc, u8 reg, u8 data)
+{
+ return tsc->mfd->write_dev(tsc->mfd, reg, 1, &data);
+}
+
+static s32 tps6507x_adc_conversion(struct tps6507x_ts *tsc,
+ u8 tsc_mode, u16 *value)
+{
+ s32 ret;
+ u8 adc_status;
+ u8 result;
+
+ /* Route input signal to A/D converter */
+
+ ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, tsc_mode);
+ if (ret) {
+ dev_err(tsc->dev, "TSC mode read failed\n");
+ goto err;
+ }
+
+ /* Start A/D conversion */
+
+ ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG,
+ TPS6507X_ADCONFIG_CONVERT_TS);
+ if (ret) {
+ dev_err(tsc->dev, "ADC config write failed\n");
+ return ret;
+ }
+
+ do {
+ ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADCONFIG,
+ &adc_status);
+ if (ret) {
+ dev_err(tsc->dev, "ADC config read failed\n");
+ goto err;
+ }
+ } while (adc_status & TPS6507X_ADCONFIG_START_CONVERSION);
+
+ ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_2, &result);
+ if (ret) {
+ dev_err(tsc->dev, "ADC result 2 read failed\n");
+ goto err;
+ }
+
+ *value = (result & TPS6507X_REG_ADRESULT_2_MASK) << 8;
+
+ ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_1, &result);
+ if (ret) {
+ dev_err(tsc->dev, "ADC result 1 read failed\n");
+ goto err;
+ }
+
+ *value |= result;
+
+ dev_dbg(tsc->dev, "TSC channel %d = 0x%X\n", tsc_mode, *value);
+
+err:
+ return ret;
+}
+
+/* Need to call tps6507x_adc_standby() after using A/D converter for the
+ * touch screen interrupt to work properly.
+ */
+
+static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc)
+{
+ s32 ret;
+ s32 loops = 0;
+ u8 val;
+
+ ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG,
+ TPS6507X_ADCONFIG_INPUT_TSC);
+ if (ret)
+ return ret;
+
+ ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE,
+ TPS6507X_TSCMODE_STANDBY);
+ if (ret)
+ return ret;
+
+ ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
+ if (ret)
+ return ret;
+
+ while (val & TPS6507X_REG_TSC_INT) {
+ mdelay(10);
+ ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
+ if (ret)
+ return ret;
+ loops++;
+ }
+
+ return ret;
+}
+
+static void tps6507x_ts_poll(struct input_polled_dev *poll_dev)
+{
+ struct tps6507x_ts *tsc = poll_dev->private;
+ struct input_dev *input_dev = poll_dev->input;
+ bool pendown;
+ s32 ret;
+
+ ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE,
+ &tsc->tc.pressure);
+ if (ret)
+ goto done;
+
+ pendown = tsc->tc.pressure > tsc->min_pressure;
+
+ if (unlikely(!pendown && tsc->pendown)) {
+ dev_dbg(tsc->dev, "UP\n");
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ input_sync(input_dev);
+ tsc->pendown = false;
+ }
+
+ if (pendown) {
+
+ if (!tsc->pendown) {
+ dev_dbg(tsc->dev, "DOWN\n");
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ } else
+ dev_dbg(tsc->dev, "still down\n");
+
+ ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_X_POSITION,
+ &tsc->tc.x);
+ if (ret)
+ goto done;
+
+ ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_Y_POSITION,
+ &tsc->tc.y);
+ if (ret)
+ goto done;
+
+ input_report_abs(input_dev, ABS_X, tsc->tc.x);
+ input_report_abs(input_dev, ABS_Y, tsc->tc.y);
+ input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure);
+ input_sync(input_dev);
+ tsc->pendown = true;
+ }
+
+done:
+ tps6507x_adc_standby(tsc);
+}
+
+static int tps6507x_ts_probe(struct platform_device *pdev)
+{
+ struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
+ const struct tps6507x_board *tps_board;
+ const struct touchscreen_init_data *init_data;
+ struct tps6507x_ts *tsc;
+ struct input_polled_dev *poll_dev;
+ struct input_dev *input_dev;
+ int error;
+
+ /*
+ * tps_board points to pmic related constants
+ * coming from the board-evm file.
+ */
+ tps_board = dev_get_platdata(tps6507x_dev->dev);
+ if (!tps_board) {
+ dev_err(tps6507x_dev->dev,
+ "Could not find tps6507x platform data\n");
+ return -ENODEV;
+ }
+
+ /*
+ * init_data points to array of regulator_init structures
+ * coming from the board-evm file.
+ */
+ init_data = tps_board->tps6507x_ts_init_data;
+
+ tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL);
+ if (!tsc) {
+ dev_err(tps6507x_dev->dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ tsc->mfd = tps6507x_dev;
+ tsc->dev = tps6507x_dev->dev;
+ tsc->min_pressure = init_data ?
+ init_data->min_pressure : TPS_DEFAULT_MIN_PRESSURE;
+
+ snprintf(tsc->phys, sizeof(tsc->phys),
+ "%s/input0", dev_name(tsc->dev));
+
+ poll_dev = input_allocate_polled_device();
+ if (!poll_dev) {
+ dev_err(tsc->dev, "Failed to allocate polled input device.\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ tsc->poll_dev = poll_dev;
+
+ poll_dev->private = tsc;
+ poll_dev->poll = tps6507x_ts_poll;
+ poll_dev->poll_interval = init_data ?
+ init_data->poll_period : TSC_DEFAULT_POLL_PERIOD;
+
+ input_dev = poll_dev->input;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0);
+
+ input_dev->name = "TPS6507x Touchscreen";
+ input_dev->phys = tsc->phys;
+ input_dev->dev.parent = tsc->dev;
+ input_dev->id.bustype = BUS_I2C;
+ if (init_data) {
+ input_dev->id.vendor = init_data->vendor;
+ input_dev->id.product = init_data->product;
+ input_dev->id.version = init_data->version;
+ }
+
+ error = tps6507x_adc_standby(tsc);
+ if (error)
+ goto err_free_polled_dev;
+
+ error = input_register_polled_device(poll_dev);
+ if (error)
+ goto err_free_polled_dev;
+
+ platform_set_drvdata(pdev, tsc);
+
+ return 0;
+
+err_free_polled_dev:
+ input_free_polled_device(poll_dev);
+err_free_mem:
+ kfree(tsc);
+ return error;
+}
+
+static int tps6507x_ts_remove(struct platform_device *pdev)
+{
+ struct tps6507x_ts *tsc = platform_get_drvdata(pdev);
+ struct input_polled_dev *poll_dev = tsc->poll_dev;
+
+ input_unregister_polled_device(poll_dev);
+ input_free_polled_device(poll_dev);
+
+ kfree(tsc);
+
+ return 0;
+}
+
+static struct platform_driver tps6507x_ts_driver = {
+ .driver = {
+ .name = "tps6507x-ts",
+ },
+ .probe = tps6507x_ts_probe,
+ .remove = tps6507x_ts_remove,
+};
+module_platform_driver(tps6507x_ts_driver);
+
+MODULE_AUTHOR("Todd Fischer <todd.fischer@ridgerun.com>");
+MODULE_DESCRIPTION("TPS6507x - TouchScreen driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps6507x-ts");
diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c
new file mode 100644
index 000000000..72657c579
--- /dev/null
+++ b/drivers/input/touchscreen/tsc2005.c
@@ -0,0 +1,827 @@
+/*
+ * TSC2005 touchscreen driver
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ *
+ * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
+ * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/tsc2005.h>
+#include <linux/regulator/consumer.h>
+
+/*
+ * The touchscreen interface operates as follows:
+ *
+ * 1) Pen is pressed against the touchscreen.
+ * 2) TSC2005 performs AD conversion.
+ * 3) After the conversion is done TSC2005 drives DAV line down.
+ * 4) GPIO IRQ is received and tsc2005_irq_thread() is scheduled.
+ * 5) tsc2005_irq_thread() queues up an spi transfer to fetch the x, y, z1, z2
+ * values.
+ * 6) tsc2005_irq_thread() reports coordinates to input layer and sets up
+ * tsc2005_penup_timer() to be called after TSC2005_PENUP_TIME_MS (40ms).
+ * 7) When the penup timer expires, there have not been touch or DAV interrupts
+ * during the last 40ms which means the pen has been lifted.
+ *
+ * ESD recovery via a hardware reset is done if the TSC2005 doesn't respond
+ * after a configurable period (in ms) of activity. If esd_timeout is 0, the
+ * watchdog is disabled.
+ */
+
+/* control byte 1 */
+#define TSC2005_CMD 0x80
+#define TSC2005_CMD_NORMAL 0x00
+#define TSC2005_CMD_STOP 0x01
+#define TSC2005_CMD_12BIT 0x04
+
+/* control byte 0 */
+#define TSC2005_REG_READ 0x0001
+#define TSC2005_REG_PND0 0x0002
+#define TSC2005_REG_X 0x0000
+#define TSC2005_REG_Y 0x0008
+#define TSC2005_REG_Z1 0x0010
+#define TSC2005_REG_Z2 0x0018
+#define TSC2005_REG_TEMP_HIGH 0x0050
+#define TSC2005_REG_CFR0 0x0060
+#define TSC2005_REG_CFR1 0x0068
+#define TSC2005_REG_CFR2 0x0070
+
+/* configuration register 0 */
+#define TSC2005_CFR0_PRECHARGE_276US 0x0040
+#define TSC2005_CFR0_STABTIME_1MS 0x0300
+#define TSC2005_CFR0_CLOCK_1MHZ 0x1000
+#define TSC2005_CFR0_RESOLUTION12 0x2000
+#define TSC2005_CFR0_PENMODE 0x8000
+#define TSC2005_CFR0_INITVALUE (TSC2005_CFR0_STABTIME_1MS | \
+ TSC2005_CFR0_CLOCK_1MHZ | \
+ TSC2005_CFR0_RESOLUTION12 | \
+ TSC2005_CFR0_PRECHARGE_276US | \
+ TSC2005_CFR0_PENMODE)
+
+/* bits common to both read and write of configuration register 0 */
+#define TSC2005_CFR0_RW_MASK 0x3fff
+
+/* configuration register 1 */
+#define TSC2005_CFR1_BATCHDELAY_4MS 0x0003
+#define TSC2005_CFR1_INITVALUE TSC2005_CFR1_BATCHDELAY_4MS
+
+/* configuration register 2 */
+#define TSC2005_CFR2_MAVE_Z 0x0004
+#define TSC2005_CFR2_MAVE_Y 0x0008
+#define TSC2005_CFR2_MAVE_X 0x0010
+#define TSC2005_CFR2_AVG_7 0x0800
+#define TSC2005_CFR2_MEDIUM_15 0x3000
+#define TSC2005_CFR2_INITVALUE (TSC2005_CFR2_MAVE_X | \
+ TSC2005_CFR2_MAVE_Y | \
+ TSC2005_CFR2_MAVE_Z | \
+ TSC2005_CFR2_MEDIUM_15 | \
+ TSC2005_CFR2_AVG_7)
+
+#define MAX_12BIT 0xfff
+#define TSC2005_DEF_X_FUZZ 4
+#define TSC2005_DEF_Y_FUZZ 8
+#define TSC2005_DEF_P_FUZZ 2
+#define TSC2005_DEF_RESISTOR 280
+
+#define TSC2005_SPI_MAX_SPEED_HZ 10000000
+#define TSC2005_PENUP_TIME_MS 40
+
+struct tsc2005_spi_rd {
+ struct spi_transfer spi_xfer;
+ u32 spi_tx;
+ u32 spi_rx;
+};
+
+struct tsc2005 {
+ struct spi_device *spi;
+
+ struct spi_message spi_read_msg;
+ struct tsc2005_spi_rd spi_x;
+ struct tsc2005_spi_rd spi_y;
+ struct tsc2005_spi_rd spi_z1;
+ struct tsc2005_spi_rd spi_z2;
+
+ struct input_dev *idev;
+ char phys[32];
+
+ struct mutex mutex;
+
+ /* raw copy of previous x,y,z */
+ int in_x;
+ int in_y;
+ int in_z1;
+ int in_z2;
+
+ spinlock_t lock;
+ struct timer_list penup_timer;
+
+ unsigned int esd_timeout;
+ struct delayed_work esd_work;
+ unsigned long last_valid_interrupt;
+
+ unsigned int x_plate_ohm;
+
+ bool opened;
+ bool suspended;
+
+ bool pen_down;
+
+ struct regulator *vio;
+
+ int reset_gpio;
+ void (*set_reset)(bool enable);
+};
+
+static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd)
+{
+ u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd;
+ struct spi_transfer xfer = {
+ .tx_buf = &tx,
+ .len = 1,
+ .bits_per_word = 8,
+ };
+ struct spi_message msg;
+ int error;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ error = spi_sync(ts->spi, &msg);
+ if (error) {
+ dev_err(&ts->spi->dev, "%s: failed, command: %x, error: %d\n",
+ __func__, cmd, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value)
+{
+ u32 tx = ((reg | TSC2005_REG_PND0) << 16) | value;
+ struct spi_transfer xfer = {
+ .tx_buf = &tx,
+ .len = 4,
+ .bits_per_word = 24,
+ };
+ struct spi_message msg;
+ int error;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ error = spi_sync(ts->spi, &msg);
+ if (error) {
+ dev_err(&ts->spi->dev,
+ "%s: failed, register: %x, value: %x, error: %d\n",
+ __func__, reg, value, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static void tsc2005_setup_read(struct tsc2005_spi_rd *rd, u8 reg, bool last)
+{
+ memset(rd, 0, sizeof(*rd));
+
+ rd->spi_tx = (reg | TSC2005_REG_READ) << 16;
+ rd->spi_xfer.tx_buf = &rd->spi_tx;
+ rd->spi_xfer.rx_buf = &rd->spi_rx;
+ rd->spi_xfer.len = 4;
+ rd->spi_xfer.bits_per_word = 24;
+ rd->spi_xfer.cs_change = !last;
+}
+
+static int tsc2005_read(struct tsc2005 *ts, u8 reg, u16 *value)
+{
+ struct tsc2005_spi_rd spi_rd;
+ struct spi_message msg;
+ int error;
+
+ tsc2005_setup_read(&spi_rd, reg, true);
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&spi_rd.spi_xfer, &msg);
+
+ error = spi_sync(ts->spi, &msg);
+ if (error)
+ return error;
+
+ *value = spi_rd.spi_rx;
+ return 0;
+}
+
+static void tsc2005_update_pen_state(struct tsc2005 *ts,
+ int x, int y, int pressure)
+{
+ if (pressure) {
+ input_report_abs(ts->idev, ABS_X, x);
+ input_report_abs(ts->idev, ABS_Y, y);
+ input_report_abs(ts->idev, ABS_PRESSURE, pressure);
+ if (!ts->pen_down) {
+ input_report_key(ts->idev, BTN_TOUCH, !!pressure);
+ ts->pen_down = true;
+ }
+ } else {
+ input_report_abs(ts->idev, ABS_PRESSURE, 0);
+ if (ts->pen_down) {
+ input_report_key(ts->idev, BTN_TOUCH, 0);
+ ts->pen_down = false;
+ }
+ }
+ input_sync(ts->idev);
+ dev_dbg(&ts->spi->dev, "point(%4d,%4d), pressure (%4d)\n", x, y,
+ pressure);
+}
+
+static irqreturn_t tsc2005_irq_thread(int irq, void *_ts)
+{
+ struct tsc2005 *ts = _ts;
+ unsigned long flags;
+ unsigned int pressure;
+ u32 x, y;
+ u32 z1, z2;
+ int error;
+
+ /* read the coordinates */
+ error = spi_sync(ts->spi, &ts->spi_read_msg);
+ if (unlikely(error))
+ goto out;
+
+ x = ts->spi_x.spi_rx;
+ y = ts->spi_y.spi_rx;
+ z1 = ts->spi_z1.spi_rx;
+ z2 = ts->spi_z2.spi_rx;
+
+ /* validate position */
+ if (unlikely(x > MAX_12BIT || y > MAX_12BIT))
+ goto out;
+
+ /* Skip reading if the pressure components are out of range */
+ if (unlikely(z1 == 0 || z2 > MAX_12BIT || z1 >= z2))
+ goto out;
+
+ /*
+ * Skip point if this is a pen down with the exact same values as
+ * the value before pen-up - that implies SPI fed us stale data
+ */
+ if (!ts->pen_down &&
+ ts->in_x == x && ts->in_y == y &&
+ ts->in_z1 == z1 && ts->in_z2 == z2) {
+ goto out;
+ }
+
+ /*
+ * At this point we are happy we have a valid and useful reading.
+ * Remember it for later comparisons. We may now begin downsampling.
+ */
+ ts->in_x = x;
+ ts->in_y = y;
+ ts->in_z1 = z1;
+ ts->in_z2 = z2;
+
+ /* Compute touch pressure resistance using equation #1 */
+ pressure = x * (z2 - z1) / z1;
+ pressure = pressure * ts->x_plate_ohm / 4096;
+ if (unlikely(pressure > MAX_12BIT))
+ goto out;
+
+ spin_lock_irqsave(&ts->lock, flags);
+
+ tsc2005_update_pen_state(ts, x, y, pressure);
+ mod_timer(&ts->penup_timer,
+ jiffies + msecs_to_jiffies(TSC2005_PENUP_TIME_MS));
+
+ spin_unlock_irqrestore(&ts->lock, flags);
+
+ ts->last_valid_interrupt = jiffies;
+out:
+ return IRQ_HANDLED;
+}
+
+static void tsc2005_penup_timer(unsigned long data)
+{
+ struct tsc2005 *ts = (struct tsc2005 *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ts->lock, flags);
+ tsc2005_update_pen_state(ts, 0, 0, 0);
+ spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+static void tsc2005_start_scan(struct tsc2005 *ts)
+{
+ tsc2005_write(ts, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE);
+ tsc2005_write(ts, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE);
+ tsc2005_write(ts, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE);
+ tsc2005_cmd(ts, TSC2005_CMD_NORMAL);
+}
+
+static void tsc2005_stop_scan(struct tsc2005 *ts)
+{
+ tsc2005_cmd(ts, TSC2005_CMD_STOP);
+}
+
+static void tsc2005_set_reset(struct tsc2005 *ts, bool enable)
+{
+ if (ts->reset_gpio >= 0)
+ gpio_set_value(ts->reset_gpio, enable);
+ else if (ts->set_reset)
+ ts->set_reset(enable);
+}
+
+/* must be called with ts->mutex held */
+static void __tsc2005_disable(struct tsc2005 *ts)
+{
+ tsc2005_stop_scan(ts);
+
+ disable_irq(ts->spi->irq);
+ del_timer_sync(&ts->penup_timer);
+
+ cancel_delayed_work_sync(&ts->esd_work);
+
+ enable_irq(ts->spi->irq);
+}
+
+/* must be called with ts->mutex held */
+static void __tsc2005_enable(struct tsc2005 *ts)
+{
+ tsc2005_start_scan(ts);
+
+ if (ts->esd_timeout && (ts->set_reset || ts->reset_gpio)) {
+ ts->last_valid_interrupt = jiffies;
+ schedule_delayed_work(&ts->esd_work,
+ round_jiffies_relative(
+ msecs_to_jiffies(ts->esd_timeout)));
+ }
+
+}
+
+static ssize_t tsc2005_selftest_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+ u16 temp_high;
+ u16 temp_high_orig;
+ u16 temp_high_test;
+ bool success = true;
+ int error;
+
+ mutex_lock(&ts->mutex);
+
+ /*
+ * Test TSC2005 communications via temp high register.
+ */
+ __tsc2005_disable(ts);
+
+ error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high_orig);
+ if (error) {
+ dev_warn(dev, "selftest failed: read error %d\n", error);
+ success = false;
+ goto out;
+ }
+
+ temp_high_test = (temp_high_orig - 1) & MAX_12BIT;
+
+ error = tsc2005_write(ts, TSC2005_REG_TEMP_HIGH, temp_high_test);
+ if (error) {
+ dev_warn(dev, "selftest failed: write error %d\n", error);
+ success = false;
+ goto out;
+ }
+
+ error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high);
+ if (error) {
+ dev_warn(dev, "selftest failed: read error %d after write\n",
+ error);
+ success = false;
+ goto out;
+ }
+
+ if (temp_high != temp_high_test) {
+ dev_warn(dev, "selftest failed: %d != %d\n",
+ temp_high, temp_high_test);
+ success = false;
+ }
+
+ /* hardware reset */
+ tsc2005_set_reset(ts, false);
+ usleep_range(100, 500); /* only 10us required */
+ tsc2005_set_reset(ts, true);
+
+ if (!success)
+ goto out;
+
+ /* test that the reset really happened */
+ error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high);
+ if (error) {
+ dev_warn(dev, "selftest failed: read error %d after reset\n",
+ error);
+ success = false;
+ goto out;
+ }
+
+ if (temp_high != temp_high_orig) {
+ dev_warn(dev, "selftest failed after reset: %d != %d\n",
+ temp_high, temp_high_orig);
+ success = false;
+ }
+
+out:
+ __tsc2005_enable(ts);
+ mutex_unlock(&ts->mutex);
+
+ return sprintf(buf, "%d\n", success);
+}
+
+static DEVICE_ATTR(selftest, S_IRUGO, tsc2005_selftest_show, NULL);
+
+static struct attribute *tsc2005_attrs[] = {
+ &dev_attr_selftest.attr,
+ NULL
+};
+
+static umode_t tsc2005_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct spi_device *spi = to_spi_device(dev);
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+ umode_t mode = attr->mode;
+
+ if (attr == &dev_attr_selftest.attr) {
+ if (!ts->set_reset && !ts->reset_gpio)
+ mode = 0;
+ }
+
+ return mode;
+}
+
+static const struct attribute_group tsc2005_attr_group = {
+ .is_visible = tsc2005_attr_is_visible,
+ .attrs = tsc2005_attrs,
+};
+
+static void tsc2005_esd_work(struct work_struct *work)
+{
+ struct tsc2005 *ts = container_of(work, struct tsc2005, esd_work.work);
+ int error;
+ u16 r;
+
+ if (!mutex_trylock(&ts->mutex)) {
+ /*
+ * If the mutex is taken, it means that disable or enable is in
+ * progress. In that case just reschedule the work. If the work
+ * is not needed, it will be canceled by disable.
+ */
+ goto reschedule;
+ }
+
+ if (time_is_after_jiffies(ts->last_valid_interrupt +
+ msecs_to_jiffies(ts->esd_timeout)))
+ goto out;
+
+ /* We should be able to read register without disabling interrupts. */
+ error = tsc2005_read(ts, TSC2005_REG_CFR0, &r);
+ if (!error &&
+ !((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK)) {
+ goto out;
+ }
+
+ /*
+ * If we could not read our known value from configuration register 0
+ * then we should reset the controller as if from power-up and start
+ * scanning again.
+ */
+ dev_info(&ts->spi->dev, "TSC2005 not responding - resetting\n");
+
+ disable_irq(ts->spi->irq);
+ del_timer_sync(&ts->penup_timer);
+
+ tsc2005_update_pen_state(ts, 0, 0, 0);
+
+ tsc2005_set_reset(ts, false);
+ usleep_range(100, 500); /* only 10us required */
+ tsc2005_set_reset(ts, true);
+
+ enable_irq(ts->spi->irq);
+ tsc2005_start_scan(ts);
+
+out:
+ mutex_unlock(&ts->mutex);
+reschedule:
+ /* re-arm the watchdog */
+ schedule_delayed_work(&ts->esd_work,
+ round_jiffies_relative(
+ msecs_to_jiffies(ts->esd_timeout)));
+}
+
+static int tsc2005_open(struct input_dev *input)
+{
+ struct tsc2005 *ts = input_get_drvdata(input);
+
+ mutex_lock(&ts->mutex);
+
+ if (!ts->suspended)
+ __tsc2005_enable(ts);
+
+ ts->opened = true;
+
+ mutex_unlock(&ts->mutex);
+
+ return 0;
+}
+
+static void tsc2005_close(struct input_dev *input)
+{
+ struct tsc2005 *ts = input_get_drvdata(input);
+
+ mutex_lock(&ts->mutex);
+
+ if (!ts->suspended)
+ __tsc2005_disable(ts);
+
+ ts->opened = false;
+
+ mutex_unlock(&ts->mutex);
+}
+
+static void tsc2005_setup_spi_xfer(struct tsc2005 *ts)
+{
+ tsc2005_setup_read(&ts->spi_x, TSC2005_REG_X, false);
+ tsc2005_setup_read(&ts->spi_y, TSC2005_REG_Y, false);
+ tsc2005_setup_read(&ts->spi_z1, TSC2005_REG_Z1, false);
+ tsc2005_setup_read(&ts->spi_z2, TSC2005_REG_Z2, true);
+
+ spi_message_init(&ts->spi_read_msg);
+ spi_message_add_tail(&ts->spi_x.spi_xfer, &ts->spi_read_msg);
+ spi_message_add_tail(&ts->spi_y.spi_xfer, &ts->spi_read_msg);
+ spi_message_add_tail(&ts->spi_z1.spi_xfer, &ts->spi_read_msg);
+ spi_message_add_tail(&ts->spi_z2.spi_xfer, &ts->spi_read_msg);
+}
+
+static int tsc2005_probe(struct spi_device *spi)
+{
+ const struct tsc2005_platform_data *pdata = dev_get_platdata(&spi->dev);
+ struct device_node *np = spi->dev.of_node;
+
+ struct tsc2005 *ts;
+ struct input_dev *input_dev;
+ unsigned int max_x = MAX_12BIT;
+ unsigned int max_y = MAX_12BIT;
+ unsigned int max_p = MAX_12BIT;
+ unsigned int fudge_x = TSC2005_DEF_X_FUZZ;
+ unsigned int fudge_y = TSC2005_DEF_Y_FUZZ;
+ unsigned int fudge_p = TSC2005_DEF_P_FUZZ;
+ unsigned int x_plate_ohm = TSC2005_DEF_RESISTOR;
+ unsigned int esd_timeout;
+ int error;
+
+ if (!np && !pdata) {
+ dev_err(&spi->dev, "no platform data\n");
+ return -ENODEV;
+ }
+
+ if (spi->irq <= 0) {
+ dev_err(&spi->dev, "no irq\n");
+ return -ENODEV;
+ }
+
+ if (pdata) {
+ fudge_x = pdata->ts_x_fudge;
+ fudge_y = pdata->ts_y_fudge;
+ fudge_p = pdata->ts_pressure_fudge;
+ max_x = pdata->ts_x_max;
+ max_y = pdata->ts_y_max;
+ max_p = pdata->ts_pressure_max;
+ x_plate_ohm = pdata->ts_x_plate_ohm;
+ esd_timeout = pdata->esd_timeout_ms;
+ } else {
+ x_plate_ohm = TSC2005_DEF_RESISTOR;
+ of_property_read_u32(np, "ti,x-plate-ohms", &x_plate_ohm);
+ esd_timeout = 0;
+ of_property_read_u32(np, "ti,esd-recovery-timeout-ms",
+ &esd_timeout);
+ }
+
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ if (!spi->max_speed_hz)
+ spi->max_speed_hz = TSC2005_SPI_MAX_SPEED_HZ;
+
+ error = spi_setup(spi);
+ if (error)
+ return error;
+
+ ts = devm_kzalloc(&spi->dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ input_dev = devm_input_allocate_device(&spi->dev);
+ if (!input_dev)
+ return -ENOMEM;
+
+ ts->spi = spi;
+ ts->idev = input_dev;
+
+ ts->x_plate_ohm = x_plate_ohm;
+ ts->esd_timeout = esd_timeout;
+
+ if (np) {
+ ts->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
+ if (ts->reset_gpio == -EPROBE_DEFER)
+ return ts->reset_gpio;
+ if (ts->reset_gpio < 0) {
+ dev_err(&spi->dev, "error acquiring reset gpio: %d\n",
+ ts->reset_gpio);
+ return ts->reset_gpio;
+ }
+
+ error = devm_gpio_request_one(&spi->dev, ts->reset_gpio, 0,
+ "reset-gpios");
+ if (error) {
+ dev_err(&spi->dev, "error requesting reset gpio: %d\n",
+ error);
+ return error;
+ }
+
+ ts->vio = devm_regulator_get(&spi->dev, "vio");
+ if (IS_ERR(ts->vio)) {
+ error = PTR_ERR(ts->vio);
+ dev_err(&spi->dev, "vio regulator missing (%d)", error);
+ return error;
+ }
+ } else {
+ ts->reset_gpio = -1;
+ ts->set_reset = pdata->set_reset;
+ }
+
+ mutex_init(&ts->mutex);
+
+ spin_lock_init(&ts->lock);
+ setup_timer(&ts->penup_timer, tsc2005_penup_timer, (unsigned long)ts);
+
+ INIT_DELAYED_WORK(&ts->esd_work, tsc2005_esd_work);
+
+ tsc2005_setup_spi_xfer(ts);
+
+ snprintf(ts->phys, sizeof(ts->phys),
+ "%s/input-ts", dev_name(&spi->dev));
+
+ input_dev->name = "TSC2005 touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = BUS_SPI;
+ input_dev->dev.parent = &spi->dev;
+ input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, max_x, fudge_x, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, max_y, fudge_y, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0);
+
+ if (np)
+ touchscreen_parse_of_params(input_dev);
+
+ input_dev->open = tsc2005_open;
+ input_dev->close = tsc2005_close;
+
+ input_set_drvdata(input_dev, ts);
+
+ /* Ensure the touchscreen is off */
+ tsc2005_stop_scan(ts);
+
+ error = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
+ tsc2005_irq_thread,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "tsc2005", ts);
+ if (error) {
+ dev_err(&spi->dev, "Failed to request irq, err: %d\n", error);
+ return error;
+ }
+
+ /* enable regulator for DT */
+ if (ts->vio) {
+ error = regulator_enable(ts->vio);
+ if (error)
+ return error;
+ }
+
+ spi_set_drvdata(spi, ts);
+ error = sysfs_create_group(&spi->dev.kobj, &tsc2005_attr_group);
+ if (error) {
+ dev_err(&spi->dev,
+ "Failed to create sysfs attributes, err: %d\n", error);
+ goto disable_regulator;
+ }
+
+ error = input_register_device(ts->idev);
+ if (error) {
+ dev_err(&spi->dev,
+ "Failed to register input device, err: %d\n", error);
+ goto err_remove_sysfs;
+ }
+
+ irq_set_irq_wake(spi->irq, 1);
+ return 0;
+
+err_remove_sysfs:
+ sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group);
+disable_regulator:
+ if (ts->vio)
+ regulator_disable(ts->vio);
+ return error;
+}
+
+static int tsc2005_remove(struct spi_device *spi)
+{
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+
+ sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group);
+
+ if (ts->vio)
+ regulator_disable(ts->vio);
+
+ return 0;
+}
+
+static int __maybe_unused tsc2005_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+
+ mutex_lock(&ts->mutex);
+
+ if (!ts->suspended && ts->opened)
+ __tsc2005_disable(ts);
+
+ ts->suspended = true;
+
+ mutex_unlock(&ts->mutex);
+
+ return 0;
+}
+
+static int __maybe_unused tsc2005_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+
+ mutex_lock(&ts->mutex);
+
+ if (ts->suspended && ts->opened)
+ __tsc2005_enable(ts);
+
+ ts->suspended = false;
+
+ mutex_unlock(&ts->mutex);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(tsc2005_pm_ops, tsc2005_suspend, tsc2005_resume);
+
+static struct spi_driver tsc2005_driver = {
+ .driver = {
+ .name = "tsc2005",
+ .owner = THIS_MODULE,
+ .pm = &tsc2005_pm_ops,
+ },
+ .probe = tsc2005_probe,
+ .remove = tsc2005_remove,
+};
+
+module_spi_driver(tsc2005_driver);
+
+MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
+MODULE_DESCRIPTION("TSC2005 Touchscreen Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:tsc2005");
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
new file mode 100644
index 000000000..ccc8aa615
--- /dev/null
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -0,0 +1,497 @@
+/*
+ * drivers/input/touchscreen/tsc2007.c
+ *
+ * Copyright (c) 2008 MtekVision Co., Ltd.
+ * Kwangwoo Lee <kwlee@mtekvision.com>
+ *
+ * Using code from:
+ * - 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/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/i2c/tsc2007.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
+#define TSC2007_MEASURE_AUX (0x2 << 4)
+#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
+#define TSC2007_ACTIVATE_XN (0x8 << 4)
+#define TSC2007_ACTIVATE_YN (0x9 << 4)
+#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
+#define TSC2007_SETUP (0xb << 4)
+#define TSC2007_MEASURE_X (0xc << 4)
+#define TSC2007_MEASURE_Y (0xd << 4)
+#define TSC2007_MEASURE_Z1 (0xe << 4)
+#define TSC2007_MEASURE_Z2 (0xf << 4)
+
+#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
+#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
+
+#define TSC2007_12BIT (0x0 << 1)
+#define TSC2007_8BIT (0x1 << 1)
+
+#define MAX_12BIT ((1 << 12) - 1)
+
+#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
+
+#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
+#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
+#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
+#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
+#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
+
+struct ts_event {
+ u16 x;
+ u16 y;
+ u16 z1, z2;
+};
+
+struct tsc2007 {
+ struct input_dev *input;
+ char phys[32];
+
+ struct i2c_client *client;
+
+ u16 model;
+ u16 x_plate_ohms;
+ u16 max_rt;
+ unsigned long poll_period; /* in jiffies */
+ int fuzzx;
+ int fuzzy;
+ int fuzzz;
+
+ unsigned gpio;
+ int irq;
+
+ wait_queue_head_t wait;
+ bool stopped;
+
+ int (*get_pendown_state)(struct device *);
+ void (*clear_penirq)(void);
+};
+
+static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
+{
+ s32 data;
+ u16 val;
+
+ data = i2c_smbus_read_word_data(tsc->client, cmd);
+ if (data < 0) {
+ dev_err(&tsc->client->dev, "i2c io error: %d\n", data);
+ return data;
+ }
+
+ /* The protocol and raw data format from i2c interface:
+ * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
+ * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
+ */
+ val = swab16(data) >> 4;
+
+ dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);
+
+ return val;
+}
+
+static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
+{
+ /* y- still on; turn on only y+ (and ADC) */
+ tc->y = tsc2007_xfer(tsc, READ_Y);
+
+ /* turn y- off, x+ on, then leave in lowpower */
+ tc->x = tsc2007_xfer(tsc, READ_X);
+
+ /* turn y+ off, x- on; we'll use formula #1 */
+ tc->z1 = tsc2007_xfer(tsc, READ_Z1);
+ tc->z2 = tsc2007_xfer(tsc, READ_Z2);
+
+ /* Prepare for next touch reading - power down ADC, enable PENIRQ */
+ tsc2007_xfer(tsc, PWRDOWN);
+}
+
+static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
+{
+ u32 rt = 0;
+
+ /* range filtering */
+ if (tc->x == MAX_12BIT)
+ tc->x = 0;
+
+ if (likely(tc->x && tc->z1)) {
+ /* compute touch pressure resistance using equation #1 */
+ rt = tc->z2 - tc->z1;
+ rt *= tc->x;
+ rt *= tsc->x_plate_ohms;
+ rt /= tc->z1;
+ rt = (rt + 2047) >> 12;
+ }
+
+ return rt;
+}
+
+static bool tsc2007_is_pen_down(struct tsc2007 *ts)
+{
+ /*
+ * NOTE: We can't rely on the pressure to determine the pen down
+ * state, even though this controller has a pressure sensor.
+ * The pressure value can fluctuate for quite a while after
+ * lifting the pen and in some cases may not even settle at the
+ * expected value.
+ *
+ * The only safe way to check for the pen up condition is in the
+ * work function by reading the pen signal state (it's a GPIO
+ * and IRQ). Unfortunately such callback is not always available,
+ * in that case we assume that the pen is down and expect caller
+ * to fall back on the pressure reading.
+ */
+
+ if (!ts->get_pendown_state)
+ return true;
+
+ return ts->get_pendown_state(&ts->client->dev);
+}
+
+static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
+{
+ struct tsc2007 *ts = handle;
+ struct input_dev *input = ts->input;
+ struct ts_event tc;
+ u32 rt;
+
+ while (!ts->stopped && tsc2007_is_pen_down(ts)) {
+
+ /* pen is down, continue with the measurement */
+ tsc2007_read_values(ts, &tc);
+
+ rt = tsc2007_calculate_pressure(ts, &tc);
+
+ if (!rt && !ts->get_pendown_state) {
+ /*
+ * If pressure reported is 0 and we don't have
+ * callback to check pendown state, we have to
+ * assume that pen was lifted up.
+ */
+ break;
+ }
+
+ if (rt <= ts->max_rt) {
+ dev_dbg(&ts->client->dev,
+ "DOWN point(%4d,%4d), pressure (%4u)\n",
+ tc.x, tc.y, rt);
+
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_X, tc.x);
+ input_report_abs(input, ABS_Y, tc.y);
+ input_report_abs(input, ABS_PRESSURE, rt);
+
+ input_sync(input);
+
+ } else {
+ /*
+ * Sample found inconsistent by debouncing or pressure is
+ * beyond the maximum. Don't report it to user space,
+ * repeat at least once more the measurement.
+ */
+ dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
+ }
+
+ wait_event_timeout(ts->wait, ts->stopped, ts->poll_period);
+ }
+
+ dev_dbg(&ts->client->dev, "UP\n");
+
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_sync(input);
+
+ if (ts->clear_penirq)
+ ts->clear_penirq();
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
+{
+ struct tsc2007 *ts = handle;
+
+ if (tsc2007_is_pen_down(ts))
+ return IRQ_WAKE_THREAD;
+
+ if (ts->clear_penirq)
+ ts->clear_penirq();
+
+ return IRQ_HANDLED;
+}
+
+static void tsc2007_stop(struct tsc2007 *ts)
+{
+ ts->stopped = true;
+ mb();
+ wake_up(&ts->wait);
+
+ disable_irq(ts->irq);
+}
+
+static int tsc2007_open(struct input_dev *input_dev)
+{
+ struct tsc2007 *ts = input_get_drvdata(input_dev);
+ int err;
+
+ ts->stopped = false;
+ mb();
+
+ enable_irq(ts->irq);
+
+ /* Prepare for touch readings - power down ADC and enable PENIRQ */
+ err = tsc2007_xfer(ts, PWRDOWN);
+ if (err < 0) {
+ tsc2007_stop(ts);
+ return err;
+ }
+
+ return 0;
+}
+
+static void tsc2007_close(struct input_dev *input_dev)
+{
+ struct tsc2007 *ts = input_get_drvdata(input_dev);
+
+ tsc2007_stop(ts);
+}
+
+#ifdef CONFIG_OF
+static int tsc2007_get_pendown_state_gpio(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tsc2007 *ts = i2c_get_clientdata(client);
+
+ return !gpio_get_value(ts->gpio);
+}
+
+static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
+{
+ struct device_node *np = client->dev.of_node;
+ u32 val32;
+ u64 val64;
+
+ if (!np) {
+ dev_err(&client->dev, "missing device tree data\n");
+ return -EINVAL;
+ }
+
+ if (!of_property_read_u32(np, "ti,max-rt", &val32))
+ ts->max_rt = val32;
+ else
+ ts->max_rt = MAX_12BIT;
+
+ if (!of_property_read_u32(np, "ti,fuzzx", &val32))
+ ts->fuzzx = val32;
+
+ if (!of_property_read_u32(np, "ti,fuzzy", &val32))
+ ts->fuzzy = val32;
+
+ if (!of_property_read_u32(np, "ti,fuzzz", &val32))
+ ts->fuzzz = val32;
+
+ if (!of_property_read_u64(np, "ti,poll-period", &val64))
+ ts->poll_period = msecs_to_jiffies(val64);
+ else
+ ts->poll_period = msecs_to_jiffies(1);
+
+ if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
+ ts->x_plate_ohms = val32;
+ } else {
+ dev_err(&client->dev, "missing ti,x-plate-ohms devicetree property.");
+ return -EINVAL;
+ }
+
+ ts->gpio = of_get_gpio(np, 0);
+ if (gpio_is_valid(ts->gpio))
+ ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
+ else
+ dev_warn(&client->dev,
+ "GPIO not specified in DT (of_get_gpio returned %d)\n",
+ ts->gpio);
+
+ return 0;
+}
+#else
+static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
+{
+ dev_err(&client->dev, "platform data is required!\n");
+ return -EINVAL;
+}
+#endif
+
+static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
+ const struct tsc2007_platform_data *pdata,
+ const struct i2c_device_id *id)
+{
+ ts->model = pdata->model;
+ ts->x_plate_ohms = pdata->x_plate_ohms;
+ ts->max_rt = pdata->max_rt ? : MAX_12BIT;
+ ts->poll_period = msecs_to_jiffies(pdata->poll_period ? : 1);
+ ts->get_pendown_state = pdata->get_pendown_state;
+ ts->clear_penirq = pdata->clear_penirq;
+ ts->fuzzx = pdata->fuzzx;
+ ts->fuzzy = pdata->fuzzy;
+ ts->fuzzz = pdata->fuzzz;
+
+ if (pdata->x_plate_ohms == 0) {
+ dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void tsc2007_call_exit_platform_hw(void *data)
+{
+ struct device *dev = data;
+ const struct tsc2007_platform_data *pdata = dev_get_platdata(dev);
+
+ pdata->exit_platform_hw();
+}
+
+static int tsc2007_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
+ struct tsc2007 *ts;
+ struct input_dev *input_dev;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -EIO;
+
+ ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ if (pdata)
+ err = tsc2007_probe_pdev(client, ts, pdata, id);
+ else
+ err = tsc2007_probe_dt(client, ts);
+ if (err)
+ return err;
+
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!input_dev)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ts);
+
+ ts->client = client;
+ ts->irq = client->irq;
+ ts->input = input_dev;
+ init_waitqueue_head(&ts->wait);
+
+ snprintf(ts->phys, sizeof(ts->phys),
+ "%s/input0", dev_name(&client->dev));
+
+ input_dev->name = "TSC2007 Touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = BUS_I2C;
+
+ input_dev->open = tsc2007_open;
+ input_dev->close = tsc2007_close;
+
+ input_set_drvdata(input_dev, ts);
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, ts->fuzzx, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
+ ts->fuzzz, 0);
+
+ if (pdata) {
+ if (pdata->exit_platform_hw) {
+ err = devm_add_action(&client->dev,
+ tsc2007_call_exit_platform_hw,
+ &client->dev);
+ if (err) {
+ dev_err(&client->dev,
+ "Failed to register exit_platform_hw action, %d\n",
+ err);
+ return err;
+ }
+ }
+
+ if (pdata->init_platform_hw)
+ pdata->init_platform_hw();
+ }
+
+ err = devm_request_threaded_irq(&client->dev, ts->irq,
+ tsc2007_hard_irq, tsc2007_soft_irq,
+ IRQF_ONESHOT,
+ client->dev.driver->name, ts);
+ if (err) {
+ dev_err(&client->dev, "Failed to request irq %d: %d\n",
+ ts->irq, err);
+ return err;
+ }
+
+ tsc2007_stop(ts);
+
+ err = input_register_device(input_dev);
+ if (err) {
+ dev_err(&client->dev,
+ "Failed to register input device: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id tsc2007_idtable[] = {
+ { "tsc2007", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
+
+#ifdef CONFIG_OF
+static const struct of_device_id tsc2007_of_match[] = {
+ { .compatible = "ti,tsc2007" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tsc2007_of_match);
+#endif
+
+static struct i2c_driver tsc2007_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tsc2007",
+ .of_match_table = of_match_ptr(tsc2007_of_match),
+ },
+ .id_table = tsc2007_idtable,
+ .probe = tsc2007_probe,
+};
+
+module_i2c_driver(tsc2007_driver);
+
+MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");
+MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tsc40.c b/drivers/input/touchscreen/tsc40.c
new file mode 100644
index 000000000..29687872c
--- /dev/null
+++ b/drivers/input/touchscreen/tsc40.c
@@ -0,0 +1,172 @@
+/*
+ * TSC-40 serial touchscreen driver. It should be compatible with
+ * TSC-10 and 25.
+ *
+ * Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ * License: GPLv2 as published by the FSF.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define PACKET_LENGTH 5
+struct tsc_ser {
+ struct input_dev *dev;
+ struct serio *serio;
+ u32 idx;
+ unsigned char data[PACKET_LENGTH];
+ char phys[32];
+};
+
+static void tsc_process_data(struct tsc_ser *ptsc)
+{
+ struct input_dev *dev = ptsc->dev;
+ u8 *data = ptsc->data;
+ u32 x;
+ u32 y;
+
+ x = ((data[1] & 0x03) << 8) | data[2];
+ y = ((data[3] & 0x03) << 8) | data[4];
+
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ input_report_key(dev, BTN_TOUCH, 1);
+
+ input_sync(dev);
+}
+
+static irqreturn_t tsc_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct tsc_ser *ptsc = serio_get_drvdata(serio);
+ struct input_dev *dev = ptsc->dev;
+
+ ptsc->data[ptsc->idx] = data;
+ switch (ptsc->idx++) {
+ case 0:
+ if (unlikely((data & 0x3e) != 0x10)) {
+ dev_dbg(&serio->dev,
+ "unsynchronized packet start (0x%02x)\n", data);
+ ptsc->idx = 0;
+ } else if (!(data & 0x01)) {
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_sync(dev);
+ ptsc->idx = 0;
+ }
+ break;
+
+ case 1:
+ case 3:
+ if (unlikely(data & 0xfc)) {
+ dev_dbg(&serio->dev,
+ "unsynchronized data 0x%02x at offset %d\n",
+ data, ptsc->idx - 1);
+ ptsc->idx = 0;
+ }
+ break;
+
+ case 4:
+ tsc_process_data(ptsc);
+ ptsc->idx = 0;
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int tsc_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct tsc_ser *ptsc;
+ struct input_dev *input_dev;
+ int error;
+
+ ptsc = kzalloc(sizeof(struct tsc_ser), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ptsc || !input_dev) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ ptsc->serio = serio;
+ ptsc->dev = input_dev;
+ snprintf(ptsc->phys, sizeof(ptsc->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "TSC-10/25/40 Serial TouchScreen";
+ input_dev->phys = ptsc->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TSC40;
+ input_dev->id.product = 40;
+ input_dev->id.version = 0x0001;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_abs_params(ptsc->dev, ABS_X, 0, 0x3ff, 0, 0);
+ input_set_abs_params(ptsc->dev, ABS_Y, 0, 0x3ff, 0, 0);
+
+ serio_set_drvdata(serio, ptsc);
+
+ error = serio_open(serio, drv);
+ if (error)
+ goto fail2;
+
+ error = input_register_device(ptsc->dev);
+ if (error)
+ goto fail3;
+
+ return 0;
+
+fail3:
+ serio_close(serio);
+fail2:
+ serio_set_drvdata(serio, NULL);
+fail1:
+ input_free_device(input_dev);
+ kfree(ptsc);
+ return error;
+}
+
+static void tsc_disconnect(struct serio *serio)
+{
+ struct tsc_ser *ptsc = serio_get_drvdata(serio);
+
+ serio_close(serio);
+
+ input_unregister_device(ptsc->dev);
+ kfree(ptsc);
+
+ serio_set_drvdata(serio, NULL);
+}
+
+static struct serio_device_id tsc_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TSC40,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(serio, tsc_serio_ids);
+
+#define DRIVER_DESC "TSC-10/25/40 serial touchscreen driver"
+
+static struct serio_driver tsc_drv = {
+ .driver = {
+ .name = "tsc40",
+ },
+ .description = DRIVER_DESC,
+ .id_table = tsc_serio_ids,
+ .interrupt = tsc_interrupt,
+ .connect = tsc_connect,
+ .disconnect = tsc_disconnect,
+};
+
+module_serio_driver(tsc_drv);
+
+MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c
new file mode 100644
index 000000000..c1e23cfc6
--- /dev/null
+++ b/drivers/input/touchscreen/ucb1400_ts.c
@@ -0,0 +1,463 @@
+/*
+ * Philips UCB1400 touchscreen driver
+ *
+ * Author: Nicolas Pitre
+ * Created: September 25, 2006
+ * Copyright: MontaVista Software, Inc.
+ *
+ * Spliting done by: Marek Vasut <marek.vasut@gmail.com>
+ * If something doesn't work and it worked before spliting, e-mail me,
+ * dont bother Nicolas please ;-)
+ *
+ * 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.
+ *
+ * This code is heavily based on ucb1x00-*.c copyrighted by Russell King
+ * covering the UCB1100, UCB1200 and UCB1300.. Support for the UCB1400 has
+ * been made separate from ucb1x00-core/ucb1x00-ts on Russell's request.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/ucb1400.h>
+
+#define UCB1400_TS_POLL_PERIOD 10 /* ms */
+
+static bool adcsync;
+static int ts_delay = 55; /* us */
+static int ts_delay_pressure; /* us */
+
+/* Switch to interrupt mode. */
+static void ucb1400_ts_mode_int(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+ UCB_TS_CR_MODE_INT);
+}
+
+/*
+ * Switch to pressure mode, and read pressure. We don't need to wait
+ * here, since both plates are being driven.
+ */
+static unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+
+ udelay(ts_delay_pressure);
+
+ return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync);
+}
+
+/*
+ * Switch to X position mode and measure Y plate. We switch the plate
+ * configuration in pressure mode, then switch to position mode. This
+ * gives a faster response time. Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+ udelay(ts_delay);
+
+ return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync);
+}
+
+/*
+ * Switch to Y position mode and measure X plate. We switch the plate
+ * configuration in pressure mode, then switch to position mode. This
+ * gives a faster response time. Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+ udelay(ts_delay);
+
+ return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPX, adcsync);
+}
+
+/*
+ * Switch to X plate resistance mode. Set MX to ground, PX to
+ * supply. Measure current.
+ */
+static unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+ return ucb1400_adc_read(ucb->ac97, 0, adcsync);
+}
+
+/*
+ * Switch to Y plate resistance mode. Set MY to ground, PY to
+ * supply. Measure current.
+ */
+static unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+ return ucb1400_adc_read(ucb->ac97, 0, adcsync);
+}
+
+static int ucb1400_ts_pen_up(struct ucb1400_ts *ucb)
+{
+ unsigned short val = ucb1400_reg_read(ucb->ac97, UCB_TS_CR);
+
+ return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW);
+}
+
+static void ucb1400_ts_irq_enable(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, UCB_IE_TSPX);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_TSPX);
+}
+
+static void ucb1400_ts_irq_disable(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0);
+}
+
+static void ucb1400_ts_report_event(struct input_dev *idev, u16 pressure, u16 x, u16 y)
+{
+ input_report_abs(idev, ABS_X, x);
+ input_report_abs(idev, ABS_Y, y);
+ input_report_abs(idev, ABS_PRESSURE, pressure);
+ input_report_key(idev, BTN_TOUCH, 1);
+ input_sync(idev);
+}
+
+static void ucb1400_ts_event_release(struct input_dev *idev)
+{
+ input_report_abs(idev, ABS_PRESSURE, 0);
+ input_report_key(idev, BTN_TOUCH, 0);
+ input_sync(idev);
+}
+
+static void ucb1400_clear_pending_irq(struct ucb1400_ts *ucb)
+{
+ unsigned int isr;
+
+ isr = ucb1400_reg_read(ucb->ac97, UCB_IE_STATUS);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, isr);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
+
+ if (isr & UCB_IE_TSPX)
+ ucb1400_ts_irq_disable(ucb);
+ else
+ dev_dbg(&ucb->ts_idev->dev,
+ "ucb1400: unexpected IE_STATUS = %#x\n", isr);
+}
+
+/*
+ * A restriction with interrupts exists when using the ucb1400, as
+ * the codec read/write routines may sleep while waiting for codec
+ * access completion and uses semaphores for access control to the
+ * AC97 bus. Therefore the driver is forced to use threaded interrupt
+ * handler.
+ */
+static irqreturn_t ucb1400_irq(int irqnr, void *devid)
+{
+ struct ucb1400_ts *ucb = devid;
+ unsigned int x, y, p;
+ bool penup;
+
+ if (unlikely(irqnr != ucb->irq))
+ return IRQ_NONE;
+
+ ucb1400_clear_pending_irq(ucb);
+
+ /* Start with a small delay before checking pendown state */
+ msleep(UCB1400_TS_POLL_PERIOD);
+
+ while (!ucb->stopped && !(penup = ucb1400_ts_pen_up(ucb))) {
+
+ ucb1400_adc_enable(ucb->ac97);
+ x = ucb1400_ts_read_xpos(ucb);
+ y = ucb1400_ts_read_ypos(ucb);
+ p = ucb1400_ts_read_pressure(ucb);
+ ucb1400_adc_disable(ucb->ac97);
+
+ ucb1400_ts_report_event(ucb->ts_idev, p, x, y);
+
+ wait_event_timeout(ucb->ts_wait, ucb->stopped,
+ msecs_to_jiffies(UCB1400_TS_POLL_PERIOD));
+ }
+
+ ucb1400_ts_event_release(ucb->ts_idev);
+
+ if (!ucb->stopped) {
+ /* Switch back to interrupt mode. */
+ ucb1400_ts_mode_int(ucb);
+ ucb1400_ts_irq_enable(ucb);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void ucb1400_ts_stop(struct ucb1400_ts *ucb)
+{
+ /* Signal IRQ thread to stop polling and disable the handler. */
+ ucb->stopped = true;
+ mb();
+ wake_up(&ucb->ts_wait);
+ disable_irq(ucb->irq);
+
+ ucb1400_ts_irq_disable(ucb);
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0);
+}
+
+/* Must be called with ts->lock held */
+static void ucb1400_ts_start(struct ucb1400_ts *ucb)
+{
+ /* Tell IRQ thread that it may poll the device. */
+ ucb->stopped = false;
+ mb();
+
+ ucb1400_ts_mode_int(ucb);
+ ucb1400_ts_irq_enable(ucb);
+
+ enable_irq(ucb->irq);
+}
+
+static int ucb1400_ts_open(struct input_dev *idev)
+{
+ struct ucb1400_ts *ucb = input_get_drvdata(idev);
+
+ ucb1400_ts_start(ucb);
+
+ return 0;
+}
+
+static void ucb1400_ts_close(struct input_dev *idev)
+{
+ struct ucb1400_ts *ucb = input_get_drvdata(idev);
+
+ ucb1400_ts_stop(ucb);
+}
+
+#ifndef NO_IRQ
+#define NO_IRQ 0
+#endif
+
+/*
+ * Try to probe our interrupt, rather than relying on lots of
+ * hard-coded machine dependencies.
+ */
+static int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb,
+ struct platform_device *pdev)
+{
+ unsigned long mask, timeout;
+
+ mask = probe_irq_on();
+
+ /* Enable the ADC interrupt. */
+ ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, UCB_IE_ADC);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_ADC);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
+
+ /* Cause an ADC interrupt. */
+ ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA);
+ ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START);
+
+ /* Wait for the conversion to complete. */
+ timeout = jiffies + HZ/2;
+ while (!(ucb1400_reg_read(ucb->ac97, UCB_ADC_DATA) &
+ UCB_ADC_DAT_VALID)) {
+ cpu_relax();
+ if (time_after(jiffies, timeout)) {
+ dev_err(&pdev->dev, "timed out in IRQ probe\n");
+ probe_irq_off(mask);
+ return -ENODEV;
+ }
+ }
+ ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, 0);
+
+ /* Disable and clear interrupt. */
+ ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, 0);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
+
+ /* Read triggered interrupt. */
+ ucb->irq = probe_irq_off(mask);
+ if (ucb->irq < 0 || ucb->irq == NO_IRQ)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int ucb1400_ts_probe(struct platform_device *pdev)
+{
+ struct ucb1400_ts *ucb = dev_get_platdata(&pdev->dev);
+ int error, x_res, y_res;
+ u16 fcsr;
+
+ ucb->ts_idev = input_allocate_device();
+ if (!ucb->ts_idev) {
+ error = -ENOMEM;
+ goto err;
+ }
+
+ /* Only in case the IRQ line wasn't supplied, try detecting it */
+ if (ucb->irq < 0) {
+ error = ucb1400_ts_detect_irq(ucb, pdev);
+ if (error) {
+ dev_err(&pdev->dev, "IRQ probe failed\n");
+ goto err_free_devs;
+ }
+ }
+ dev_dbg(&pdev->dev, "found IRQ %d\n", ucb->irq);
+
+ init_waitqueue_head(&ucb->ts_wait);
+
+ input_set_drvdata(ucb->ts_idev, ucb);
+
+ ucb->ts_idev->dev.parent = &pdev->dev;
+ ucb->ts_idev->name = "UCB1400 touchscreen interface";
+ ucb->ts_idev->id.vendor = ucb1400_reg_read(ucb->ac97,
+ AC97_VENDOR_ID1);
+ ucb->ts_idev->id.product = ucb->id;
+ ucb->ts_idev->open = ucb1400_ts_open;
+ ucb->ts_idev->close = ucb1400_ts_close;
+ ucb->ts_idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
+ ucb->ts_idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ /*
+ * Enable ADC filter to prevent horrible jitter on Colibri.
+ * This also further reduces jitter on boards where ADCSYNC
+ * pin is connected.
+ */
+ fcsr = ucb1400_reg_read(ucb->ac97, UCB_FCSR);
+ ucb1400_reg_write(ucb->ac97, UCB_FCSR, fcsr | UCB_FCSR_AVE);
+
+ ucb1400_adc_enable(ucb->ac97);
+ x_res = ucb1400_ts_read_xres(ucb);
+ y_res = ucb1400_ts_read_yres(ucb);
+ ucb1400_adc_disable(ucb->ac97);
+ dev_dbg(&pdev->dev, "x/y = %d/%d\n", x_res, y_res);
+
+ input_set_abs_params(ucb->ts_idev, ABS_X, 0, x_res, 0, 0);
+ input_set_abs_params(ucb->ts_idev, ABS_Y, 0, y_res, 0, 0);
+ input_set_abs_params(ucb->ts_idev, ABS_PRESSURE, 0, 0, 0, 0);
+
+ ucb1400_ts_stop(ucb);
+
+ error = request_threaded_irq(ucb->irq, NULL, ucb1400_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "UCB1400", ucb);
+ if (error) {
+ dev_err(&pdev->dev,
+ "unable to grab irq%d: %d\n", ucb->irq, error);
+ goto err_free_devs;
+ }
+
+ error = input_register_device(ucb->ts_idev);
+ if (error)
+ goto err_free_irq;
+
+ return 0;
+
+err_free_irq:
+ free_irq(ucb->irq, ucb);
+err_free_devs:
+ input_free_device(ucb->ts_idev);
+err:
+ return error;
+}
+
+static int ucb1400_ts_remove(struct platform_device *pdev)
+{
+ struct ucb1400_ts *ucb = dev_get_platdata(&pdev->dev);
+
+ free_irq(ucb->irq, ucb);
+ input_unregister_device(ucb->ts_idev);
+
+ return 0;
+}
+
+static int __maybe_unused ucb1400_ts_suspend(struct device *dev)
+{
+ struct ucb1400_ts *ucb = dev_get_platdata(dev);
+ struct input_dev *idev = ucb->ts_idev;
+
+ mutex_lock(&idev->mutex);
+
+ if (idev->users)
+ ucb1400_ts_start(ucb);
+
+ mutex_unlock(&idev->mutex);
+ return 0;
+}
+
+static int __maybe_unused ucb1400_ts_resume(struct device *dev)
+{
+ struct ucb1400_ts *ucb = dev_get_platdata(dev);
+ struct input_dev *idev = ucb->ts_idev;
+
+ mutex_lock(&idev->mutex);
+
+ if (idev->users)
+ ucb1400_ts_stop(ucb);
+
+ mutex_unlock(&idev->mutex);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ucb1400_ts_pm_ops,
+ ucb1400_ts_suspend, ucb1400_ts_resume);
+
+static struct platform_driver ucb1400_ts_driver = {
+ .probe = ucb1400_ts_probe,
+ .remove = ucb1400_ts_remove,
+ .driver = {
+ .name = "ucb1400_ts",
+ .pm = &ucb1400_ts_pm_ops,
+ },
+};
+module_platform_driver(ucb1400_ts_driver);
+
+module_param(adcsync, bool, 0444);
+MODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin.");
+
+module_param(ts_delay, int, 0444);
+MODULE_PARM_DESC(ts_delay, "Delay between panel setup and"
+ " position read. Default = 55us.");
+
+module_param(ts_delay_pressure, int, 0444);
+MODULE_PARM_DESC(ts_delay_pressure,
+ "delay between panel setup and pressure read."
+ " Default = 0us.");
+
+MODULE_DESCRIPTION("Philips UCB1400 touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
new file mode 100644
index 000000000..f2c6c352c
--- /dev/null
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -0,0 +1,1769 @@
+/******************************************************************************
+ * usbtouchscreen.c
+ * Driver for USB Touchscreens, supporting those devices:
+ * - eGalax Touchkit
+ * includes eTurboTouch CT-410/510/700
+ * - 3M/Microtouch EX II series
+ * - ITM
+ * - PanJit TouchSet
+ * - eTurboTouch
+ * - Gunze AHL61
+ * - DMC TSC-10/25
+ * - IRTOUCHSYSTEMS/UNITOP
+ * - IdealTEK URTC1000
+ * - General Touch
+ * - GoTop Super_Q2/GogoPen/PenPower tablets
+ * - JASTEC USB touch controller/DigiTech DTR-02U
+ * - Zytronic capacitive touchscreen
+ * - NEXIO/iNexio
+ * - Elo TouchSystems 2700 IntelliTouch
+ * - EasyTouch USB Dual/Multi touch controller from Data Modul
+ *
+ * Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch>
+ * Copyright (C) by Todd E. Johnson (mtouchusb.c)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Driver is based on touchkitusb.c
+ * - ITM parts are from itmtouch.c
+ * - 3M parts are from mtouchusb.c
+ * - PanJit parts are from an unmerged driver by Lanslott Gish
+ * - DMC TSC 10/25 are from Holger Schurig, with ideas from an unmerged
+ * driver from Marius Vollmer
+ *
+ *****************************************************************************/
+
+//#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/hid.h>
+
+
+#define DRIVER_VERSION "v0.6"
+#define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>"
+#define DRIVER_DESC "USB Touchscreen Driver"
+
+static bool swap_xy;
+module_param(swap_xy, bool, 0644);
+MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped.");
+
+static bool hwcalib_xy;
+module_param(hwcalib_xy, bool, 0644);
+MODULE_PARM_DESC(hwcalib_xy, "If set hw-calibrated X/Y are used if available");
+
+/* device specifc data/functions */
+struct usbtouch_usb;
+struct usbtouch_device_info {
+ int min_xc, max_xc;
+ int min_yc, max_yc;
+ int min_press, max_press;
+ int rept_size;
+
+ /*
+ * Always service the USB devices irq not just when the input device is
+ * open. This is useful when devices have a watchdog which prevents us
+ * from periodically polling the device. Leave this unset unless your
+ * touchscreen device requires it, as it does consume more of the USB
+ * bandwidth.
+ */
+ bool irq_always;
+
+ void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len);
+
+ /*
+ * used to get the packet len. possible return values:
+ * > 0: packet len
+ * = 0: skip one byte
+ * < 0: -return value more bytes needed
+ */
+ int (*get_pkt_len) (unsigned char *pkt, int len);
+
+ int (*read_data) (struct usbtouch_usb *usbtouch, unsigned char *pkt);
+ int (*alloc) (struct usbtouch_usb *usbtouch);
+ int (*init) (struct usbtouch_usb *usbtouch);
+ void (*exit) (struct usbtouch_usb *usbtouch);
+};
+
+/* a usbtouch device */
+struct usbtouch_usb {
+ unsigned char *data;
+ dma_addr_t data_dma;
+ int data_size;
+ unsigned char *buffer;
+ int buf_len;
+ struct urb *irq;
+ struct usb_interface *interface;
+ struct input_dev *input;
+ struct usbtouch_device_info *type;
+ char name[128];
+ char phys[64];
+ void *priv;
+
+ int x, y;
+ int touch, press;
+};
+
+
+/* device types */
+enum {
+ DEVTYPE_IGNORE = -1,
+ DEVTYPE_EGALAX,
+ DEVTYPE_PANJIT,
+ DEVTYPE_3M,
+ DEVTYPE_ITM,
+ DEVTYPE_ETURBO,
+ DEVTYPE_GUNZE,
+ DEVTYPE_DMC_TSC10,
+ DEVTYPE_IRTOUCH,
+ DEVTYPE_IRTOUCH_HIRES,
+ DEVTYPE_IDEALTEK,
+ DEVTYPE_GENERAL_TOUCH,
+ DEVTYPE_GOTOP,
+ DEVTYPE_JASTEC,
+ DEVTYPE_E2I,
+ DEVTYPE_ZYTRONIC,
+ DEVTYPE_TC45USB,
+ DEVTYPE_NEXIO,
+ DEVTYPE_ELO,
+ DEVTYPE_ETOUCH,
+};
+
+#define USB_DEVICE_HID_CLASS(vend, prod) \
+ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS \
+ | USB_DEVICE_ID_MATCH_DEVICE, \
+ .idVendor = (vend), \
+ .idProduct = (prod), \
+ .bInterfaceClass = USB_INTERFACE_CLASS_HID
+
+static const struct usb_device_id usbtouch_devices[] = {
+#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
+ /* ignore the HID capable devices, handled by usbhid */
+ {USB_DEVICE_HID_CLASS(0x0eef, 0x0001), .driver_info = DEVTYPE_IGNORE},
+ {USB_DEVICE_HID_CLASS(0x0eef, 0x0002), .driver_info = DEVTYPE_IGNORE},
+
+ /* normal device IDs */
+ {USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x3823, 0x0002), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x0123, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x0eef, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x0eef, 0x0002), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x1234, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x1234, 0x0002), .driver_info = DEVTYPE_EGALAX},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_PANJIT
+ {USB_DEVICE(0x134c, 0x0001), .driver_info = DEVTYPE_PANJIT},
+ {USB_DEVICE(0x134c, 0x0002), .driver_info = DEVTYPE_PANJIT},
+ {USB_DEVICE(0x134c, 0x0003), .driver_info = DEVTYPE_PANJIT},
+ {USB_DEVICE(0x134c, 0x0004), .driver_info = DEVTYPE_PANJIT},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_3M
+ {USB_DEVICE(0x0596, 0x0001), .driver_info = DEVTYPE_3M},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ITM
+ {USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM},
+ {USB_DEVICE(0x16e3, 0xf9e9), .driver_info = DEVTYPE_ITM},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO
+ {USB_DEVICE(0x1234, 0x5678), .driver_info = DEVTYPE_ETURBO},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GUNZE
+ {USB_DEVICE(0x0637, 0x0001), .driver_info = DEVTYPE_GUNZE},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_DMC_TSC10
+ {USB_DEVICE(0x0afa, 0x03e8), .driver_info = DEVTYPE_DMC_TSC10},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
+ {USB_DEVICE(0x595a, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
+ {USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
+ {USB_DEVICE(0x6615, 0x0012), .driver_info = DEVTYPE_IRTOUCH_HIRES},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
+ {USB_DEVICE(0x1391, 0x1000), .driver_info = DEVTYPE_IDEALTEK},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
+ {USB_DEVICE(0x0dfc, 0x0001), .driver_info = DEVTYPE_GENERAL_TOUCH},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GOTOP
+ {USB_DEVICE(0x08f2, 0x007f), .driver_info = DEVTYPE_GOTOP},
+ {USB_DEVICE(0x08f2, 0x00ce), .driver_info = DEVTYPE_GOTOP},
+ {USB_DEVICE(0x08f2, 0x00f4), .driver_info = DEVTYPE_GOTOP},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC
+ {USB_DEVICE(0x0f92, 0x0001), .driver_info = DEVTYPE_JASTEC},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_E2I
+ {USB_DEVICE(0x1ac7, 0x0001), .driver_info = DEVTYPE_E2I},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+ {USB_DEVICE(0x14c8, 0x0003), .driver_info = DEVTYPE_ZYTRONIC},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB
+ /* TC5UH */
+ {USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC45USB},
+ /* TC4UM */
+ {USB_DEVICE(0x0664, 0x0306), .driver_info = DEVTYPE_TC45USB},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO
+ /* data interface only */
+ {USB_DEVICE_AND_INTERFACE_INFO(0x10f0, 0x2002, 0x0a, 0x00, 0x00),
+ .driver_info = DEVTYPE_NEXIO},
+ {USB_DEVICE_AND_INTERFACE_INFO(0x1870, 0x0001, 0x0a, 0x00, 0x00),
+ .driver_info = DEVTYPE_NEXIO},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ELO
+ {USB_DEVICE(0x04e7, 0x0020), .driver_info = DEVTYPE_ELO},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH
+ {USB_DEVICE(0x7374, 0x0001), .driver_info = DEVTYPE_ETOUCH},
+#endif
+
+ {}
+};
+
+
+/*****************************************************************************
+ * e2i Part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_E2I
+static int e2i_init(struct usbtouch_usb *usbtouch)
+{
+ int ret;
+ struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x01, 0x02, 0x0000, 0x0081,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+
+ dev_dbg(&usbtouch->interface->dev,
+ "%s - usb_control_msg - E2I_RESET - bytes|err: %d\n",
+ __func__, ret);
+ return ret;
+}
+
+static int e2i_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ int tmp = (pkt[0] << 8) | pkt[1];
+ dev->x = (pkt[2] << 8) | pkt[3];
+ dev->y = (pkt[4] << 8) | pkt[5];
+
+ tmp = tmp - 0xA000;
+ dev->touch = (tmp > 0);
+ dev->press = (tmp > 0 ? tmp : 0);
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * eGalax part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
+
+#ifndef MULTI_PACKET
+#define MULTI_PACKET
+#endif
+
+#define EGALAX_PKT_TYPE_MASK 0xFE
+#define EGALAX_PKT_TYPE_REPT 0x80
+#define EGALAX_PKT_TYPE_DIAG 0x0A
+
+static int egalax_init(struct usbtouch_usb *usbtouch)
+{
+ int ret, i;
+ unsigned char *buf;
+ struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+
+ /*
+ * An eGalax diagnostic packet kicks the device into using the right
+ * protocol. We send a "check active" packet. The response will be
+ * read later and ignored.
+ */
+
+ buf = kmalloc(3, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = EGALAX_PKT_TYPE_DIAG;
+ buf[1] = 1; /* length */
+ buf[2] = 'A'; /* command - check active */
+
+ for (i = 0; i < 3; i++) {
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, buf, 3,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret >= 0) {
+ ret = 0;
+ break;
+ }
+ if (ret != -EPIPE)
+ break;
+ }
+
+ kfree(buf);
+
+ return ret;
+}
+
+static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT)
+ return 0;
+
+ dev->x = ((pkt[3] & 0x0F) << 7) | (pkt[4] & 0x7F);
+ dev->y = ((pkt[1] & 0x0F) << 7) | (pkt[2] & 0x7F);
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+
+static int egalax_get_pkt_len(unsigned char *buf, int len)
+{
+ switch (buf[0] & EGALAX_PKT_TYPE_MASK) {
+ case EGALAX_PKT_TYPE_REPT:
+ return 5;
+
+ case EGALAX_PKT_TYPE_DIAG:
+ if (len < 2)
+ return -1;
+
+ return buf[1] + 2;
+ }
+
+ return 0;
+}
+#endif
+
+/*****************************************************************************
+ * EasyTouch part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH
+
+#ifndef MULTI_PACKET
+#define MULTI_PACKET
+#endif
+
+#define ETOUCH_PKT_TYPE_MASK 0xFE
+#define ETOUCH_PKT_TYPE_REPT 0x80
+#define ETOUCH_PKT_TYPE_REPT2 0xB0
+#define ETOUCH_PKT_TYPE_DIAG 0x0A
+
+static int etouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ if ((pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT &&
+ (pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT2)
+ return 0;
+
+ dev->x = ((pkt[1] & 0x1F) << 7) | (pkt[2] & 0x7F);
+ dev->y = ((pkt[3] & 0x1F) << 7) | (pkt[4] & 0x7F);
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+
+static int etouch_get_pkt_len(unsigned char *buf, int len)
+{
+ switch (buf[0] & ETOUCH_PKT_TYPE_MASK) {
+ case ETOUCH_PKT_TYPE_REPT:
+ case ETOUCH_PKT_TYPE_REPT2:
+ return 5;
+
+ case ETOUCH_PKT_TYPE_DIAG:
+ if (len < 2)
+ return -1;
+
+ return buf[1] + 2;
+ }
+
+ return 0;
+}
+#endif
+
+/*****************************************************************************
+ * PanJit Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_PANJIT
+static int panjit_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1];
+ dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3];
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * 3M/Microtouch Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_3M
+
+#define MTOUCHUSB_ASYNC_REPORT 1
+#define MTOUCHUSB_RESET 7
+#define MTOUCHUSB_REQ_CTRLLR_ID 10
+
+static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ if (hwcalib_xy) {
+ dev->x = (pkt[4] << 8) | pkt[3];
+ dev->y = 0xffff - ((pkt[6] << 8) | pkt[5]);
+ } else {
+ dev->x = (pkt[8] << 8) | pkt[7];
+ dev->y = (pkt[10] << 8) | pkt[9];
+ }
+ dev->touch = (pkt[2] & 0x40) ? 1 : 0;
+
+ return 1;
+}
+
+static int mtouch_init(struct usbtouch_usb *usbtouch)
+{
+ int ret, i;
+ struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ MTOUCHUSB_RESET,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ dev_dbg(&usbtouch->interface->dev,
+ "%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d\n",
+ __func__, ret);
+ if (ret < 0)
+ return ret;
+ msleep(150);
+
+ for (i = 0; i < 3; i++) {
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ MTOUCHUSB_ASYNC_REPORT,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ dev_dbg(&usbtouch->interface->dev,
+ "%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d\n",
+ __func__, ret);
+ if (ret >= 0)
+ break;
+ if (ret != -EPIPE)
+ return ret;
+ }
+
+ /* Default min/max xy are the raw values, override if using hw-calib */
+ if (hwcalib_xy) {
+ input_set_abs_params(usbtouch->input, ABS_X, 0, 0xffff, 0, 0);
+ input_set_abs_params(usbtouch->input, ABS_Y, 0, 0xffff, 0, 0);
+ }
+
+ return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * ITM Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ITM
+static int itm_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ int touch;
+ /*
+ * ITM devices report invalid x/y data if not touched.
+ * if the screen was touched before but is not touched any more
+ * report touch as 0 with the last valid x/y data once. then stop
+ * reporting data until touched again.
+ */
+ dev->press = ((pkt[2] & 0x01) << 7) | (pkt[5] & 0x7F);
+
+ touch = ~pkt[7] & 0x20;
+ if (!touch) {
+ if (dev->touch) {
+ dev->touch = 0;
+ return 1;
+ }
+
+ return 0;
+ }
+
+ dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[3] & 0x7F);
+ dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[4] & 0x7F);
+ dev->touch = touch;
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * eTurboTouch part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO
+#ifndef MULTI_PACKET
+#define MULTI_PACKET
+#endif
+static int eturbo_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ unsigned int shift;
+
+ /* packets should start with sync */
+ if (!(pkt[0] & 0x80))
+ return 0;
+
+ shift = (6 - (pkt[0] & 0x03));
+ dev->x = ((pkt[3] << 7) | pkt[4]) >> shift;
+ dev->y = ((pkt[1] << 7) | pkt[2]) >> shift;
+ dev->touch = (pkt[0] & 0x10) ? 1 : 0;
+
+ return 1;
+}
+
+static int eturbo_get_pkt_len(unsigned char *buf, int len)
+{
+ if (buf[0] & 0x80)
+ return 5;
+ if (buf[0] == 0x01)
+ return 3;
+ return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * Gunze part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_GUNZE
+static int gunze_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ if (!(pkt[0] & 0x80) || ((pkt[1] | pkt[2] | pkt[3]) & 0x80))
+ return 0;
+
+ dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[2] & 0x7F);
+ dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[3] & 0x7F);
+ dev->touch = pkt[0] & 0x20;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * DMC TSC-10/25 Part
+ *
+ * Documentation about the controller and it's protocol can be found at
+ * http://www.dmccoltd.com/files/controler/tsc10usb_pi_e.pdf
+ * http://www.dmccoltd.com/files/controler/tsc25_usb_e.pdf
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_DMC_TSC10
+
+/* supported data rates. currently using 130 */
+#define TSC10_RATE_POINT 0x50
+#define TSC10_RATE_30 0x40
+#define TSC10_RATE_50 0x41
+#define TSC10_RATE_80 0x42
+#define TSC10_RATE_100 0x43
+#define TSC10_RATE_130 0x44
+#define TSC10_RATE_150 0x45
+
+/* commands */
+#define TSC10_CMD_RESET 0x55
+#define TSC10_CMD_RATE 0x05
+#define TSC10_CMD_DATA1 0x01
+
+static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
+{
+ struct usb_device *dev = interface_to_usbdev(usbtouch->interface);
+ int ret = -ENOMEM;
+ unsigned char *buf;
+
+ buf = kmalloc(2, GFP_NOIO);
+ if (!buf)
+ goto err_nobuf;
+ /* reset */
+ buf[0] = buf[1] = 0xFF;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
+ TSC10_CMD_RESET,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
+ if (ret < 0)
+ goto err_out;
+ if (buf[0] != 0x06) {
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ /* set coordinate output rate */
+ buf[0] = buf[1] = 0xFF;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
+ TSC10_CMD_RATE,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
+ if (ret < 0)
+ goto err_out;
+ if ((buf[0] != 0x06) && (buf[0] != 0x15 || buf[1] != 0x01)) {
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ /* start sending data */
+ ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
+ TSC10_CMD_DATA1,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+err_out:
+ kfree(buf);
+err_nobuf:
+ return ret;
+}
+
+
+static int dmc_tsc10_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = ((pkt[2] & 0x03) << 8) | pkt[1];
+ dev->y = ((pkt[4] & 0x03) << 8) | pkt[3];
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * IRTOUCH Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
+static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = (pkt[3] << 8) | pkt[2];
+ dev->y = (pkt[5] << 8) | pkt[4];
+ dev->touch = (pkt[1] & 0x03) ? 1 : 0;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * ET&T TC5UH/TC4UM part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB
+static int tc45usb_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1];
+ dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3];
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * IdealTEK URTC1000 Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
+#ifndef MULTI_PACKET
+#define MULTI_PACKET
+#endif
+static int idealtek_get_pkt_len(unsigned char *buf, int len)
+{
+ if (buf[0] & 0x80)
+ return 5;
+ if (buf[0] == 0x01)
+ return len;
+ return 0;
+}
+
+static int idealtek_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ switch (pkt[0] & 0x98) {
+ case 0x88:
+ /* touch data in IdealTEK mode */
+ dev->x = (pkt[1] << 5) | (pkt[2] >> 2);
+ dev->y = (pkt[3] << 5) | (pkt[4] >> 2);
+ dev->touch = (pkt[0] & 0x40) ? 1 : 0;
+ return 1;
+
+ case 0x98:
+ /* touch data in MT emulation mode */
+ dev->x = (pkt[2] << 5) | (pkt[1] >> 2);
+ dev->y = (pkt[4] << 5) | (pkt[3] >> 2);
+ dev->touch = (pkt[0] & 0x40) ? 1 : 0;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+#endif
+
+/*****************************************************************************
+ * General Touch Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
+static int general_touch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = (pkt[2] << 8) | pkt[1];
+ dev->y = (pkt[4] << 8) | pkt[3];
+ dev->press = pkt[5] & 0xff;
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * GoTop Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_GOTOP
+static int gotop_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = ((pkt[1] & 0x38) << 4) | pkt[2];
+ dev->y = ((pkt[1] & 0x07) << 7) | pkt[3];
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * JASTEC Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC
+static int jastec_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = ((pkt[0] & 0x3f) << 6) | (pkt[2] & 0x3f);
+ dev->y = ((pkt[1] & 0x3f) << 6) | (pkt[3] & 0x3f);
+ dev->touch = (pkt[0] & 0x40) >> 6;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * Zytronic Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+static int zytronic_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ struct usb_interface *intf = dev->interface;
+
+ switch (pkt[0]) {
+ case 0x3A: /* command response */
+ dev_dbg(&intf->dev, "%s: Command response %d\n", __func__, pkt[1]);
+ break;
+
+ case 0xC0: /* down */
+ dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7);
+ dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7);
+ dev->touch = 1;
+ dev_dbg(&intf->dev, "%s: down %d,%d\n", __func__, dev->x, dev->y);
+ return 1;
+
+ case 0x80: /* up */
+ dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7);
+ dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7);
+ dev->touch = 0;
+ dev_dbg(&intf->dev, "%s: up %d,%d\n", __func__, dev->x, dev->y);
+ return 1;
+
+ default:
+ dev_dbg(&intf->dev, "%s: Unknown return %d\n", __func__, pkt[0]);
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+/*****************************************************************************
+ * NEXIO Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO
+
+#define NEXIO_TIMEOUT 5000
+#define NEXIO_BUFSIZE 1024
+#define NEXIO_THRESHOLD 50
+
+struct nexio_priv {
+ struct urb *ack;
+ unsigned char *ack_buf;
+};
+
+struct nexio_touch_packet {
+ u8 flags; /* 0xe1 = touch, 0xe1 = release */
+ __be16 data_len; /* total bytes of touch data */
+ __be16 x_len; /* bytes for X axis */
+ __be16 y_len; /* bytes for Y axis */
+ u8 data[];
+} __attribute__ ((packed));
+
+static unsigned char nexio_ack_pkt[2] = { 0xaa, 0x02 };
+static unsigned char nexio_init_pkt[4] = { 0x82, 0x04, 0x0a, 0x0f };
+
+static void nexio_ack_complete(struct urb *urb)
+{
+}
+
+static int nexio_alloc(struct usbtouch_usb *usbtouch)
+{
+ struct nexio_priv *priv;
+ int ret = -ENOMEM;
+
+ usbtouch->priv = kmalloc(sizeof(struct nexio_priv), GFP_KERNEL);
+ if (!usbtouch->priv)
+ goto out_buf;
+
+ priv = usbtouch->priv;
+
+ priv->ack_buf = kmemdup(nexio_ack_pkt, sizeof(nexio_ack_pkt),
+ GFP_KERNEL);
+ if (!priv->ack_buf)
+ goto err_priv;
+
+ priv->ack = usb_alloc_urb(0, GFP_KERNEL);
+ if (!priv->ack) {
+ dev_dbg(&usbtouch->interface->dev,
+ "%s - usb_alloc_urb failed: usbtouch->ack\n", __func__);
+ goto err_ack_buf;
+ }
+
+ return 0;
+
+err_ack_buf:
+ kfree(priv->ack_buf);
+err_priv:
+ kfree(priv);
+out_buf:
+ return ret;
+}
+
+static int nexio_init(struct usbtouch_usb *usbtouch)
+{
+ struct usb_device *dev = interface_to_usbdev(usbtouch->interface);
+ struct usb_host_interface *interface = usbtouch->interface->cur_altsetting;
+ struct nexio_priv *priv = usbtouch->priv;
+ int ret = -ENOMEM;
+ int actual_len, i;
+ unsigned char *buf;
+ char *firmware_ver = NULL, *device_name = NULL;
+ int input_ep = 0, output_ep = 0;
+
+ /* find first input and output endpoint */
+ for (i = 0; i < interface->desc.bNumEndpoints; i++) {
+ if (!input_ep &&
+ usb_endpoint_dir_in(&interface->endpoint[i].desc))
+ input_ep = interface->endpoint[i].desc.bEndpointAddress;
+ if (!output_ep &&
+ usb_endpoint_dir_out(&interface->endpoint[i].desc))
+ output_ep = interface->endpoint[i].desc.bEndpointAddress;
+ }
+ if (!input_ep || !output_ep)
+ return -ENXIO;
+
+ buf = kmalloc(NEXIO_BUFSIZE, GFP_NOIO);
+ if (!buf)
+ goto out_buf;
+
+ /* two empty reads */
+ for (i = 0; i < 2; i++) {
+ ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, input_ep),
+ buf, NEXIO_BUFSIZE, &actual_len,
+ NEXIO_TIMEOUT);
+ if (ret < 0)
+ goto out_buf;
+ }
+
+ /* send init command */
+ memcpy(buf, nexio_init_pkt, sizeof(nexio_init_pkt));
+ ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, output_ep),
+ buf, sizeof(nexio_init_pkt), &actual_len,
+ NEXIO_TIMEOUT);
+ if (ret < 0)
+ goto out_buf;
+
+ /* read replies */
+ for (i = 0; i < 3; i++) {
+ memset(buf, 0, NEXIO_BUFSIZE);
+ ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, input_ep),
+ buf, NEXIO_BUFSIZE, &actual_len,
+ NEXIO_TIMEOUT);
+ if (ret < 0 || actual_len < 1 || buf[1] != actual_len)
+ continue;
+ switch (buf[0]) {
+ case 0x83: /* firmware version */
+ if (!firmware_ver)
+ firmware_ver = kstrdup(&buf[2], GFP_NOIO);
+ break;
+ case 0x84: /* device name */
+ if (!device_name)
+ device_name = kstrdup(&buf[2], GFP_NOIO);
+ break;
+ }
+ }
+
+ printk(KERN_INFO "Nexio device: %s, firmware version: %s\n",
+ device_name, firmware_ver);
+
+ kfree(firmware_ver);
+ kfree(device_name);
+
+ usb_fill_bulk_urb(priv->ack, dev, usb_sndbulkpipe(dev, output_ep),
+ priv->ack_buf, sizeof(nexio_ack_pkt),
+ nexio_ack_complete, usbtouch);
+ ret = 0;
+
+out_buf:
+ kfree(buf);
+ return ret;
+}
+
+static void nexio_exit(struct usbtouch_usb *usbtouch)
+{
+ struct nexio_priv *priv = usbtouch->priv;
+
+ usb_kill_urb(priv->ack);
+ usb_free_urb(priv->ack);
+ kfree(priv->ack_buf);
+ kfree(priv);
+}
+
+static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)
+{
+ struct nexio_touch_packet *packet = (void *) pkt;
+ struct nexio_priv *priv = usbtouch->priv;
+ unsigned int data_len = be16_to_cpu(packet->data_len);
+ unsigned int x_len = be16_to_cpu(packet->x_len);
+ unsigned int y_len = be16_to_cpu(packet->y_len);
+ int x, y, begin_x, begin_y, end_x, end_y, w, h, ret;
+
+ /* got touch data? */
+ if ((pkt[0] & 0xe0) != 0xe0)
+ return 0;
+
+ if (data_len > 0xff)
+ data_len -= 0x100;
+ if (x_len > 0xff)
+ x_len -= 0x80;
+
+ /* send ACK */
+ ret = usb_submit_urb(priv->ack, GFP_ATOMIC);
+
+ if (!usbtouch->type->max_xc) {
+ usbtouch->type->max_xc = 2 * x_len;
+ input_set_abs_params(usbtouch->input, ABS_X,
+ 0, usbtouch->type->max_xc, 0, 0);
+ usbtouch->type->max_yc = 2 * y_len;
+ input_set_abs_params(usbtouch->input, ABS_Y,
+ 0, usbtouch->type->max_yc, 0, 0);
+ }
+ /*
+ * The device reports state of IR sensors on X and Y axes.
+ * Each byte represents "darkness" percentage (0-100) of one element.
+ * 17" touchscreen reports only 64 x 52 bytes so the resolution is low.
+ * This also means that there's a limited multi-touch capability but
+ * it's disabled (and untested) here as there's no X driver for that.
+ */
+ begin_x = end_x = begin_y = end_y = -1;
+ for (x = 0; x < x_len; x++) {
+ if (begin_x == -1 && packet->data[x] > NEXIO_THRESHOLD) {
+ begin_x = x;
+ continue;
+ }
+ if (end_x == -1 && begin_x != -1 && packet->data[x] < NEXIO_THRESHOLD) {
+ end_x = x - 1;
+ for (y = x_len; y < data_len; y++) {
+ if (begin_y == -1 && packet->data[y] > NEXIO_THRESHOLD) {
+ begin_y = y - x_len;
+ continue;
+ }
+ if (end_y == -1 &&
+ begin_y != -1 && packet->data[y] < NEXIO_THRESHOLD) {
+ end_y = y - 1 - x_len;
+ w = end_x - begin_x;
+ h = end_y - begin_y;
+#if 0
+ /* multi-touch */
+ input_report_abs(usbtouch->input,
+ ABS_MT_TOUCH_MAJOR, max(w,h));
+ input_report_abs(usbtouch->input,
+ ABS_MT_TOUCH_MINOR, min(x,h));
+ input_report_abs(usbtouch->input,
+ ABS_MT_POSITION_X, 2*begin_x+w);
+ input_report_abs(usbtouch->input,
+ ABS_MT_POSITION_Y, 2*begin_y+h);
+ input_report_abs(usbtouch->input,
+ ABS_MT_ORIENTATION, w > h);
+ input_mt_sync(usbtouch->input);
+#endif
+ /* single touch */
+ usbtouch->x = 2 * begin_x + w;
+ usbtouch->y = 2 * begin_y + h;
+ usbtouch->touch = packet->flags & 0x01;
+ begin_y = end_y = -1;
+ return 1;
+ }
+ }
+ begin_x = end_x = -1;
+ }
+
+ }
+ return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * ELO part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ELO
+
+static int elo_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = (pkt[3] << 8) | pkt[2];
+ dev->y = (pkt[5] << 8) | pkt[4];
+ dev->touch = pkt[6] > 0;
+ dev->press = pkt[6];
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * the different device descriptors
+ */
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+ unsigned char *pkt, int len);
+#endif
+
+static struct usbtouch_device_info usbtouch_dev_info[] = {
+#ifdef CONFIG_TOUCHSCREEN_USB_ELO
+ [DEVTYPE_ELO] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .max_press = 0xff,
+ .rept_size = 8,
+ .read_data = elo_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
+ [DEVTYPE_EGALAX] = {
+ .min_xc = 0x0,
+ .max_xc = 0x07ff,
+ .min_yc = 0x0,
+ .max_yc = 0x07ff,
+ .rept_size = 16,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = egalax_get_pkt_len,
+ .read_data = egalax_read_data,
+ .init = egalax_init,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_PANJIT
+ [DEVTYPE_PANJIT] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 8,
+ .read_data = panjit_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_3M
+ [DEVTYPE_3M] = {
+ .min_xc = 0x0,
+ .max_xc = 0x4000,
+ .min_yc = 0x0,
+ .max_yc = 0x4000,
+ .rept_size = 11,
+ .read_data = mtouch_read_data,
+ .init = mtouch_init,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ITM
+ [DEVTYPE_ITM] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .max_press = 0xff,
+ .rept_size = 8,
+ .read_data = itm_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO
+ [DEVTYPE_ETURBO] = {
+ .min_xc = 0x0,
+ .max_xc = 0x07ff,
+ .min_yc = 0x0,
+ .max_yc = 0x07ff,
+ .rept_size = 8,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = eturbo_get_pkt_len,
+ .read_data = eturbo_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GUNZE
+ [DEVTYPE_GUNZE] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 4,
+ .read_data = gunze_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_DMC_TSC10
+ [DEVTYPE_DMC_TSC10] = {
+ .min_xc = 0x0,
+ .max_xc = 0x03ff,
+ .min_yc = 0x0,
+ .max_yc = 0x03ff,
+ .rept_size = 5,
+ .init = dmc_tsc10_init,
+ .read_data = dmc_tsc10_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
+ [DEVTYPE_IRTOUCH] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 8,
+ .read_data = irtouch_read_data,
+ },
+
+ [DEVTYPE_IRTOUCH_HIRES] = {
+ .min_xc = 0x0,
+ .max_xc = 0x7fff,
+ .min_yc = 0x0,
+ .max_yc = 0x7fff,
+ .rept_size = 8,
+ .read_data = irtouch_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
+ [DEVTYPE_IDEALTEK] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 8,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = idealtek_get_pkt_len,
+ .read_data = idealtek_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
+ [DEVTYPE_GENERAL_TOUCH] = {
+ .min_xc = 0x0,
+ .max_xc = 0x7fff,
+ .min_yc = 0x0,
+ .max_yc = 0x7fff,
+ .rept_size = 7,
+ .read_data = general_touch_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GOTOP
+ [DEVTYPE_GOTOP] = {
+ .min_xc = 0x0,
+ .max_xc = 0x03ff,
+ .min_yc = 0x0,
+ .max_yc = 0x03ff,
+ .rept_size = 4,
+ .read_data = gotop_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC
+ [DEVTYPE_JASTEC] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 4,
+ .read_data = jastec_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_E2I
+ [DEVTYPE_E2I] = {
+ .min_xc = 0x0,
+ .max_xc = 0x7fff,
+ .min_yc = 0x0,
+ .max_yc = 0x7fff,
+ .rept_size = 6,
+ .init = e2i_init,
+ .read_data = e2i_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+ [DEVTYPE_ZYTRONIC] = {
+ .min_xc = 0x0,
+ .max_xc = 0x03ff,
+ .min_yc = 0x0,
+ .max_yc = 0x03ff,
+ .rept_size = 5,
+ .read_data = zytronic_read_data,
+ .irq_always = true,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB
+ [DEVTYPE_TC45USB] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 5,
+ .read_data = tc45usb_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO
+ [DEVTYPE_NEXIO] = {
+ .rept_size = 1024,
+ .irq_always = true,
+ .read_data = nexio_read_data,
+ .alloc = nexio_alloc,
+ .init = nexio_init,
+ .exit = nexio_exit,
+ },
+#endif
+#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH
+ [DEVTYPE_ETOUCH] = {
+ .min_xc = 0x0,
+ .max_xc = 0x07ff,
+ .min_yc = 0x0,
+ .max_yc = 0x07ff,
+ .rept_size = 16,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = etouch_get_pkt_len,
+ .read_data = etouch_read_data,
+ },
+#endif
+};
+
+
+/*****************************************************************************
+ * Generic Part
+ */
+static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
+ unsigned char *pkt, int len)
+{
+ struct usbtouch_device_info *type = usbtouch->type;
+
+ if (!type->read_data(usbtouch, pkt))
+ return;
+
+ input_report_key(usbtouch->input, BTN_TOUCH, usbtouch->touch);
+
+ if (swap_xy) {
+ input_report_abs(usbtouch->input, ABS_X, usbtouch->y);
+ input_report_abs(usbtouch->input, ABS_Y, usbtouch->x);
+ } else {
+ input_report_abs(usbtouch->input, ABS_X, usbtouch->x);
+ input_report_abs(usbtouch->input, ABS_Y, usbtouch->y);
+ }
+ if (type->max_press)
+ input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press);
+ input_sync(usbtouch->input);
+}
+
+
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+ unsigned char *pkt, int len)
+{
+ unsigned char *buffer;
+ int pkt_len, pos, buf_len, tmp;
+
+ /* process buffer */
+ if (unlikely(usbtouch->buf_len)) {
+ /* try to get size */
+ pkt_len = usbtouch->type->get_pkt_len(
+ usbtouch->buffer, usbtouch->buf_len);
+
+ /* drop? */
+ if (unlikely(!pkt_len))
+ goto out_flush_buf;
+
+ /* need to append -pkt_len bytes before able to get size */
+ if (unlikely(pkt_len < 0)) {
+ int append = -pkt_len;
+ if (unlikely(append > len))
+ append = len;
+ if (usbtouch->buf_len + append >= usbtouch->type->rept_size)
+ goto out_flush_buf;
+ memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, append);
+ usbtouch->buf_len += append;
+
+ pkt_len = usbtouch->type->get_pkt_len(
+ usbtouch->buffer, usbtouch->buf_len);
+ if (pkt_len < 0)
+ return;
+ }
+
+ /* append */
+ tmp = pkt_len - usbtouch->buf_len;
+ if (usbtouch->buf_len + tmp >= usbtouch->type->rept_size)
+ goto out_flush_buf;
+ memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp);
+ usbtouch_process_pkt(usbtouch, usbtouch->buffer, pkt_len);
+
+ buffer = pkt + tmp;
+ buf_len = len - tmp;
+ } else {
+ buffer = pkt;
+ buf_len = len;
+ }
+
+ /* loop over the received packet, process */
+ pos = 0;
+ while (pos < buf_len) {
+ /* get packet len */
+ pkt_len = usbtouch->type->get_pkt_len(buffer + pos,
+ buf_len - pos);
+
+ /* unknown packet: skip one byte */
+ if (unlikely(!pkt_len)) {
+ pos++;
+ continue;
+ }
+
+ /* full packet: process */
+ if (likely((pkt_len > 0) && (pkt_len <= buf_len - pos))) {
+ usbtouch_process_pkt(usbtouch, buffer + pos, pkt_len);
+ } else {
+ /* incomplete packet: save in buffer */
+ memcpy(usbtouch->buffer, buffer + pos, buf_len - pos);
+ usbtouch->buf_len = buf_len - pos;
+ return;
+ }
+ pos += pkt_len;
+ }
+
+out_flush_buf:
+ usbtouch->buf_len = 0;
+ return;
+}
+#endif
+
+
+static void usbtouch_irq(struct urb *urb)
+{
+ struct usbtouch_usb *usbtouch = urb->context;
+ struct device *dev = &usbtouch->interface->dev;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ETIME:
+ /* this urb is timing out */
+ dev_dbg(dev,
+ "%s - urb timed out - was the device unplugged?\n",
+ __func__);
+ return;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -EPIPE:
+ /* this urb is terminated, clean up */
+ dev_dbg(dev, "%s - urb shutting down with status: %d\n",
+ __func__, urb->status);
+ return;
+ default:
+ dev_dbg(dev, "%s - nonzero urb status received: %d\n",
+ __func__, urb->status);
+ goto exit;
+ }
+
+ usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);
+
+exit:
+ usb_mark_last_busy(interface_to_usbdev(usbtouch->interface));
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(dev, "%s - usb_submit_urb failed with result: %d\n",
+ __func__, retval);
+}
+
+static int usbtouch_open(struct input_dev *input)
+{
+ struct usbtouch_usb *usbtouch = input_get_drvdata(input);
+ int r;
+
+ usbtouch->irq->dev = interface_to_usbdev(usbtouch->interface);
+
+ r = usb_autopm_get_interface(usbtouch->interface) ? -EIO : 0;
+ if (r < 0)
+ goto out;
+
+ if (!usbtouch->type->irq_always) {
+ if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) {
+ r = -EIO;
+ goto out_put;
+ }
+ }
+
+ usbtouch->interface->needs_remote_wakeup = 1;
+out_put:
+ usb_autopm_put_interface(usbtouch->interface);
+out:
+ return r;
+}
+
+static void usbtouch_close(struct input_dev *input)
+{
+ struct usbtouch_usb *usbtouch = input_get_drvdata(input);
+ int r;
+
+ if (!usbtouch->type->irq_always)
+ usb_kill_urb(usbtouch->irq);
+ r = usb_autopm_get_interface(usbtouch->interface);
+ usbtouch->interface->needs_remote_wakeup = 0;
+ if (!r)
+ usb_autopm_put_interface(usbtouch->interface);
+}
+
+static int usbtouch_suspend
+(struct usb_interface *intf, pm_message_t message)
+{
+ struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
+
+ usb_kill_urb(usbtouch->irq);
+
+ return 0;
+}
+
+static int usbtouch_resume(struct usb_interface *intf)
+{
+ struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
+ struct input_dev *input = usbtouch->input;
+ int result = 0;
+
+ mutex_lock(&input->mutex);
+ if (input->users || usbtouch->type->irq_always)
+ result = usb_submit_urb(usbtouch->irq, GFP_NOIO);
+ mutex_unlock(&input->mutex);
+
+ return result;
+}
+
+static int usbtouch_reset_resume(struct usb_interface *intf)
+{
+ struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
+ struct input_dev *input = usbtouch->input;
+ int err = 0;
+
+ /* reinit the device */
+ if (usbtouch->type->init) {
+ err = usbtouch->type->init(usbtouch);
+ if (err) {
+ dev_dbg(&intf->dev,
+ "%s - type->init() failed, err: %d\n",
+ __func__, err);
+ return err;
+ }
+ }
+
+ /* restart IO if needed */
+ mutex_lock(&input->mutex);
+ if (input->users)
+ err = usb_submit_urb(usbtouch->irq, GFP_NOIO);
+ mutex_unlock(&input->mutex);
+
+ return err;
+}
+
+static void usbtouch_free_buffers(struct usb_device *udev,
+ struct usbtouch_usb *usbtouch)
+{
+ usb_free_coherent(udev, usbtouch->data_size,
+ usbtouch->data, usbtouch->data_dma);
+ kfree(usbtouch->buffer);
+}
+
+static struct usb_endpoint_descriptor *
+usbtouch_get_input_endpoint(struct usb_host_interface *interface)
+{
+ int i;
+
+ for (i = 0; i < interface->desc.bNumEndpoints; i++)
+ if (usb_endpoint_dir_in(&interface->endpoint[i].desc))
+ return &interface->endpoint[i].desc;
+
+ return NULL;
+}
+
+static int usbtouch_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usbtouch_usb *usbtouch;
+ struct input_dev *input_dev;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usbtouch_device_info *type;
+ int err = -ENOMEM;
+
+ /* some devices are ignored */
+ if (id->driver_info == DEVTYPE_IGNORE)
+ return -ENODEV;
+
+ endpoint = usbtouch_get_input_endpoint(intf->cur_altsetting);
+ if (!endpoint)
+ return -ENXIO;
+
+ usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!usbtouch || !input_dev)
+ goto out_free;
+
+ type = &usbtouch_dev_info[id->driver_info];
+ usbtouch->type = type;
+ if (!type->process_pkt)
+ type->process_pkt = usbtouch_process_pkt;
+
+ usbtouch->data_size = type->rept_size;
+ if (type->get_pkt_len) {
+ /*
+ * When dealing with variable-length packets we should
+ * not request more than wMaxPacketSize bytes at once
+ * as we do not know if there is more data coming or
+ * we filled exactly wMaxPacketSize bytes and there is
+ * nothing else.
+ */
+ usbtouch->data_size = min(usbtouch->data_size,
+ usb_endpoint_maxp(endpoint));
+ }
+
+ usbtouch->data = usb_alloc_coherent(udev, usbtouch->data_size,
+ GFP_KERNEL, &usbtouch->data_dma);
+ if (!usbtouch->data)
+ goto out_free;
+
+ if (type->get_pkt_len) {
+ usbtouch->buffer = kmalloc(type->rept_size, GFP_KERNEL);
+ if (!usbtouch->buffer)
+ goto out_free_buffers;
+ }
+
+ usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!usbtouch->irq) {
+ dev_dbg(&intf->dev,
+ "%s - usb_alloc_urb failed: usbtouch->irq\n", __func__);
+ goto out_free_buffers;
+ }
+
+ usbtouch->interface = intf;
+ usbtouch->input = input_dev;
+
+ if (udev->manufacturer)
+ strlcpy(usbtouch->name, udev->manufacturer, sizeof(usbtouch->name));
+
+ if (udev->product) {
+ if (udev->manufacturer)
+ strlcat(usbtouch->name, " ", sizeof(usbtouch->name));
+ strlcat(usbtouch->name, udev->product, sizeof(usbtouch->name));
+ }
+
+ if (!strlen(usbtouch->name))
+ snprintf(usbtouch->name, sizeof(usbtouch->name),
+ "USB Touchscreen %04x:%04x",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ usb_make_path(udev, usbtouch->phys, sizeof(usbtouch->phys));
+ strlcat(usbtouch->phys, "/input0", sizeof(usbtouch->phys));
+
+ input_dev->name = usbtouch->name;
+ input_dev->phys = usbtouch->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, usbtouch);
+
+ input_dev->open = usbtouch_open;
+ input_dev->close = usbtouch_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0);
+ if (type->max_press)
+ input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press,
+ type->max_press, 0, 0);
+
+ if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT)
+ usb_fill_int_urb(usbtouch->irq, udev,
+ usb_rcvintpipe(udev, endpoint->bEndpointAddress),
+ usbtouch->data, usbtouch->data_size,
+ usbtouch_irq, usbtouch, endpoint->bInterval);
+ else
+ usb_fill_bulk_urb(usbtouch->irq, udev,
+ usb_rcvbulkpipe(udev, endpoint->bEndpointAddress),
+ usbtouch->data, usbtouch->data_size,
+ usbtouch_irq, usbtouch);
+
+ usbtouch->irq->dev = udev;
+ usbtouch->irq->transfer_dma = usbtouch->data_dma;
+ usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* device specific allocations */
+ if (type->alloc) {
+ err = type->alloc(usbtouch);
+ if (err) {
+ dev_dbg(&intf->dev,
+ "%s - type->alloc() failed, err: %d\n",
+ __func__, err);
+ goto out_free_urb;
+ }
+ }
+
+ /* device specific initialisation*/
+ if (type->init) {
+ err = type->init(usbtouch);
+ if (err) {
+ dev_dbg(&intf->dev,
+ "%s - type->init() failed, err: %d\n",
+ __func__, err);
+ goto out_do_exit;
+ }
+ }
+
+ err = input_register_device(usbtouch->input);
+ if (err) {
+ dev_dbg(&intf->dev,
+ "%s - input_register_device failed, err: %d\n",
+ __func__, err);
+ goto out_do_exit;
+ }
+
+ usb_set_intfdata(intf, usbtouch);
+
+ if (usbtouch->type->irq_always) {
+ /* this can't fail */
+ usb_autopm_get_interface(intf);
+ err = usb_submit_urb(usbtouch->irq, GFP_KERNEL);
+ if (err) {
+ usb_autopm_put_interface(intf);
+ dev_err(&intf->dev,
+ "%s - usb_submit_urb failed with result: %d\n",
+ __func__, err);
+ goto out_unregister_input;
+ }
+ }
+
+ return 0;
+
+out_unregister_input:
+ input_unregister_device(input_dev);
+ input_dev = NULL;
+out_do_exit:
+ if (type->exit)
+ type->exit(usbtouch);
+out_free_urb:
+ usb_free_urb(usbtouch->irq);
+out_free_buffers:
+ usbtouch_free_buffers(udev, usbtouch);
+out_free:
+ input_free_device(input_dev);
+ kfree(usbtouch);
+ return err;
+}
+
+static void usbtouch_disconnect(struct usb_interface *intf)
+{
+ struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
+
+ if (!usbtouch)
+ return;
+
+ dev_dbg(&intf->dev,
+ "%s - usbtouch is initialized, cleaning up\n", __func__);
+
+ usb_set_intfdata(intf, NULL);
+ /* this will stop IO via close */
+ input_unregister_device(usbtouch->input);
+ usb_free_urb(usbtouch->irq);
+ if (usbtouch->type->exit)
+ usbtouch->type->exit(usbtouch);
+ usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch);
+ kfree(usbtouch);
+}
+
+MODULE_DEVICE_TABLE(usb, usbtouch_devices);
+
+static struct usb_driver usbtouch_driver = {
+ .name = "usbtouchscreen",
+ .probe = usbtouch_probe,
+ .disconnect = usbtouch_disconnect,
+ .suspend = usbtouch_suspend,
+ .resume = usbtouch_resume,
+ .reset_resume = usbtouch_reset_resume,
+ .id_table = usbtouch_devices,
+ .supports_autosuspend = 1,
+};
+
+module_usb_driver(usbtouch_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("touchkitusb");
+MODULE_ALIAS("itmtouch");
+MODULE_ALIAS("mtouchusb");
diff --git a/drivers/input/touchscreen/w90p910_ts.c b/drivers/input/touchscreen/w90p910_ts.c
new file mode 100644
index 000000000..da6004e97
--- /dev/null
+++ b/drivers/input/touchscreen/w90p910_ts.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2008 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+/* ADC controller bit defines */
+#define ADC_DELAY 0xf00
+#define ADC_DOWN 0x01
+#define ADC_TSC_Y (0x01 << 8)
+#define ADC_TSC_X (0x00 << 8)
+#define TSC_FOURWIRE (~(0x03 << 1))
+#define ADC_CLK_EN (0x01 << 28) /* ADC clock enable */
+#define ADC_READ_CON (0x01 << 12)
+#define ADC_CONV (0x01 << 13)
+#define ADC_SEMIAUTO (0x01 << 14)
+#define ADC_WAITTRIG (0x03 << 14)
+#define ADC_RST1 (0x01 << 16)
+#define ADC_RST0 (0x00 << 16)
+#define ADC_EN (0x01 << 17)
+#define ADC_INT (0x01 << 18)
+#define WT_INT (0x01 << 20)
+#define ADC_INT_EN (0x01 << 21)
+#define LVD_INT_EN (0x01 << 22)
+#define WT_INT_EN (0x01 << 23)
+#define ADC_DIV (0x04 << 1) /* div = 6 */
+
+enum ts_state {
+ TS_WAIT_NEW_PACKET, /* We are waiting next touch report */
+ TS_WAIT_X_COORD, /* We are waiting for ADC to report X coord */
+ TS_WAIT_Y_COORD, /* We are waiting for ADC to report Y coord */
+ TS_IDLE, /* Input device is closed, don't do anything */
+};
+
+struct w90p910_ts {
+ struct input_dev *input;
+ struct timer_list timer;
+ struct clk *clk;
+ int irq_num;
+ void __iomem *ts_reg;
+ spinlock_t lock;
+ enum ts_state state;
+};
+
+static void w90p910_report_event(struct w90p910_ts *w90p910_ts, bool down)
+{
+ struct input_dev *dev = w90p910_ts->input;
+
+ if (down) {
+ input_report_abs(dev, ABS_X,
+ __raw_readl(w90p910_ts->ts_reg + 0x0c));
+ input_report_abs(dev, ABS_Y,
+ __raw_readl(w90p910_ts->ts_reg + 0x10));
+ }
+
+ input_report_key(dev, BTN_TOUCH, down);
+ input_sync(dev);
+}
+
+static void w90p910_prepare_x_reading(struct w90p910_ts *w90p910_ts)
+{
+ unsigned long ctlreg;
+
+ __raw_writel(ADC_TSC_X, w90p910_ts->ts_reg + 0x04);
+ ctlreg = __raw_readl(w90p910_ts->ts_reg);
+ ctlreg &= ~(ADC_WAITTRIG | WT_INT | WT_INT_EN);
+ ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV;
+ __raw_writel(ctlreg, w90p910_ts->ts_reg);
+
+ w90p910_ts->state = TS_WAIT_X_COORD;
+}
+
+static void w90p910_prepare_y_reading(struct w90p910_ts *w90p910_ts)
+{
+ unsigned long ctlreg;
+
+ __raw_writel(ADC_TSC_Y, w90p910_ts->ts_reg + 0x04);
+ ctlreg = __raw_readl(w90p910_ts->ts_reg);
+ ctlreg &= ~(ADC_WAITTRIG | ADC_INT | WT_INT_EN);
+ ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV;
+ __raw_writel(ctlreg, w90p910_ts->ts_reg);
+
+ w90p910_ts->state = TS_WAIT_Y_COORD;
+}
+
+static void w90p910_prepare_next_packet(struct w90p910_ts *w90p910_ts)
+{
+ unsigned long ctlreg;
+
+ ctlreg = __raw_readl(w90p910_ts->ts_reg);
+ ctlreg &= ~(ADC_INT | ADC_INT_EN | ADC_SEMIAUTO | ADC_CONV);
+ ctlreg |= ADC_WAITTRIG | WT_INT_EN;
+ __raw_writel(ctlreg, w90p910_ts->ts_reg);
+
+ w90p910_ts->state = TS_WAIT_NEW_PACKET;
+}
+
+static irqreturn_t w90p910_ts_interrupt(int irq, void *dev_id)
+{
+ struct w90p910_ts *w90p910_ts = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&w90p910_ts->lock, flags);
+
+ switch (w90p910_ts->state) {
+ case TS_WAIT_NEW_PACKET:
+ /*
+ * The controller only generates interrupts when pen
+ * is down.
+ */
+ del_timer(&w90p910_ts->timer);
+ w90p910_prepare_x_reading(w90p910_ts);
+ break;
+
+
+ case TS_WAIT_X_COORD:
+ w90p910_prepare_y_reading(w90p910_ts);
+ break;
+
+ case TS_WAIT_Y_COORD:
+ w90p910_report_event(w90p910_ts, true);
+ w90p910_prepare_next_packet(w90p910_ts);
+ mod_timer(&w90p910_ts->timer, jiffies + msecs_to_jiffies(100));
+ break;
+
+ case TS_IDLE:
+ break;
+ }
+
+ spin_unlock_irqrestore(&w90p910_ts->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void w90p910_check_pen_up(unsigned long data)
+{
+ struct w90p910_ts *w90p910_ts = (struct w90p910_ts *) data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&w90p910_ts->lock, flags);
+
+ if (w90p910_ts->state == TS_WAIT_NEW_PACKET &&
+ !(__raw_readl(w90p910_ts->ts_reg + 0x04) & ADC_DOWN)) {
+
+ w90p910_report_event(w90p910_ts, false);
+ }
+
+ spin_unlock_irqrestore(&w90p910_ts->lock, flags);
+}
+
+static int w90p910_open(struct input_dev *dev)
+{
+ struct w90p910_ts *w90p910_ts = input_get_drvdata(dev);
+ unsigned long val;
+
+ /* enable the ADC clock */
+ clk_enable(w90p910_ts->clk);
+
+ __raw_writel(ADC_RST1, w90p910_ts->ts_reg);
+ msleep(1);
+ __raw_writel(ADC_RST0, w90p910_ts->ts_reg);
+ msleep(1);
+
+ /* set delay and screen type */
+ val = __raw_readl(w90p910_ts->ts_reg + 0x04);
+ __raw_writel(val & TSC_FOURWIRE, w90p910_ts->ts_reg + 0x04);
+ __raw_writel(ADC_DELAY, w90p910_ts->ts_reg + 0x08);
+
+ w90p910_ts->state = TS_WAIT_NEW_PACKET;
+ wmb();
+
+ /* set trigger mode */
+ val = __raw_readl(w90p910_ts->ts_reg);
+ val |= ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN;
+ __raw_writel(val, w90p910_ts->ts_reg);
+
+ return 0;
+}
+
+static void w90p910_close(struct input_dev *dev)
+{
+ struct w90p910_ts *w90p910_ts = input_get_drvdata(dev);
+ unsigned long val;
+
+ /* disable trigger mode */
+
+ spin_lock_irq(&w90p910_ts->lock);
+
+ w90p910_ts->state = TS_IDLE;
+
+ val = __raw_readl(w90p910_ts->ts_reg);
+ val &= ~(ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN | ADC_INT_EN);
+ __raw_writel(val, w90p910_ts->ts_reg);
+
+ spin_unlock_irq(&w90p910_ts->lock);
+
+ /* Now that interrupts are shut off we can safely delete timer */
+ del_timer_sync(&w90p910_ts->timer);
+
+ /* stop the ADC clock */
+ clk_disable(w90p910_ts->clk);
+}
+
+static int w90x900ts_probe(struct platform_device *pdev)
+{
+ struct w90p910_ts *w90p910_ts;
+ struct input_dev *input_dev;
+ struct resource *res;
+ int err;
+
+ w90p910_ts = kzalloc(sizeof(struct w90p910_ts), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!w90p910_ts || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ w90p910_ts->input = input_dev;
+ w90p910_ts->state = TS_IDLE;
+ spin_lock_init(&w90p910_ts->lock);
+ setup_timer(&w90p910_ts->timer, w90p910_check_pen_up,
+ (unsigned long)w90p910_ts);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ err = -ENXIO;
+ goto fail1;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res),
+ pdev->name)) {
+ err = -EBUSY;
+ goto fail1;
+ }
+
+ w90p910_ts->ts_reg = ioremap(res->start, resource_size(res));
+ if (!w90p910_ts->ts_reg) {
+ err = -ENOMEM;
+ goto fail2;
+ }
+
+ w90p910_ts->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(w90p910_ts->clk)) {
+ err = PTR_ERR(w90p910_ts->clk);
+ goto fail3;
+ }
+
+ input_dev->name = "W90P910 TouchScreen";
+ input_dev->phys = "w90p910ts/event0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0005;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->open = w90p910_open;
+ input_dev->close = w90p910_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, 0x400, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 0x400, 0, 0);
+
+ input_set_drvdata(input_dev, w90p910_ts);
+
+ w90p910_ts->irq_num = platform_get_irq(pdev, 0);
+ if (request_irq(w90p910_ts->irq_num, w90p910_ts_interrupt,
+ 0, "w90p910ts", w90p910_ts)) {
+ err = -EBUSY;
+ goto fail4;
+ }
+
+ err = input_register_device(w90p910_ts->input);
+ if (err)
+ goto fail5;
+
+ platform_set_drvdata(pdev, w90p910_ts);
+
+ return 0;
+
+fail5: free_irq(w90p910_ts->irq_num, w90p910_ts);
+fail4: clk_put(w90p910_ts->clk);
+fail3: iounmap(w90p910_ts->ts_reg);
+fail2: release_mem_region(res->start, resource_size(res));
+fail1: input_free_device(input_dev);
+ kfree(w90p910_ts);
+ return err;
+}
+
+static int w90x900ts_remove(struct platform_device *pdev)
+{
+ struct w90p910_ts *w90p910_ts = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ free_irq(w90p910_ts->irq_num, w90p910_ts);
+ del_timer_sync(&w90p910_ts->timer);
+ iounmap(w90p910_ts->ts_reg);
+
+ clk_put(w90p910_ts->clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+
+ input_unregister_device(w90p910_ts->input);
+ kfree(w90p910_ts);
+
+ return 0;
+}
+
+static struct platform_driver w90x900ts_driver = {
+ .probe = w90x900ts_probe,
+ .remove = w90x900ts_remove,
+ .driver = {
+ .name = "nuc900-ts",
+ },
+};
+module_platform_driver(w90x900ts_driver);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("w90p910 touch screen driver!");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nuc900-ts");
diff --git a/drivers/input/touchscreen/wacom_i2c.c b/drivers/input/touchscreen/wacom_i2c.c
new file mode 100644
index 000000000..32f8ac003
--- /dev/null
+++ b/drivers/input/touchscreen/wacom_i2c.c
@@ -0,0 +1,286 @@
+/*
+ * Wacom Penabled Driver for I2C
+ *
+ * Copyright (c) 2011 - 2013 Tatsunosuke Tobita, Wacom.
+ * <tobita.tatsunosuke@wacom.co.jp>
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software
+ * Foundation; either version of 2 of the License,
+ * or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <asm/unaligned.h>
+
+#define WACOM_CMD_QUERY0 0x04
+#define WACOM_CMD_QUERY1 0x00
+#define WACOM_CMD_QUERY2 0x33
+#define WACOM_CMD_QUERY3 0x02
+#define WACOM_CMD_THROW0 0x05
+#define WACOM_CMD_THROW1 0x00
+#define WACOM_QUERY_SIZE 19
+
+struct wacom_features {
+ int x_max;
+ int y_max;
+ int pressure_max;
+ char fw_version;
+};
+
+struct wacom_i2c {
+ struct i2c_client *client;
+ struct input_dev *input;
+ u8 data[WACOM_QUERY_SIZE];
+ bool prox;
+ int tool;
+};
+
+static int wacom_query_device(struct i2c_client *client,
+ struct wacom_features *features)
+{
+ int ret;
+ u8 cmd1[] = { WACOM_CMD_QUERY0, WACOM_CMD_QUERY1,
+ WACOM_CMD_QUERY2, WACOM_CMD_QUERY3 };
+ u8 cmd2[] = { WACOM_CMD_THROW0, WACOM_CMD_THROW1 };
+ u8 data[WACOM_QUERY_SIZE];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(cmd1),
+ .buf = cmd1,
+ },
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(cmd2),
+ .buf = cmd2,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(data),
+ .buf = data,
+ },
+ };
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ features->x_max = get_unaligned_le16(&data[3]);
+ features->y_max = get_unaligned_le16(&data[5]);
+ features->pressure_max = get_unaligned_le16(&data[11]);
+ features->fw_version = get_unaligned_le16(&data[13]);
+
+ dev_dbg(&client->dev,
+ "x_max:%d, y_max:%d, pressure:%d, fw:%d\n",
+ features->x_max, features->y_max,
+ features->pressure_max, features->fw_version);
+
+ return 0;
+}
+
+static irqreturn_t wacom_i2c_irq(int irq, void *dev_id)
+{
+ struct wacom_i2c *wac_i2c = dev_id;
+ struct input_dev *input = wac_i2c->input;
+ u8 *data = wac_i2c->data;
+ unsigned int x, y, pressure;
+ unsigned char tsw, f1, f2, ers;
+ int error;
+
+ error = i2c_master_recv(wac_i2c->client,
+ wac_i2c->data, sizeof(wac_i2c->data));
+ if (error < 0)
+ goto out;
+
+ tsw = data[3] & 0x01;
+ ers = data[3] & 0x04;
+ f1 = data[3] & 0x02;
+ f2 = data[3] & 0x10;
+ x = le16_to_cpup((__le16 *)&data[4]);
+ y = le16_to_cpup((__le16 *)&data[6]);
+ pressure = le16_to_cpup((__le16 *)&data[8]);
+
+ if (!wac_i2c->prox)
+ wac_i2c->tool = (data[3] & 0x0c) ?
+ BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+
+ wac_i2c->prox = data[3] & 0x20;
+
+ input_report_key(input, BTN_TOUCH, tsw || ers);
+ input_report_key(input, wac_i2c->tool, wac_i2c->prox);
+ input_report_key(input, BTN_STYLUS, f1);
+ input_report_key(input, BTN_STYLUS2, f2);
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ input_report_abs(input, ABS_PRESSURE, pressure);
+ input_sync(input);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int wacom_i2c_open(struct input_dev *dev)
+{
+ struct wacom_i2c *wac_i2c = input_get_drvdata(dev);
+ struct i2c_client *client = wac_i2c->client;
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static void wacom_i2c_close(struct input_dev *dev)
+{
+ struct wacom_i2c *wac_i2c = input_get_drvdata(dev);
+ struct i2c_client *client = wac_i2c->client;
+
+ disable_irq(client->irq);
+}
+
+static int wacom_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct wacom_i2c *wac_i2c;
+ struct input_dev *input;
+ struct wacom_features features = { 0 };
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "i2c_check_functionality error\n");
+ return -EIO;
+ }
+
+ error = wacom_query_device(client, &features);
+ if (error)
+ return error;
+
+ wac_i2c = kzalloc(sizeof(*wac_i2c), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!wac_i2c || !input) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ wac_i2c->client = client;
+ wac_i2c->input = input;
+
+ input->name = "Wacom I2C Digitizer";
+ input->id.bustype = BUS_I2C;
+ input->id.vendor = 0x56a;
+ input->id.version = features.fw_version;
+ input->dev.parent = &client->dev;
+ input->open = wacom_i2c_open;
+ input->close = wacom_i2c_close;
+
+ input->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ __set_bit(BTN_TOOL_PEN, input->keybit);
+ __set_bit(BTN_TOOL_RUBBER, input->keybit);
+ __set_bit(BTN_STYLUS, input->keybit);
+ __set_bit(BTN_STYLUS2, input->keybit);
+ __set_bit(BTN_TOUCH, input->keybit);
+
+ input_set_abs_params(input, ABS_X, 0, features.x_max, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, features.y_max, 0, 0);
+ input_set_abs_params(input, ABS_PRESSURE,
+ 0, features.pressure_max, 0, 0);
+
+ input_set_drvdata(input, wac_i2c);
+
+ error = request_threaded_irq(client->irq, NULL, wacom_i2c_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "wacom_i2c", wac_i2c);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to enable IRQ, error: %d\n", error);
+ goto err_free_mem;
+ }
+
+ /* Disable the IRQ, we'll enable it in wac_i2c_open() */
+ disable_irq(client->irq);
+
+ error = input_register_device(wac_i2c->input);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to register input device, error: %d\n", error);
+ goto err_free_irq;
+ }
+
+ i2c_set_clientdata(client, wac_i2c);
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, wac_i2c);
+err_free_mem:
+ input_free_device(input);
+ kfree(wac_i2c);
+
+ return error;
+}
+
+static int wacom_i2c_remove(struct i2c_client *client)
+{
+ struct wacom_i2c *wac_i2c = i2c_get_clientdata(client);
+
+ free_irq(client->irq, wac_i2c);
+ input_unregister_device(wac_i2c->input);
+ kfree(wac_i2c);
+
+ return 0;
+}
+
+static int __maybe_unused wacom_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ disable_irq(client->irq);
+
+ return 0;
+}
+
+static int __maybe_unused wacom_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(wacom_i2c_pm, wacom_i2c_suspend, wacom_i2c_resume);
+
+static const struct i2c_device_id wacom_i2c_id[] = {
+ { "WAC_I2C_EMR", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, wacom_i2c_id);
+
+static struct i2c_driver wacom_i2c_driver = {
+ .driver = {
+ .name = "wacom_i2c",
+ .owner = THIS_MODULE,
+ .pm = &wacom_i2c_pm,
+ },
+
+ .probe = wacom_i2c_probe,
+ .remove = wacom_i2c_remove,
+ .id_table = wacom_i2c_id,
+};
+module_i2c_driver(wacom_i2c_driver);
+
+MODULE_AUTHOR("Tatsunosuke Tobita <tobita.tatsunosuke@wacom.co.jp>");
+MODULE_DESCRIPTION("WACOM EMR I2C Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
new file mode 100644
index 000000000..2792ca397
--- /dev/null
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -0,0 +1,596 @@
+/*
+ * Wacom W8001 penabled serial touchscreen driver
+ *
+ * Copyright (c) 2008 Jaya Kumar
+ * Copyright (c) 2010 Red Hat, Inc.
+ * Copyright (c) 2010 - 2011 Ping Cheng, Wacom. <pingc@wacom.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * Layout based on Elo serial touchscreen driver by Vojtech Pavlik
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input/mt.h>
+#include <linux/serio.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+
+#define DRIVER_DESC "Wacom W8001 serial touchscreen driver"
+
+MODULE_AUTHOR("Jaya Kumar <jayakumar.lkml@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define W8001_MAX_LENGTH 11
+#define W8001_LEAD_MASK 0x80
+#define W8001_LEAD_BYTE 0x80
+#define W8001_TAB_MASK 0x40
+#define W8001_TAB_BYTE 0x40
+/* set in first byte of touch data packets */
+#define W8001_TOUCH_MASK (0x10 | W8001_LEAD_MASK)
+#define W8001_TOUCH_BYTE (0x10 | W8001_LEAD_BYTE)
+
+#define W8001_QUERY_PACKET 0x20
+
+#define W8001_CMD_STOP '0'
+#define W8001_CMD_START '1'
+#define W8001_CMD_QUERY '*'
+#define W8001_CMD_TOUCHQUERY '%'
+
+/* length of data packets in bytes, depends on device. */
+#define W8001_PKTLEN_TOUCH93 5
+#define W8001_PKTLEN_TOUCH9A 7
+#define W8001_PKTLEN_TPCPEN 9
+#define W8001_PKTLEN_TPCCTL 11 /* control packet */
+#define W8001_PKTLEN_TOUCH2FG 13
+
+/* resolution in points/mm */
+#define W8001_PEN_RESOLUTION 100
+#define W8001_TOUCH_RESOLUTION 10
+
+struct w8001_coord {
+ u8 rdy;
+ u8 tsw;
+ u8 f1;
+ u8 f2;
+ u16 x;
+ u16 y;
+ u16 pen_pressure;
+ u8 tilt_x;
+ u8 tilt_y;
+};
+
+/* touch query reply packet */
+struct w8001_touch_query {
+ u16 x;
+ u16 y;
+ u8 panel_res;
+ u8 capacity_res;
+ u8 sensor_id;
+};
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct w8001 {
+ struct input_dev *dev;
+ struct serio *serio;
+ struct completion cmd_done;
+ int id;
+ int idx;
+ unsigned char response_type;
+ unsigned char response[W8001_MAX_LENGTH];
+ unsigned char data[W8001_MAX_LENGTH];
+ char phys[32];
+ int type;
+ unsigned int pktlen;
+ u16 max_touch_x;
+ u16 max_touch_y;
+ u16 max_pen_x;
+ u16 max_pen_y;
+ char name[64];
+};
+
+static void parse_pen_data(u8 *data, struct w8001_coord *coord)
+{
+ memset(coord, 0, sizeof(*coord));
+
+ coord->rdy = data[0] & 0x20;
+ coord->tsw = data[0] & 0x01;
+ coord->f1 = data[0] & 0x02;
+ coord->f2 = data[0] & 0x04;
+
+ coord->x = (data[1] & 0x7F) << 9;
+ coord->x |= (data[2] & 0x7F) << 2;
+ coord->x |= (data[6] & 0x60) >> 5;
+
+ coord->y = (data[3] & 0x7F) << 9;
+ coord->y |= (data[4] & 0x7F) << 2;
+ coord->y |= (data[6] & 0x18) >> 3;
+
+ coord->pen_pressure = data[5] & 0x7F;
+ coord->pen_pressure |= (data[6] & 0x07) << 7 ;
+
+ coord->tilt_x = data[7] & 0x7F;
+ coord->tilt_y = data[8] & 0x7F;
+}
+
+static void parse_single_touch(u8 *data, struct w8001_coord *coord)
+{
+ coord->x = (data[1] << 7) | data[2];
+ coord->y = (data[3] << 7) | data[4];
+ coord->tsw = data[0] & 0x01;
+}
+
+static void scale_touch_coordinates(struct w8001 *w8001,
+ unsigned int *x, unsigned int *y)
+{
+ if (w8001->max_pen_x && w8001->max_touch_x)
+ *x = *x * w8001->max_pen_x / w8001->max_touch_x;
+
+ if (w8001->max_pen_y && w8001->max_touch_y)
+ *y = *y * w8001->max_pen_y / w8001->max_touch_y;
+}
+
+static void parse_multi_touch(struct w8001 *w8001)
+{
+ struct input_dev *dev = w8001->dev;
+ unsigned char *data = w8001->data;
+ unsigned int x, y;
+ int i;
+ int count = 0;
+
+ for (i = 0; i < 2; i++) {
+ bool touch = data[0] & (1 << i);
+
+ input_mt_slot(dev, i);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
+ if (touch) {
+ x = (data[6 * i + 1] << 7) | data[6 * i + 2];
+ y = (data[6 * i + 3] << 7) | data[6 * i + 4];
+ /* data[5,6] and [11,12] is finger capacity */
+
+ /* scale to pen maximum */
+ scale_touch_coordinates(w8001, &x, &y);
+
+ input_report_abs(dev, ABS_MT_POSITION_X, x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, y);
+ count++;
+ }
+ }
+
+ /* emulate single touch events when stylus is out of proximity.
+ * This is to make single touch backward support consistent
+ * across all Wacom single touch devices.
+ */
+ if (w8001->type != BTN_TOOL_PEN &&
+ w8001->type != BTN_TOOL_RUBBER) {
+ w8001->type = count == 1 ? BTN_TOOL_FINGER : KEY_RESERVED;
+ input_mt_report_pointer_emulation(dev, true);
+ }
+
+ input_sync(dev);
+}
+
+static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
+{
+ memset(query, 0, sizeof(*query));
+
+ query->panel_res = data[1];
+ query->sensor_id = data[2] & 0x7;
+ query->capacity_res = data[7];
+
+ query->x = data[3] << 9;
+ query->x |= data[4] << 2;
+ query->x |= (data[2] >> 5) & 0x3;
+
+ query->y = data[5] << 9;
+ query->y |= data[6] << 2;
+ query->y |= (data[2] >> 3) & 0x3;
+
+ /* Early days' single-finger touch models need the following defaults */
+ if (!query->x && !query->y) {
+ query->x = 1024;
+ query->y = 1024;
+ if (query->panel_res)
+ query->x = query->y = (1 << query->panel_res);
+ query->panel_res = W8001_TOUCH_RESOLUTION;
+ }
+}
+
+static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
+{
+ struct input_dev *dev = w8001->dev;
+
+ /*
+ * We have 1 bit for proximity (rdy) and 3 bits for tip, side,
+ * side2/eraser. If rdy && f2 are set, this can be either pen + side2,
+ * or eraser. Assume:
+ * - if dev is already in proximity and f2 is toggled → pen + side2
+ * - if dev comes into proximity with f2 set → eraser
+ * If f2 disappears after assuming eraser, fake proximity out for
+ * eraser and in for pen.
+ */
+
+ switch (w8001->type) {
+ case BTN_TOOL_RUBBER:
+ if (!coord->f2) {
+ input_report_abs(dev, ABS_PRESSURE, 0);
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_report_key(dev, BTN_STYLUS, 0);
+ input_report_key(dev, BTN_STYLUS2, 0);
+ input_report_key(dev, BTN_TOOL_RUBBER, 0);
+ input_sync(dev);
+ w8001->type = BTN_TOOL_PEN;
+ }
+ break;
+
+ case BTN_TOOL_FINGER:
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_report_key(dev, BTN_TOOL_FINGER, 0);
+ input_sync(dev);
+ /* fall through */
+
+ case KEY_RESERVED:
+ w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+ break;
+
+ default:
+ input_report_key(dev, BTN_STYLUS2, coord->f2);
+ break;
+ }
+
+ input_report_abs(dev, ABS_X, coord->x);
+ input_report_abs(dev, ABS_Y, coord->y);
+ input_report_abs(dev, ABS_PRESSURE, coord->pen_pressure);
+ input_report_key(dev, BTN_TOUCH, coord->tsw);
+ input_report_key(dev, BTN_STYLUS, coord->f1);
+ input_report_key(dev, w8001->type, coord->rdy);
+ input_sync(dev);
+
+ if (!coord->rdy)
+ w8001->type = KEY_RESERVED;
+}
+
+static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
+{
+ struct input_dev *dev = w8001->dev;
+ unsigned int x = coord->x;
+ unsigned int y = coord->y;
+
+ /* scale to pen maximum */
+ scale_touch_coordinates(w8001, &x, &y);
+
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ input_report_key(dev, BTN_TOUCH, coord->tsw);
+ input_report_key(dev, BTN_TOOL_FINGER, coord->tsw);
+
+ input_sync(dev);
+
+ w8001->type = coord->tsw ? BTN_TOOL_FINGER : KEY_RESERVED;
+}
+
+static irqreturn_t w8001_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct w8001 *w8001 = serio_get_drvdata(serio);
+ struct w8001_coord coord;
+ unsigned char tmp;
+
+ w8001->data[w8001->idx] = data;
+ switch (w8001->idx++) {
+ case 0:
+ if ((data & W8001_LEAD_MASK) != W8001_LEAD_BYTE) {
+ pr_debug("w8001: unsynchronized data: 0x%02x\n", data);
+ w8001->idx = 0;
+ }
+ break;
+
+ case W8001_PKTLEN_TOUCH93 - 1:
+ case W8001_PKTLEN_TOUCH9A - 1:
+ tmp = w8001->data[0] & W8001_TOUCH_BYTE;
+ if (tmp != W8001_TOUCH_BYTE)
+ break;
+
+ if (w8001->pktlen == w8001->idx) {
+ w8001->idx = 0;
+ if (w8001->type != BTN_TOOL_PEN &&
+ w8001->type != BTN_TOOL_RUBBER) {
+ parse_single_touch(w8001->data, &coord);
+ report_single_touch(w8001, &coord);
+ }
+ }
+ break;
+
+ /* Pen coordinates packet */
+ case W8001_PKTLEN_TPCPEN - 1:
+ tmp = w8001->data[0] & W8001_TAB_MASK;
+ if (unlikely(tmp == W8001_TAB_BYTE))
+ break;
+
+ tmp = w8001->data[0] & W8001_TOUCH_BYTE;
+ if (tmp == W8001_TOUCH_BYTE)
+ break;
+
+ w8001->idx = 0;
+ parse_pen_data(w8001->data, &coord);
+ report_pen_events(w8001, &coord);
+ break;
+
+ /* control packet */
+ case W8001_PKTLEN_TPCCTL - 1:
+ tmp = w8001->data[0] & W8001_TOUCH_MASK;
+ if (tmp == W8001_TOUCH_BYTE)
+ break;
+
+ w8001->idx = 0;
+ memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH);
+ w8001->response_type = W8001_QUERY_PACKET;
+ complete(&w8001->cmd_done);
+ break;
+
+ /* 2 finger touch packet */
+ case W8001_PKTLEN_TOUCH2FG - 1:
+ w8001->idx = 0;
+ parse_multi_touch(w8001);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int w8001_command(struct w8001 *w8001, unsigned char command,
+ bool wait_response)
+{
+ int rc;
+
+ w8001->response_type = 0;
+ init_completion(&w8001->cmd_done);
+
+ rc = serio_write(w8001->serio, command);
+ if (rc == 0 && wait_response) {
+
+ wait_for_completion_timeout(&w8001->cmd_done, HZ);
+ if (w8001->response_type != W8001_QUERY_PACKET)
+ rc = -EIO;
+ }
+
+ return rc;
+}
+
+static int w8001_open(struct input_dev *dev)
+{
+ struct w8001 *w8001 = input_get_drvdata(dev);
+
+ return w8001_command(w8001, W8001_CMD_START, false);
+}
+
+static void w8001_close(struct input_dev *dev)
+{
+ struct w8001 *w8001 = input_get_drvdata(dev);
+
+ w8001_command(w8001, W8001_CMD_STOP, false);
+}
+
+static int w8001_setup(struct w8001 *w8001)
+{
+ struct input_dev *dev = w8001->dev;
+ struct w8001_coord coord;
+ struct w8001_touch_query touch;
+ int error;
+
+ error = w8001_command(w8001, W8001_CMD_STOP, false);
+ if (error)
+ return error;
+
+ msleep(250); /* wait 250ms before querying the device */
+
+ dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name));
+
+ __set_bit(INPUT_PROP_DIRECT, dev->propbit);
+
+ /* penabled? */
+ error = w8001_command(w8001, W8001_CMD_QUERY, true);
+ if (!error) {
+ __set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(BTN_TOOL_PEN, dev->keybit);
+ __set_bit(BTN_TOOL_RUBBER, dev->keybit);
+ __set_bit(BTN_STYLUS, dev->keybit);
+ __set_bit(BTN_STYLUS2, dev->keybit);
+
+ parse_pen_data(w8001->response, &coord);
+ w8001->max_pen_x = coord.x;
+ w8001->max_pen_y = coord.y;
+
+ input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
+ input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION);
+ input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION);
+ input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
+ if (coord.tilt_x && coord.tilt_y) {
+ input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
+ input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
+ }
+ w8001->id = 0x90;
+ strlcat(w8001->name, " Penabled", sizeof(w8001->name));
+ }
+
+ /* Touch enabled? */
+ error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
+
+ /*
+ * Some non-touch devices may reply to the touch query. But their
+ * second byte is empty, which indicates touch is not supported.
+ */
+ if (!error && w8001->response[1]) {
+ __set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(BTN_TOOL_FINGER, dev->keybit);
+
+ parse_touchquery(w8001->response, &touch);
+ w8001->max_touch_x = touch.x;
+ w8001->max_touch_y = touch.y;
+
+ if (w8001->max_pen_x && w8001->max_pen_y) {
+ /* if pen is supported scale to pen maximum */
+ touch.x = w8001->max_pen_x;
+ touch.y = w8001->max_pen_y;
+ touch.panel_res = W8001_PEN_RESOLUTION;
+ }
+
+ input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
+ input_abs_set_res(dev, ABS_X, touch.panel_res);
+ input_abs_set_res(dev, ABS_Y, touch.panel_res);
+
+ switch (touch.sensor_id) {
+ case 0:
+ case 2:
+ w8001->pktlen = W8001_PKTLEN_TOUCH93;
+ w8001->id = 0x93;
+ strlcat(w8001->name, " 1FG", sizeof(w8001->name));
+ break;
+
+ case 1:
+ case 3:
+ case 4:
+ w8001->pktlen = W8001_PKTLEN_TOUCH9A;
+ strlcat(w8001->name, " 1FG", sizeof(w8001->name));
+ w8001->id = 0x9a;
+ break;
+
+ case 5:
+ w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
+
+ input_mt_init_slots(dev, 2, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_X,
+ 0, touch.x, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_Y,
+ 0, touch.y, 0, 0);
+ input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
+ 0, MT_TOOL_MAX, 0, 0);
+
+ strlcat(w8001->name, " 2FG", sizeof(w8001->name));
+ if (w8001->max_pen_x && w8001->max_pen_y)
+ w8001->id = 0xE3;
+ else
+ w8001->id = 0xE2;
+ break;
+ }
+ }
+
+ strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
+
+ return 0;
+}
+
+/*
+ * w8001_disconnect() is the opposite of w8001_connect()
+ */
+
+static void w8001_disconnect(struct serio *serio)
+{
+ struct w8001 *w8001 = serio_get_drvdata(serio);
+
+ serio_close(serio);
+
+ input_unregister_device(w8001->dev);
+ kfree(w8001);
+
+ serio_set_drvdata(serio, NULL);
+}
+
+/*
+ * w8001_connect() is the routine that is called when someone adds a
+ * new serio device that supports the w8001 protocol and registers it as
+ * an input device.
+ */
+
+static int w8001_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct w8001 *w8001;
+ struct input_dev *input_dev;
+ int err;
+
+ w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!w8001 || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ w8001->serio = serio;
+ w8001->dev = input_dev;
+ init_completion(&w8001->cmd_done);
+ snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
+
+ serio_set_drvdata(serio, w8001);
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = w8001_setup(w8001);
+ if (err)
+ goto fail3;
+
+ input_dev->name = w8001->name;
+ input_dev->phys = w8001->phys;
+ input_dev->id.product = w8001->id;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = 0x056a;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->open = w8001_open;
+ input_dev->close = w8001_close;
+
+ input_set_drvdata(input_dev, w8001);
+
+ err = input_register_device(w8001->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+fail3:
+ serio_close(serio);
+fail2:
+ serio_set_drvdata(serio, NULL);
+fail1:
+ input_free_device(input_dev);
+ kfree(w8001);
+ return err;
+}
+
+static struct serio_device_id w8001_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_W8001,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, w8001_serio_ids);
+
+static struct serio_driver w8001_drv = {
+ .driver = {
+ .name = "w8001",
+ },
+ .description = DRIVER_DESC,
+ .id_table = w8001_serio_ids,
+ .interrupt = w8001_interrupt,
+ .connect = w8001_connect,
+ .disconnect = w8001_disconnect,
+};
+
+module_serio_driver(w8001_drv);
diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c
new file mode 100644
index 000000000..1db0a1410
--- /dev/null
+++ b/drivers/input/touchscreen/wm831x-ts.c
@@ -0,0 +1,405 @@
+/*
+ * Touchscreen driver for WM831x PMICs
+ *
+ * Copyright 2011 Wolfson Microelectronics plc.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/pm.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/irq.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+/*
+ * R16424 (0x4028) - Touch Control 1
+ */
+#define WM831X_TCH_ENA 0x8000 /* TCH_ENA */
+#define WM831X_TCH_CVT_ENA 0x4000 /* TCH_CVT_ENA */
+#define WM831X_TCH_SLPENA 0x1000 /* TCH_SLPENA */
+#define WM831X_TCH_Z_ENA 0x0400 /* TCH_Z_ENA */
+#define WM831X_TCH_Y_ENA 0x0200 /* TCH_Y_ENA */
+#define WM831X_TCH_X_ENA 0x0100 /* TCH_X_ENA */
+#define WM831X_TCH_DELAY_MASK 0x00E0 /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_DELAY_SHIFT 5 /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_DELAY_WIDTH 3 /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_RATE_MASK 0x001F /* TCH_RATE - [4:0] */
+#define WM831X_TCH_RATE_SHIFT 0 /* TCH_RATE - [4:0] */
+#define WM831X_TCH_RATE_WIDTH 5 /* TCH_RATE - [4:0] */
+
+/*
+ * R16425 (0x4029) - Touch Control 2
+ */
+#define WM831X_TCH_PD_WK 0x2000 /* TCH_PD_WK */
+#define WM831X_TCH_5WIRE 0x1000 /* TCH_5WIRE */
+#define WM831X_TCH_PDONLY 0x0800 /* TCH_PDONLY */
+#define WM831X_TCH_ISEL 0x0100 /* TCH_ISEL */
+#define WM831X_TCH_RPU_MASK 0x000F /* TCH_RPU - [3:0] */
+#define WM831X_TCH_RPU_SHIFT 0 /* TCH_RPU - [3:0] */
+#define WM831X_TCH_RPU_WIDTH 4 /* TCH_RPU - [3:0] */
+
+/*
+ * R16426-8 (0x402A-C) - Touch Data X/Y/X
+ */
+#define WM831X_TCH_PD 0x8000 /* TCH_PD1 */
+#define WM831X_TCH_DATA_MASK 0x0FFF /* TCH_DATA - [11:0] */
+#define WM831X_TCH_DATA_SHIFT 0 /* TCH_DATA - [11:0] */
+#define WM831X_TCH_DATA_WIDTH 12 /* TCH_DATA - [11:0] */
+
+struct wm831x_ts {
+ struct input_dev *input_dev;
+ struct wm831x *wm831x;
+ unsigned int data_irq;
+ unsigned int pd_irq;
+ bool pressure;
+ bool pen_down;
+ struct work_struct pd_data_work;
+};
+
+static void wm831x_pd_data_work(struct work_struct *work)
+{
+ struct wm831x_ts *wm831x_ts =
+ container_of(work, struct wm831x_ts, pd_data_work);
+
+ if (wm831x_ts->pen_down) {
+ enable_irq(wm831x_ts->data_irq);
+ dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n");
+ } else {
+ enable_irq(wm831x_ts->pd_irq);
+ dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n");
+ }
+}
+
+static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data)
+{
+ struct wm831x_ts *wm831x_ts = irq_data;
+ struct wm831x *wm831x = wm831x_ts->wm831x;
+ static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE };
+ u16 data[3];
+ int count;
+ int i, ret;
+
+ if (wm831x_ts->pressure)
+ count = 3;
+ else
+ count = 2;
+
+ wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+ WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
+
+ ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count,
+ data);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to read touch data: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ /*
+ * We get a pen down reading on every reading, report pen up if any
+ * individual reading does so.
+ */
+ wm831x_ts->pen_down = true;
+ for (i = 0; i < count; i++) {
+ if (!(data[i] & WM831X_TCH_PD)) {
+ wm831x_ts->pen_down = false;
+ continue;
+ }
+ input_report_abs(wm831x_ts->input_dev, data_types[i],
+ data[i] & WM831X_TCH_DATA_MASK);
+ }
+
+ if (!wm831x_ts->pen_down) {
+ /* Switch from data to pen down */
+ dev_dbg(wm831x->dev, "IRQ DATA->PD\n");
+
+ disable_irq_nosync(wm831x_ts->data_irq);
+
+ /* Don't need data any more */
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
+ WM831X_TCH_Z_ENA, 0);
+
+ /* Flush any final samples that arrived while reading */
+ wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+ WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
+
+ wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data);
+
+ if (wm831x_ts->pressure)
+ input_report_abs(wm831x_ts->input_dev,
+ ABS_PRESSURE, 0);
+
+ input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0);
+
+ schedule_work(&wm831x_ts->pd_data_work);
+ } else {
+ input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1);
+ }
+
+ input_sync(wm831x_ts->input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data)
+{
+ struct wm831x_ts *wm831x_ts = irq_data;
+ struct wm831x *wm831x = wm831x_ts->wm831x;
+ int ena = 0;
+
+ if (wm831x_ts->pen_down)
+ return IRQ_HANDLED;
+
+ disable_irq_nosync(wm831x_ts->pd_irq);
+
+ /* Start collecting data */
+ if (wm831x_ts->pressure)
+ ena |= WM831X_TCH_Z_ENA;
+
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA,
+ WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena);
+
+ wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+ WM831X_TCHPD_EINT, WM831X_TCHPD_EINT);
+
+ wm831x_ts->pen_down = true;
+
+ /* Switch from pen down to data */
+ dev_dbg(wm831x->dev, "IRQ PD->DATA\n");
+ schedule_work(&wm831x_ts->pd_data_work);
+
+ return IRQ_HANDLED;
+}
+
+static int wm831x_ts_input_open(struct input_dev *idev)
+{
+ struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
+ struct wm831x *wm831x = wm831x_ts->wm831x;
+
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_ENA | WM831X_TCH_CVT_ENA |
+ WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
+ WM831X_TCH_Z_ENA, WM831X_TCH_ENA);
+
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA);
+
+ return 0;
+}
+
+static void wm831x_ts_input_close(struct input_dev *idev)
+{
+ struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
+ struct wm831x *wm831x = wm831x_ts->wm831x;
+
+ /* Shut the controller down, disabling all other functionality too */
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_ENA | WM831X_TCH_X_ENA |
+ WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0);
+
+ /* Make sure any pending IRQs are done, the above will prevent
+ * new ones firing.
+ */
+ synchronize_irq(wm831x_ts->data_irq);
+ synchronize_irq(wm831x_ts->pd_irq);
+
+ /* Make sure the IRQ completion work is quiesced */
+ flush_work(&wm831x_ts->pd_data_work);
+
+ /* If we ended up with the pen down then make sure we revert back
+ * to pen detection state for the next time we start up.
+ */
+ if (wm831x_ts->pen_down) {
+ disable_irq(wm831x_ts->data_irq);
+ enable_irq(wm831x_ts->pd_irq);
+ wm831x_ts->pen_down = false;
+ }
+}
+
+static int wm831x_ts_probe(struct platform_device *pdev)
+{
+ struct wm831x_ts *wm831x_ts;
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent);
+ struct wm831x_touch_pdata *pdata = NULL;
+ struct input_dev *input_dev;
+ int error, irqf;
+
+ if (core_pdata)
+ pdata = core_pdata->touch;
+
+ wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts),
+ GFP_KERNEL);
+ input_dev = devm_input_allocate_device(&pdev->dev);
+ if (!wm831x_ts || !input_dev) {
+ error = -ENOMEM;
+ goto err_alloc;
+ }
+
+ wm831x_ts->wm831x = wm831x;
+ wm831x_ts->input_dev = input_dev;
+ INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work);
+
+ /*
+ * If we have a direct IRQ use it, otherwise use the interrupt
+ * from the WM831x IRQ controller.
+ */
+ wm831x_ts->data_irq = wm831x_irq(wm831x,
+ platform_get_irq_byname(pdev,
+ "TCHDATA"));
+ if (pdata && pdata->data_irq)
+ wm831x_ts->data_irq = pdata->data_irq;
+
+ wm831x_ts->pd_irq = wm831x_irq(wm831x,
+ platform_get_irq_byname(pdev, "TCHPD"));
+ if (pdata && pdata->pd_irq)
+ wm831x_ts->pd_irq = pdata->pd_irq;
+
+ if (pdata)
+ wm831x_ts->pressure = pdata->pressure;
+ else
+ wm831x_ts->pressure = true;
+
+ /* Five wire touchscreens can't report pressure */
+ if (pdata && pdata->fivewire) {
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_5WIRE, WM831X_TCH_5WIRE);
+
+ /* Pressure measurements are not possible for five wire mode */
+ WARN_ON(pdata->pressure && pdata->fivewire);
+ wm831x_ts->pressure = false;
+ } else {
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_5WIRE, 0);
+ }
+
+ if (pdata) {
+ switch (pdata->isel) {
+ default:
+ dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n",
+ pdata->isel);
+ /* Fall through */
+ case 200:
+ case 0:
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_ISEL, 0);
+ break;
+ case 400:
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_ISEL, WM831X_TCH_ISEL);
+ break;
+ }
+ }
+
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_PDONLY, 0);
+
+ /* Default to 96 samples/sec */
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_RATE_MASK, 6);
+
+ if (pdata && pdata->data_irqf)
+ irqf = pdata->data_irqf;
+ else
+ irqf = IRQF_TRIGGER_HIGH;
+
+ error = request_threaded_irq(wm831x_ts->data_irq,
+ NULL, wm831x_ts_data_irq,
+ irqf | IRQF_ONESHOT,
+ "Touchscreen data", wm831x_ts);
+ if (error) {
+ dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n",
+ wm831x_ts->data_irq, error);
+ goto err_alloc;
+ }
+ disable_irq(wm831x_ts->data_irq);
+
+ if (pdata && pdata->pd_irqf)
+ irqf = pdata->pd_irqf;
+ else
+ irqf = IRQF_TRIGGER_HIGH;
+
+ error = request_threaded_irq(wm831x_ts->pd_irq,
+ NULL, wm831x_ts_pen_down_irq,
+ irqf | IRQF_ONESHOT,
+ "Touchscreen pen down", wm831x_ts);
+ if (error) {
+ dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n",
+ wm831x_ts->pd_irq, error);
+ goto err_data_irq;
+ }
+
+ /* set up touch configuration */
+ input_dev->name = "WM831x touchscreen";
+ input_dev->phys = "wm831x";
+ input_dev->open = wm831x_ts_input_open;
+ input_dev->close = wm831x_ts_input_close;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ input_set_abs_params(input_dev, ABS_X, 0, 4095, 5, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 4095, 5, 0);
+ if (wm831x_ts->pressure)
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 4095, 5, 0);
+
+ input_set_drvdata(input_dev, wm831x_ts);
+ input_dev->dev.parent = &pdev->dev;
+
+ error = input_register_device(input_dev);
+ if (error)
+ goto err_pd_irq;
+
+ platform_set_drvdata(pdev, wm831x_ts);
+ return 0;
+
+err_pd_irq:
+ free_irq(wm831x_ts->pd_irq, wm831x_ts);
+err_data_irq:
+ free_irq(wm831x_ts->data_irq, wm831x_ts);
+err_alloc:
+
+ return error;
+}
+
+static int wm831x_ts_remove(struct platform_device *pdev)
+{
+ struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev);
+
+ free_irq(wm831x_ts->pd_irq, wm831x_ts);
+ free_irq(wm831x_ts->data_irq, wm831x_ts);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_ts_driver = {
+ .driver = {
+ .name = "wm831x-touch",
+ },
+ .probe = wm831x_ts_probe,
+ .remove = wm831x_ts_remove,
+};
+module_platform_driver(wm831x_ts_driver);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM831x PMIC touchscreen driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-touch");
diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c
new file mode 100644
index 000000000..adc13a523
--- /dev/null
+++ b/drivers/input/touchscreen/wm9705.c
@@ -0,0 +1,350 @@
+/*
+ * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ * Andrew Zabolotny <zap@homelink.ru>
+ * Russell King <rmk@arm.linux.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME "wm97xx"
+#define WM9705_VERSION "1.00"
+#define DEFAULT_PRESSURE 0xb0c0
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ * pil = 1 to use 200uA and
+ * pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 4;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Pen detect comparator threshold.
+ *
+ * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold
+ * i.e. 1 = Vmid/15 threshold
+ * 15 = Vmid/1 threshold
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down events.
+ */
+static int pdd = 8;
+module_param(pdd, int, 0);
+MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+ 21, /* 1 AC97 Link frames */
+ 42, /* 2 */
+ 84, /* 4 */
+ 167, /* 8 */
+ 333, /* 16 */
+ 667, /* 32 */
+ 1000, /* 48 */
+ 1333, /* 64 */
+ 2000, /* 96 */
+ 2667, /* 128 */
+ 3333, /* 160 */
+ 4000, /* 192 */
+ 4667, /* 224 */
+ 5333, /* 256 */
+ 6000, /* 288 */
+ 0 /* No delay, switch matrix always on */
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+ udelay(3 * AC97_LINK_FRAME + delay_table[d]);
+}
+
+/*
+ * set up the physical settings of the WM9705
+ */
+static void wm9705_phy_init(struct wm97xx *wm)
+{
+ u16 dig1 = 0, dig2 = WM97XX_RPR;
+
+ /*
+ * mute VIDEO and AUX as they share X and Y touchscreen
+ * inputs on the WM9705
+ */
+ wm97xx_reg_write(wm, AC97_AUX, 0x8000);
+ wm97xx_reg_write(wm, AC97_VIDEO, 0x8000);
+
+ /* touchpanel pressure current*/
+ if (pil == 2) {
+ dig2 |= WM9705_PIL;
+ dev_dbg(wm->dev,
+ "setting pressure measurement current to 400uA.");
+ } else if (pil)
+ dev_dbg(wm->dev,
+ "setting pressure measurement current to 200uA.");
+ if (!pil)
+ pressure = 0;
+
+ /* polling mode sample settling delay */
+ if (delay != 4) {
+ if (delay < 0 || delay > 15) {
+ dev_dbg(wm->dev, "supplied delay out of range.");
+ delay = 4;
+ }
+ }
+ dig1 &= 0xff0f;
+ dig1 |= WM97XX_DELAY(delay);
+ dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.",
+ delay_table[delay]);
+
+ /* WM9705 pdd */
+ dig2 |= (pdd & 0x000f);
+ dev_dbg(wm->dev, "setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
+
+ /* mask */
+ dig2 |= ((mask & 0x3) << 4);
+
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+}
+
+static void wm9705_dig_enable(struct wm97xx *wm, int enable)
+{
+ if (enable) {
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+ wm->dig[2] | WM97XX_PRP_DET_DIG);
+ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+ } else
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+ wm->dig[2] & ~WM97XX_PRP_DET_DIG);
+}
+
+static void wm9705_aux_prepare(struct wm97xx *wm)
+{
+ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
+}
+
+static void wm9705_dig_restore(struct wm97xx *wm)
+{
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
+}
+
+static inline int is_pden(struct wm97xx *wm)
+{
+ return wm->dig[2] & WM9705_PDEN;
+}
+
+/*
+ * Read a sample from the WM9705 adc in polling mode.
+ */
+static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
+{
+ int timeout = 5 * delay;
+ bool wants_pen = adcsel & WM97XX_PEN_DOWN;
+
+ if (wants_pen && !wm->pen_probably_down) {
+ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (!(data & WM97XX_PEN_DOWN))
+ return RC_PENUP;
+ wm->pen_probably_down = 1;
+ }
+
+ /* set up digitiser */
+ if (wm->mach_ops && wm->mach_ops->pre_sample)
+ wm->mach_ops->pre_sample(adcsel);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK)
+ | WM97XX_POLL | WM97XX_DELAY(delay));
+
+ /* wait 3 AC97 time slots + delay for conversion */
+ poll_delay(delay);
+
+ /* wait for POLL to go low */
+ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
+ && timeout) {
+ udelay(AC97_LINK_FRAME);
+ timeout--;
+ }
+
+ if (timeout == 0) {
+ /* If PDEN is set, we can get a timeout when pen goes up */
+ if (is_pden(wm))
+ wm->pen_probably_down = 0;
+ else
+ dev_dbg(wm->dev, "adc sample timeout");
+ return RC_PENUP;
+ }
+
+ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (wm->mach_ops && wm->mach_ops->post_sample)
+ wm->mach_ops->post_sample(adcsel);
+
+ /* check we have correct sample */
+ if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+ dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+ adcsel & WM97XX_ADCSEL_MASK,
+ *sample & WM97XX_ADCSEL_MASK);
+ return RC_PENUP;
+ }
+
+ if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
+ wm->pen_probably_down = 0;
+ return RC_PENUP;
+ }
+
+ return RC_VALID;
+}
+
+/*
+ * Sample the WM9705 touchscreen in polling mode
+ */
+static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
+{
+ int rc;
+
+ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);
+ if (rc != RC_VALID)
+ return rc;
+ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);
+ if (rc != RC_VALID)
+ return rc;
+ if (pil) {
+ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, &data->p);
+ if (rc != RC_VALID)
+ return rc;
+ } else
+ data->p = DEFAULT_PRESSURE;
+
+ return RC_VALID;
+}
+
+/*
+ * Enable WM9705 continuous mode, i.e. touch data is streamed across
+ * an AC97 slot
+ */
+static int wm9705_acc_enable(struct wm97xx *wm, int enable)
+{
+ u16 dig1, dig2;
+ int ret = 0;
+
+ dig1 = wm->dig[1];
+ dig2 = wm->dig[2];
+
+ if (enable) {
+ /* continuous mode */
+ if (wm->mach_ops->acc_startup &&
+ (ret = wm->mach_ops->acc_startup(wm)) < 0)
+ return ret;
+ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
+ WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
+ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
+ WM97XX_DELAY(delay) |
+ WM97XX_SLT(wm->acc_slot) |
+ WM97XX_RATE(wm->acc_rate);
+ if (pil)
+ dig1 |= WM97XX_ADCSEL_PRES;
+ dig2 |= WM9705_PDEN;
+ } else {
+ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
+ dig2 &= ~WM9705_PDEN;
+ if (wm->mach_ops->acc_shutdown)
+ wm->mach_ops->acc_shutdown(wm);
+ }
+
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+
+ return ret;
+}
+
+struct wm97xx_codec_drv wm9705_codec = {
+ .id = WM9705_ID2,
+ .name = "wm9705",
+ .poll_sample = wm9705_poll_sample,
+ .poll_touch = wm9705_poll_touch,
+ .acc_enable = wm9705_acc_enable,
+ .phy_init = wm9705_phy_init,
+ .dig_enable = wm9705_dig_enable,
+ .dig_restore = wm9705_dig_restore,
+ .aux_prepare = wm9705_aux_prepare,
+};
+EXPORT_SYMBOL_GPL(wm9705_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c
new file mode 100644
index 000000000..705ffa1e0
--- /dev/null
+++ b/drivers/input/touchscreen/wm9712.c
@@ -0,0 +1,471 @@
+/*
+ * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ * Andrew Zabolotny <zap@homelink.ru>
+ * Russell King <rmk@arm.linux.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME "wm97xx"
+#define WM9712_VERSION "1.00"
+#define DEFAULT_PRESSURE 0xb0c0
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set internal pull up for pen detect.
+ *
+ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
+ * i.e. pull up resistance = 64k Ohms / rpu.
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down event.
+ */
+static int rpu = 8;
+module_param(rpu, int, 0);
+MODULE_PARM_DESC(rpu, "Set internal pull up resistor for pen detect.");
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ * pil = 1 to use 200uA and
+ * pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 3;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Set five_wire = 1 to use a 5 wire touchscreen.
+ *
+ * NOTE: Five wire mode does not allow for readback of pressure.
+ */
+static int five_wire;
+module_param(five_wire, int, 0);
+MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen.");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * Coordinate Polling Enable.
+ *
+ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
+ * for every poll.
+ */
+static int coord;
+module_param(coord, int, 0);
+MODULE_PARM_DESC(coord, "Polling coordinate mode");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+ 21, /* 1 AC97 Link frames */
+ 42, /* 2 */
+ 84, /* 4 */
+ 167, /* 8 */
+ 333, /* 16 */
+ 667, /* 32 */
+ 1000, /* 48 */
+ 1333, /* 64 */
+ 2000, /* 96 */
+ 2667, /* 128 */
+ 3333, /* 160 */
+ 4000, /* 192 */
+ 4667, /* 224 */
+ 5333, /* 256 */
+ 6000, /* 288 */
+ 0 /* No delay, switch matrix always on */
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+ udelay(3 * AC97_LINK_FRAME + delay_table[d]);
+}
+
+/*
+ * set up the physical settings of the WM9712
+ */
+static void wm9712_phy_init(struct wm97xx *wm)
+{
+ u16 dig1 = 0;
+ u16 dig2 = WM97XX_RPR | WM9712_RPU(1);
+
+ /* WM9712 rpu */
+ if (rpu) {
+ dig2 &= 0xffc0;
+ dig2 |= WM9712_RPU(rpu);
+ dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms\n",
+ 64000 / rpu);
+ }
+
+ /* WM9712 five wire */
+ if (five_wire) {
+ dig2 |= WM9712_45W;
+ dev_dbg(wm->dev, "setting 5-wire touchscreen mode.\n");
+
+ if (pil) {
+ dev_warn(wm->dev, "pressure measurement is not "
+ "supported in 5-wire mode\n");
+ pil = 0;
+ }
+ }
+
+ /* touchpanel pressure current*/
+ if (pil == 2) {
+ dig2 |= WM9712_PIL;
+ dev_dbg(wm->dev,
+ "setting pressure measurement current to 400uA.\n");
+ } else if (pil)
+ dev_dbg(wm->dev,
+ "setting pressure measurement current to 200uA.\n");
+ if (!pil)
+ pressure = 0;
+
+ /* polling mode sample settling delay */
+ if (delay < 0 || delay > 15) {
+ dev_dbg(wm->dev, "supplied delay out of range.\n");
+ delay = 4;
+ }
+ dig1 &= 0xff0f;
+ dig1 |= WM97XX_DELAY(delay);
+ dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.\n",
+ delay_table[delay]);
+
+ /* mask */
+ dig2 |= ((mask & 0x3) << 6);
+ if (mask) {
+ u16 reg;
+ /* Set GPIO4 as Mask Pin*/
+ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+ wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4);
+ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4);
+ }
+
+ /* wait - coord mode */
+ if (coord)
+ dig2 |= WM9712_WAIT;
+
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+}
+
+static void wm9712_dig_enable(struct wm97xx *wm, int enable)
+{
+ u16 dig2 = wm->dig[2];
+
+ if (enable) {
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+ dig2 | WM97XX_PRP_DET_DIG);
+ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+ } else
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+ dig2 & ~WM97XX_PRP_DET_DIG);
+}
+
+static void wm9712_aux_prepare(struct wm97xx *wm)
+{
+ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
+}
+
+static void wm9712_dig_restore(struct wm97xx *wm)
+{
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
+}
+
+static inline int is_pden(struct wm97xx *wm)
+{
+ return wm->dig[2] & WM9712_PDEN;
+}
+
+/*
+ * Read a sample from the WM9712 adc in polling mode.
+ */
+static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
+{
+ int timeout = 5 * delay;
+ bool wants_pen = adcsel & WM97XX_PEN_DOWN;
+
+ if (wants_pen && !wm->pen_probably_down) {
+ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (!(data & WM97XX_PEN_DOWN))
+ return RC_PENUP;
+ wm->pen_probably_down = 1;
+ }
+
+ /* set up digitiser */
+ if (wm->mach_ops && wm->mach_ops->pre_sample)
+ wm->mach_ops->pre_sample(adcsel);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK)
+ | WM97XX_POLL | WM97XX_DELAY(delay));
+
+ /* wait 3 AC97 time slots + delay for conversion */
+ poll_delay(delay);
+
+ /* wait for POLL to go low */
+ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
+ && timeout) {
+ udelay(AC97_LINK_FRAME);
+ timeout--;
+ }
+
+ if (timeout <= 0) {
+ /* If PDEN is set, we can get a timeout when pen goes up */
+ if (is_pden(wm))
+ wm->pen_probably_down = 0;
+ else
+ dev_dbg(wm->dev, "adc sample timeout\n");
+ return RC_PENUP;
+ }
+
+ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (wm->mach_ops && wm->mach_ops->post_sample)
+ wm->mach_ops->post_sample(adcsel);
+
+ /* check we have correct sample */
+ if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+ dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x\n",
+ adcsel & WM97XX_ADCSEL_MASK,
+ *sample & WM97XX_ADCSEL_MASK);
+ return RC_AGAIN;
+ }
+
+ if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
+ /* Sometimes it reads a wrong value the first time. */
+ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (!(*sample & WM97XX_PEN_DOWN)) {
+ wm->pen_probably_down = 0;
+ return RC_PENUP;
+ }
+ }
+
+ return RC_VALID;
+}
+
+/*
+ * Read a coord from the WM9712 adc in polling mode.
+ */
+static int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data)
+{
+ int timeout = 5 * delay;
+
+ if (!wm->pen_probably_down) {
+ u16 data_rd = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (!(data_rd & WM97XX_PEN_DOWN))
+ return RC_PENUP;
+ wm->pen_probably_down = 1;
+ }
+
+ /* set up digitiser */
+ if (wm->mach_ops && wm->mach_ops->pre_sample)
+ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
+ WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay));
+
+ /* wait 3 AC97 time slots + delay for conversion and read x */
+ poll_delay(delay);
+ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ /* wait for POLL to go low */
+ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
+ && timeout) {
+ udelay(AC97_LINK_FRAME);
+ timeout--;
+ }
+
+ if (timeout <= 0) {
+ /* If PDEN is set, we can get a timeout when pen goes up */
+ if (is_pden(wm))
+ wm->pen_probably_down = 0;
+ else
+ dev_dbg(wm->dev, "adc sample timeout\n");
+ return RC_PENUP;
+ }
+
+ /* read back y data */
+ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (pil)
+ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ else
+ data->p = DEFAULT_PRESSURE;
+
+ if (wm->mach_ops && wm->mach_ops->post_sample)
+ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+ /* check we have correct sample */
+ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
+ goto err;
+ if (pil && !(data->p & WM97XX_ADCSEL_PRES))
+ goto err;
+
+ if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) {
+ wm->pen_probably_down = 0;
+ return RC_PENUP;
+ }
+ return RC_VALID;
+err:
+ return 0;
+}
+
+/*
+ * Sample the WM9712 touchscreen in polling mode
+ */
+static int wm9712_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
+{
+ int rc;
+
+ if (coord) {
+ rc = wm9712_poll_coord(wm, data);
+ if (rc != RC_VALID)
+ return rc;
+ } else {
+ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN,
+ &data->x);
+ if (rc != RC_VALID)
+ return rc;
+
+ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN,
+ &data->y);
+ if (rc != RC_VALID)
+ return rc;
+
+ if (pil && !five_wire) {
+ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN,
+ &data->p);
+ if (rc != RC_VALID)
+ return rc;
+ } else
+ data->p = DEFAULT_PRESSURE;
+ }
+ return RC_VALID;
+}
+
+/*
+ * Enable WM9712 continuous mode, i.e. touch data is streamed across
+ * an AC97 slot
+ */
+static int wm9712_acc_enable(struct wm97xx *wm, int enable)
+{
+ u16 dig1, dig2;
+ int ret = 0;
+
+ dig1 = wm->dig[1];
+ dig2 = wm->dig[2];
+
+ if (enable) {
+ /* continuous mode */
+ if (wm->mach_ops->acc_startup) {
+ ret = wm->mach_ops->acc_startup(wm);
+ if (ret < 0)
+ return ret;
+ }
+ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
+ WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
+ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
+ WM97XX_DELAY(delay) |
+ WM97XX_SLT(wm->acc_slot) |
+ WM97XX_RATE(wm->acc_rate);
+ if (pil)
+ dig1 |= WM97XX_ADCSEL_PRES;
+ dig2 |= WM9712_PDEN;
+ } else {
+ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
+ dig2 &= ~WM9712_PDEN;
+ if (wm->mach_ops->acc_shutdown)
+ wm->mach_ops->acc_shutdown(wm);
+ }
+
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+
+ return 0;
+}
+
+struct wm97xx_codec_drv wm9712_codec = {
+ .id = WM9712_ID2,
+ .name = "wm9712",
+ .poll_sample = wm9712_poll_sample,
+ .poll_touch = wm9712_poll_touch,
+ .acc_enable = wm9712_acc_enable,
+ .phy_init = wm9712_phy_init,
+ .dig_enable = wm9712_dig_enable,
+ .dig_restore = wm9712_dig_restore,
+ .aux_prepare = wm9712_aux_prepare,
+};
+EXPORT_SYMBOL_GPL(wm9712_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("WM9712 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c
new file mode 100644
index 000000000..572a5a64f
--- /dev/null
+++ b/drivers/input/touchscreen/wm9713.c
@@ -0,0 +1,481 @@
+/*
+ * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ * Andrew Zabolotny <zap@homelink.ru>
+ * Russell King <rmk@arm.linux.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME "wm97xx"
+#define WM9713_VERSION "1.00"
+#define DEFAULT_PRESSURE 0xb0c0
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set internal pull up for pen detect.
+ *
+ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
+ * i.e. pull up resistance = 64k Ohms / rpu.
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down event.
+ */
+static int rpu = 8;
+module_param(rpu, int, 0);
+MODULE_PARM_DESC(rpu, "Set internal pull up resistor for pen detect.");
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ * pil = 1 to use 200uA and
+ * pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 4;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Set five_wire = 1 to use a 5 wire touchscreen.
+ *
+ * NOTE: Five wire mode does not allow for readback of pressure.
+ */
+static int five_wire;
+module_param(five_wire, int, 0);
+MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen.");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * Coordinate Polling Enable.
+ *
+ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
+ * for every poll.
+ */
+static int coord;
+module_param(coord, int, 0);
+MODULE_PARM_DESC(coord, "Polling coordinate mode");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+ 21, /* 1 AC97 Link frames */
+ 42, /* 2 */
+ 84, /* 4 */
+ 167, /* 8 */
+ 333, /* 16 */
+ 667, /* 32 */
+ 1000, /* 48 */
+ 1333, /* 64 */
+ 2000, /* 96 */
+ 2667, /* 128 */
+ 3333, /* 160 */
+ 4000, /* 192 */
+ 4667, /* 224 */
+ 5333, /* 256 */
+ 6000, /* 288 */
+ 0 /* No delay, switch matrix always on */
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+ udelay(3 * AC97_LINK_FRAME + delay_table[d]);
+}
+
+/*
+ * set up the physical settings of the WM9713
+ */
+static void wm9713_phy_init(struct wm97xx *wm)
+{
+ u16 dig1 = 0, dig2, dig3;
+
+ /* default values */
+ dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5);
+ dig3 = WM9712_RPU(1);
+
+ /* rpu */
+ if (rpu) {
+ dig3 &= 0xffc0;
+ dig3 |= WM9712_RPU(rpu);
+ dev_info(wm->dev, "setting pen detect pull-up to %d Ohms\n",
+ 64000 / rpu);
+ }
+
+ /* Five wire panel? */
+ if (five_wire) {
+ dig3 |= WM9713_45W;
+ dev_info(wm->dev, "setting 5-wire touchscreen mode.");
+
+ if (pil) {
+ dev_warn(wm->dev,
+ "Pressure measurement not supported in 5 "
+ "wire mode, disabling\n");
+ pil = 0;
+ }
+ }
+
+ /* touchpanel pressure */
+ if (pil == 2) {
+ dig3 |= WM9712_PIL;
+ dev_info(wm->dev,
+ "setting pressure measurement current to 400uA.");
+ } else if (pil)
+ dev_info(wm->dev,
+ "setting pressure measurement current to 200uA.");
+ if (!pil)
+ pressure = 0;
+
+ /* sample settling delay */
+ if (delay < 0 || delay > 15) {
+ dev_info(wm->dev, "supplied delay out of range.");
+ delay = 4;
+ dev_info(wm->dev, "setting adc sample delay to %d u Secs.",
+ delay_table[delay]);
+ }
+ dig2 &= 0xff0f;
+ dig2 |= WM97XX_DELAY(delay);
+
+ /* mask */
+ dig3 |= ((mask & 0x3) << 4);
+ if (coord)
+ dig3 |= WM9713_WAIT;
+
+ wm->misc = wm97xx_reg_read(wm, 0x5a);
+
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
+ wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0);
+}
+
+static void wm9713_dig_enable(struct wm97xx *wm, int enable)
+{
+ u16 val;
+
+ if (enable) {
+ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
+ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] |
+ WM97XX_PRP_DET_DIG);
+ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+ } else {
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] &
+ ~WM97XX_PRP_DET_DIG);
+ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
+ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000);
+ }
+}
+
+static void wm9713_dig_restore(struct wm97xx *wm)
+{
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]);
+}
+
+static void wm9713_aux_prepare(struct wm97xx *wm)
+{
+ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG);
+}
+
+static inline int is_pden(struct wm97xx *wm)
+{
+ return wm->dig[2] & WM9713_PDEN;
+}
+
+/*
+ * Read a sample from the WM9713 adc in polling mode.
+ */
+static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
+{
+ u16 dig1;
+ int timeout = 5 * delay;
+ bool wants_pen = adcsel & WM97XX_PEN_DOWN;
+
+ if (wants_pen && !wm->pen_probably_down) {
+ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (!(data & WM97XX_PEN_DOWN))
+ return RC_PENUP;
+ wm->pen_probably_down = 1;
+ }
+
+ /* set up digitiser */
+ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
+ dig1 &= ~WM9713_ADCSEL_MASK;
+ /* WM97XX_ADCSEL_* channels need to be converted to WM9713 format */
+ dig1 |= 1 << ((adcsel & WM97XX_ADCSEL_MASK) >> 12);
+
+ if (wm->mach_ops && wm->mach_ops->pre_sample)
+ wm->mach_ops->pre_sample(adcsel);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL);
+
+ /* wait 3 AC97 time slots + delay for conversion */
+ poll_delay(delay);
+
+ /* wait for POLL to go low */
+ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) &&
+ timeout) {
+ udelay(AC97_LINK_FRAME);
+ timeout--;
+ }
+
+ if (timeout <= 0) {
+ /* If PDEN is set, we can get a timeout when pen goes up */
+ if (is_pden(wm))
+ wm->pen_probably_down = 0;
+ else
+ dev_dbg(wm->dev, "adc sample timeout");
+ return RC_PENUP;
+ }
+
+ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (wm->mach_ops && wm->mach_ops->post_sample)
+ wm->mach_ops->post_sample(adcsel);
+
+ /* check we have correct sample */
+ if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+ dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+ adcsel & WM97XX_ADCSEL_MASK,
+ *sample & WM97XX_ADCSEL_MASK);
+ return RC_PENUP;
+ }
+
+ if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
+ wm->pen_probably_down = 0;
+ return RC_PENUP;
+ }
+
+ return RC_VALID;
+}
+
+/*
+ * Read a coordinate from the WM9713 adc in polling mode.
+ */
+static int wm9713_poll_coord(struct wm97xx *wm, struct wm97xx_data *data)
+{
+ u16 dig1;
+ int timeout = 5 * delay;
+
+ if (!wm->pen_probably_down) {
+ u16 val = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (!(val & WM97XX_PEN_DOWN))
+ return RC_PENUP;
+ wm->pen_probably_down = 1;
+ }
+
+ /* set up digitiser */
+ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
+ dig1 &= ~WM9713_ADCSEL_MASK;
+ if (pil)
+ dig1 |= WM9713_ADCSEL_PRES;
+
+ if (wm->mach_ops && wm->mach_ops->pre_sample)
+ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1,
+ dig1 | WM9713_POLL | WM9713_COO);
+
+ /* wait 3 AC97 time slots + delay for conversion */
+ poll_delay(delay);
+ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ /* wait for POLL to go low */
+ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL)
+ && timeout) {
+ udelay(AC97_LINK_FRAME);
+ timeout--;
+ }
+
+ if (timeout <= 0) {
+ /* If PDEN is set, we can get a timeout when pen goes up */
+ if (is_pden(wm))
+ wm->pen_probably_down = 0;
+ else
+ dev_dbg(wm->dev, "adc sample timeout");
+ return RC_PENUP;
+ }
+
+ /* read back data */
+ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (pil)
+ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ else
+ data->p = DEFAULT_PRESSURE;
+
+ if (wm->mach_ops && wm->mach_ops->post_sample)
+ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+ /* check we have correct sample */
+ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
+ goto err;
+ if (pil && !(data->p & WM97XX_ADCSEL_PRES))
+ goto err;
+
+ if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) {
+ wm->pen_probably_down = 0;
+ return RC_PENUP;
+ }
+ return RC_VALID;
+err:
+ return 0;
+}
+
+/*
+ * Sample the WM9713 touchscreen in polling mode
+ */
+static int wm9713_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
+{
+ int rc;
+
+ if (coord) {
+ rc = wm9713_poll_coord(wm, data);
+ if (rc != RC_VALID)
+ return rc;
+ } else {
+ rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);
+ if (rc != RC_VALID)
+ return rc;
+ rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);
+ if (rc != RC_VALID)
+ return rc;
+ if (pil) {
+ rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN,
+ &data->p);
+ if (rc != RC_VALID)
+ return rc;
+ } else
+ data->p = DEFAULT_PRESSURE;
+ }
+ return RC_VALID;
+}
+
+/*
+ * Enable WM9713 continuous mode, i.e. touch data is streamed across
+ * an AC97 slot
+ */
+static int wm9713_acc_enable(struct wm97xx *wm, int enable)
+{
+ u16 dig1, dig2, dig3;
+ int ret = 0;
+
+ dig1 = wm->dig[0];
+ dig2 = wm->dig[1];
+ dig3 = wm->dig[2];
+
+ if (enable) {
+ /* continuous mode */
+ if (wm->mach_ops->acc_startup &&
+ (ret = wm->mach_ops->acc_startup(wm)) < 0)
+ return ret;
+
+ dig1 &= ~WM9713_ADCSEL_MASK;
+ dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X |
+ WM9713_ADCSEL_Y;
+ if (pil)
+ dig1 |= WM9713_ADCSEL_PRES;
+ dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK |
+ WM97XX_CM_RATE_MASK);
+ dig2 |= WM97XX_SLEN | WM97XX_DELAY(delay) |
+ WM97XX_SLT(wm->acc_slot) | WM97XX_RATE(wm->acc_rate);
+ dig3 |= WM9713_PDEN;
+ } else {
+ dig1 &= ~(WM9713_CTC | WM9713_COO);
+ dig2 &= ~WM97XX_SLEN;
+ dig3 &= ~WM9713_PDEN;
+ if (wm->mach_ops->acc_shutdown)
+ wm->mach_ops->acc_shutdown(wm);
+ }
+
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
+
+ return ret;
+}
+
+struct wm97xx_codec_drv wm9713_codec = {
+ .id = WM9713_ID2,
+ .name = "wm9713",
+ .poll_sample = wm9713_poll_sample,
+ .poll_touch = wm9713_poll_touch,
+ .acc_enable = wm9713_acc_enable,
+ .phy_init = wm9713_phy_init,
+ .dig_enable = wm9713_dig_enable,
+ .dig_restore = wm9713_dig_restore,
+ .aux_prepare = wm9713_aux_prepare,
+};
+EXPORT_SYMBOL_GPL(wm9713_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("WM9713 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
new file mode 100644
index 000000000..b1ae77995
--- /dev/null
+++ b/drivers/input/touchscreen/wm97xx-core.c
@@ -0,0 +1,859 @@
+/*
+ * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712
+ * and WM9713 AC97 Codecs.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ * Andrew Zabolotny <zap@homelink.ru>
+ * Russell King <rmk@arm.linux.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Notes:
+ *
+ * Features:
+ * - supports WM9705, WM9712, WM9713
+ * - polling mode
+ * - continuous mode (arch-dependent)
+ * - adjustable rpu/dpp settings
+ * - adjustable pressure current
+ * - adjustable sample settle delay
+ * - 4 and 5 wire touchscreens (5 wire is WM9712 only)
+ * - pen down detection
+ * - battery monitor
+ * - sample AUX adcs
+ * - power management
+ * - codec GPIO
+ * - codec event notification
+ * Todo
+ * - Support for async sampling control for noisy LCDs.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/pm.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/wm97xx.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#define TS_NAME "wm97xx"
+#define WM_CORE_VERSION "1.00"
+#define DEFAULT_PRESSURE 0xb0c0
+
+
+/*
+ * Touchscreen absolute values
+ *
+ * These parameters are used to help the input layer discard out of
+ * range readings and reduce jitter etc.
+ *
+ * o min, max:- indicate the min and max values your touch screen returns
+ * o fuzz:- use a higher number to reduce jitter
+ *
+ * The default values correspond to Mainstone II in QVGA mode
+ *
+ * Please read
+ * Documentation/input/input-programming.txt for more details.
+ */
+
+static int abs_x[3] = {150, 4000, 5};
+module_param_array(abs_x, int, NULL, 0);
+MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz");
+
+static int abs_y[3] = {200, 4000, 40};
+module_param_array(abs_y, int, NULL, 0);
+MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz");
+
+static int abs_p[3] = {0, 150, 4};
+module_param_array(abs_p, int, NULL, 0);
+MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz");
+
+/*
+ * wm97xx IO access, all IO locking done by AC97 layer
+ */
+int wm97xx_reg_read(struct wm97xx *wm, u16 reg)
+{
+ if (wm->ac97)
+ return wm->ac97->bus->ops->read(wm->ac97, reg);
+ else
+ return -1;
+}
+EXPORT_SYMBOL_GPL(wm97xx_reg_read);
+
+void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val)
+{
+ /* cache digitiser registers */
+ if (reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3)
+ wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val;
+
+ /* cache gpio regs */
+ if (reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE)
+ wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val;
+
+ /* wm9713 irq reg */
+ if (reg == 0x5a)
+ wm->misc = val;
+
+ if (wm->ac97)
+ wm->ac97->bus->ops->write(wm->ac97, reg, val);
+}
+EXPORT_SYMBOL_GPL(wm97xx_reg_write);
+
+/**
+ * wm97xx_read_aux_adc - Read the aux adc.
+ * @wm: wm97xx device.
+ * @adcsel: codec ADC to be read
+ *
+ * Reads the selected AUX ADC.
+ */
+
+int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
+{
+ int power_adc = 0, auxval;
+ u16 power = 0;
+ int rc = 0;
+ int timeout = 0;
+
+ /* get codec */
+ mutex_lock(&wm->codec_mutex);
+
+ /* When the touchscreen is not in use, we may have to power up
+ * the AUX ADC before we can use sample the AUX inputs->
+ */
+ if (wm->id == WM9713_ID2 &&
+ (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) {
+ power_adc = 1;
+ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff);
+ }
+
+ /* Prepare the codec for AUX reading */
+ wm->codec->aux_prepare(wm);
+
+ /* Turn polling mode on to read AUX ADC */
+ wm->pen_probably_down = 1;
+
+ while (rc != RC_VALID && timeout++ < 5)
+ rc = wm->codec->poll_sample(wm, adcsel, &auxval);
+
+ if (power_adc)
+ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000);
+
+ wm->codec->dig_restore(wm);
+
+ wm->pen_probably_down = 0;
+
+ if (timeout >= 5) {
+ dev_err(wm->dev,
+ "timeout reading auxadc %d, disabling digitiser\n",
+ adcsel);
+ wm->codec->dig_enable(wm, false);
+ }
+
+ mutex_unlock(&wm->codec_mutex);
+ return (rc == RC_VALID ? auxval & 0xfff : -EBUSY);
+}
+EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc);
+
+/**
+ * wm97xx_get_gpio - Get the status of a codec GPIO.
+ * @wm: wm97xx device.
+ * @gpio: gpio
+ *
+ * Get the status of a codec GPIO pin
+ */
+
+enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio)
+{
+ u16 status;
+ enum wm97xx_gpio_status ret;
+
+ mutex_lock(&wm->codec_mutex);
+ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+
+ if (status & gpio)
+ ret = WM97XX_GPIO_HIGH;
+ else
+ ret = WM97XX_GPIO_LOW;
+
+ mutex_unlock(&wm->codec_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm97xx_get_gpio);
+
+/**
+ * wm97xx_set_gpio - Set the status of a codec GPIO.
+ * @wm: wm97xx device.
+ * @gpio: gpio
+ *
+ *
+ * Set the status of a codec GPIO pin
+ */
+
+void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
+ enum wm97xx_gpio_status status)
+{
+ u16 reg;
+
+ mutex_lock(&wm->codec_mutex);
+ reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+
+ if (status == WM97XX_GPIO_HIGH)
+ reg |= gpio;
+ else
+ reg &= ~gpio;
+
+ if (wm->id == WM9712_ID2 && wm->variant != WM97xx_WM1613)
+ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1);
+ else
+ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg);
+ mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_set_gpio);
+
+/*
+ * Codec GPIO pin configuration, this sets pin direction, polarity,
+ * stickyness and wake up.
+ */
+void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir,
+ enum wm97xx_gpio_pol pol, enum wm97xx_gpio_sticky sticky,
+ enum wm97xx_gpio_wake wake)
+{
+ u16 reg;
+
+ mutex_lock(&wm->codec_mutex);
+ reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+
+ if (pol == WM97XX_GPIO_POL_HIGH)
+ reg |= gpio;
+ else
+ reg &= ~gpio;
+
+ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg);
+ reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
+
+ if (sticky == WM97XX_GPIO_STICKY)
+ reg |= gpio;
+ else
+ reg &= ~gpio;
+
+ wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg);
+ reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
+
+ if (wake == WM97XX_GPIO_WAKE)
+ reg |= gpio;
+ else
+ reg &= ~gpio;
+
+ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg);
+ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+
+ if (dir == WM97XX_GPIO_IN)
+ reg |= gpio;
+ else
+ reg &= ~gpio;
+
+ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg);
+ mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_config_gpio);
+
+/*
+ * Configure the WM97XX_PRP value to use while system is suspended.
+ * If a value other than 0 is set then WM97xx pen detection will be
+ * left enabled in the configured mode while the system is in suspend,
+ * the device has users and suspend has not been disabled via the
+ * wakeup sysfs entries.
+ *
+ * @wm: WM97xx device to configure
+ * @mode: WM97XX_PRP value to configure while suspended
+ */
+void wm97xx_set_suspend_mode(struct wm97xx *wm, u16 mode)
+{
+ wm->suspend_mode = mode;
+ device_init_wakeup(&wm->input_dev->dev, mode != 0);
+}
+EXPORT_SYMBOL_GPL(wm97xx_set_suspend_mode);
+
+/*
+ * Handle a pen down interrupt.
+ */
+static void wm97xx_pen_irq_worker(struct work_struct *work)
+{
+ struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work);
+ int pen_was_down = wm->pen_is_down;
+
+ /* do we need to enable the touch panel reader */
+ if (wm->id == WM9705_ID2) {
+ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) &
+ WM97XX_PEN_DOWN)
+ wm->pen_is_down = 1;
+ else
+ wm->pen_is_down = 0;
+ } else {
+ u16 status, pol;
+ mutex_lock(&wm->codec_mutex);
+ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+ pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+
+ if (WM97XX_GPIO_13 & pol & status) {
+ wm->pen_is_down = 1;
+ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol &
+ ~WM97XX_GPIO_13);
+ } else {
+ wm->pen_is_down = 0;
+ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol |
+ WM97XX_GPIO_13);
+ }
+
+ if (wm->id == WM9712_ID2 && wm->variant != WM97xx_WM1613)
+ wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status &
+ ~WM97XX_GPIO_13) << 1);
+ else
+ wm97xx_reg_write(wm, AC97_GPIO_STATUS, status &
+ ~WM97XX_GPIO_13);
+ mutex_unlock(&wm->codec_mutex);
+ }
+
+ /* If the system is not using continuous mode or it provides a
+ * pen down operation then we need to schedule polls while the
+ * pen is down. Otherwise the machine driver is responsible
+ * for scheduling reads.
+ */
+ if (!wm->mach_ops->acc_enabled || wm->mach_ops->acc_pen_down) {
+ if (wm->pen_is_down && !pen_was_down) {
+ /* Data is not available immediately on pen down */
+ queue_delayed_work(wm->ts_workq, &wm->ts_reader, 1);
+ }
+
+ /* Let ts_reader report the pen up for debounce. */
+ if (!wm->pen_is_down && pen_was_down)
+ wm->pen_is_down = 1;
+ }
+
+ if (!wm->pen_is_down && wm->mach_ops->acc_enabled)
+ wm->mach_ops->acc_pen_up(wm);
+
+ wm->mach_ops->irq_enable(wm, 1);
+}
+
+/*
+ * Codec PENDOWN irq handler
+ *
+ * We have to disable the codec interrupt in the handler because it
+ * can take up to 1ms to clear the interrupt source. We schedule a task
+ * in a work queue to do the actual interaction with the chip. The
+ * interrupt is then enabled again in the slow handler when the source
+ * has been cleared.
+ */
+static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id)
+{
+ struct wm97xx *wm = dev_id;
+
+ if (!work_pending(&wm->pen_event_work)) {
+ wm->mach_ops->irq_enable(wm, 0);
+ queue_work(wm->ts_workq, &wm->pen_event_work);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * initialise pen IRQ handler and workqueue
+ */
+static int wm97xx_init_pen_irq(struct wm97xx *wm)
+{
+ u16 reg;
+
+ /* If an interrupt is supplied an IRQ enable operation must also be
+ * provided. */
+ BUG_ON(!wm->mach_ops->irq_enable);
+
+ if (request_irq(wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED,
+ "wm97xx-pen", wm)) {
+ dev_err(wm->dev,
+ "Failed to register pen down interrupt, polling");
+ wm->pen_irq = 0;
+ return -EINVAL;
+ }
+
+ /* Configure GPIO as interrupt source on WM971x */
+ if (wm->id != WM9705_ID2) {
+ BUG_ON(!wm->mach_ops->irq_gpio);
+ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+ wm97xx_reg_write(wm, AC97_MISC_AFE,
+ reg & ~(wm->mach_ops->irq_gpio));
+ reg = wm97xx_reg_read(wm, 0x5a);
+ wm97xx_reg_write(wm, 0x5a, reg & ~0x0001);
+ }
+
+ return 0;
+}
+
+static int wm97xx_read_samples(struct wm97xx *wm)
+{
+ struct wm97xx_data data;
+ int rc;
+
+ mutex_lock(&wm->codec_mutex);
+
+ if (wm->mach_ops && wm->mach_ops->acc_enabled)
+ rc = wm->mach_ops->acc_pen_down(wm);
+ else
+ rc = wm->codec->poll_touch(wm, &data);
+
+ if (rc & RC_PENUP) {
+ if (wm->pen_is_down) {
+ wm->pen_is_down = 0;
+ dev_dbg(wm->dev, "pen up\n");
+ input_report_abs(wm->input_dev, ABS_PRESSURE, 0);
+ input_report_key(wm->input_dev, BTN_TOUCH, 0);
+ input_sync(wm->input_dev);
+ } else if (!(rc & RC_AGAIN)) {
+ /* We need high frequency updates only while
+ * pen is down, the user never will be able to
+ * touch screen faster than a few times per
+ * second... On the other hand, when the user
+ * is actively working with the touchscreen we
+ * don't want to lose the quick response. So we
+ * will slowly increase sleep time after the
+ * pen is up and quicky restore it to ~one task
+ * switch when pen is down again.
+ */
+ if (wm->ts_reader_interval < HZ / 10)
+ wm->ts_reader_interval++;
+ }
+
+ } else if (rc & RC_VALID) {
+ dev_dbg(wm->dev,
+ "pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n",
+ data.x >> 12, data.x & 0xfff, data.y >> 12,
+ data.y & 0xfff, data.p >> 12, data.p & 0xfff);
+
+ if (abs_x[0] > (data.x & 0xfff) ||
+ abs_x[1] < (data.x & 0xfff) ||
+ abs_y[0] > (data.y & 0xfff) ||
+ abs_y[1] < (data.y & 0xfff)) {
+ dev_dbg(wm->dev, "Measurement out of range, dropping it\n");
+ rc = RC_AGAIN;
+ goto out;
+ }
+
+ input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff);
+ input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff);
+ input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff);
+ input_report_key(wm->input_dev, BTN_TOUCH, 1);
+ input_sync(wm->input_dev);
+ wm->pen_is_down = 1;
+ wm->ts_reader_interval = wm->ts_reader_min_interval;
+ } else if (rc & RC_PENDOWN) {
+ dev_dbg(wm->dev, "pen down\n");
+ wm->pen_is_down = 1;
+ wm->ts_reader_interval = wm->ts_reader_min_interval;
+ }
+
+out:
+ mutex_unlock(&wm->codec_mutex);
+ return rc;
+}
+
+/*
+* The touchscreen sample reader.
+*/
+static void wm97xx_ts_reader(struct work_struct *work)
+{
+ int rc;
+ struct wm97xx *wm = container_of(work, struct wm97xx, ts_reader.work);
+
+ BUG_ON(!wm->codec);
+
+ do {
+ rc = wm97xx_read_samples(wm);
+ } while (rc & RC_AGAIN);
+
+ if (wm->pen_is_down || !wm->pen_irq)
+ queue_delayed_work(wm->ts_workq, &wm->ts_reader,
+ wm->ts_reader_interval);
+}
+
+/**
+ * wm97xx_ts_input_open - Open the touch screen input device.
+ * @idev: Input device to be opened.
+ *
+ * Called by the input sub system to open a wm97xx touchscreen device.
+ * Starts the touchscreen thread and touch digitiser.
+ */
+static int wm97xx_ts_input_open(struct input_dev *idev)
+{
+ struct wm97xx *wm = input_get_drvdata(idev);
+
+ wm->ts_workq = create_singlethread_workqueue("kwm97xx");
+ if (wm->ts_workq == NULL) {
+ dev_err(wm->dev,
+ "Failed to create workqueue\n");
+ return -EINVAL;
+ }
+
+ /* start digitiser */
+ if (wm->mach_ops && wm->mach_ops->acc_enabled)
+ wm->codec->acc_enable(wm, 1);
+ wm->codec->dig_enable(wm, 1);
+
+ INIT_DELAYED_WORK(&wm->ts_reader, wm97xx_ts_reader);
+ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker);
+
+ wm->ts_reader_min_interval = HZ >= 100 ? HZ / 100 : 1;
+ if (wm->ts_reader_min_interval < 1)
+ wm->ts_reader_min_interval = 1;
+ wm->ts_reader_interval = wm->ts_reader_min_interval;
+
+ wm->pen_is_down = 0;
+ if (wm->pen_irq)
+ wm97xx_init_pen_irq(wm);
+ else
+ dev_err(wm->dev, "No IRQ specified\n");
+
+ /* If we either don't have an interrupt for pen down events or
+ * failed to acquire it then we need to poll.
+ */
+ if (wm->pen_irq == 0)
+ queue_delayed_work(wm->ts_workq, &wm->ts_reader,
+ wm->ts_reader_interval);
+
+ return 0;
+}
+
+/**
+ * wm97xx_ts_input_close - Close the touch screen input device.
+ * @idev: Input device to be closed.
+ *
+ * Called by the input sub system to close a wm97xx touchscreen
+ * device. Kills the touchscreen thread and stops the touch
+ * digitiser.
+ */
+
+static void wm97xx_ts_input_close(struct input_dev *idev)
+{
+ struct wm97xx *wm = input_get_drvdata(idev);
+ u16 reg;
+
+ if (wm->pen_irq) {
+ /* Return the interrupt to GPIO usage (disabling it) */
+ if (wm->id != WM9705_ID2) {
+ BUG_ON(!wm->mach_ops->irq_gpio);
+ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+ wm97xx_reg_write(wm, AC97_MISC_AFE,
+ reg | wm->mach_ops->irq_gpio);
+ }
+
+ free_irq(wm->pen_irq, wm);
+ }
+
+ wm->pen_is_down = 0;
+
+ /* Balance out interrupt disables/enables */
+ if (cancel_work_sync(&wm->pen_event_work))
+ wm->mach_ops->irq_enable(wm, 1);
+
+ /* ts_reader rearms itself so we need to explicitly stop it
+ * before we destroy the workqueue.
+ */
+ cancel_delayed_work_sync(&wm->ts_reader);
+
+ destroy_workqueue(wm->ts_workq);
+
+ /* stop digitiser */
+ wm->codec->dig_enable(wm, 0);
+ if (wm->mach_ops && wm->mach_ops->acc_enabled)
+ wm->codec->acc_enable(wm, 0);
+}
+
+static int wm97xx_probe(struct device *dev)
+{
+ struct wm97xx *wm;
+ struct wm97xx_pdata *pdata = dev_get_platdata(dev);
+ int ret = 0, id = 0;
+
+ wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL);
+ if (!wm)
+ return -ENOMEM;
+ mutex_init(&wm->codec_mutex);
+
+ wm->dev = dev;
+ dev_set_drvdata(dev, wm);
+ wm->ac97 = to_ac97_t(dev);
+
+ /* check that we have a supported codec */
+ id = wm97xx_reg_read(wm, AC97_VENDOR_ID1);
+ if (id != WM97XX_ID1) {
+ dev_err(dev, "Device with vendor %04x is not a wm97xx\n", id);
+ ret = -ENODEV;
+ goto alloc_err;
+ }
+
+ wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2);
+
+ wm->variant = WM97xx_GENERIC;
+
+ dev_info(wm->dev, "detected a wm97%02x codec\n", wm->id & 0xff);
+
+ switch (wm->id & 0xff) {
+#ifdef CONFIG_TOUCHSCREEN_WM9705
+ case 0x05:
+ wm->codec = &wm9705_codec;
+ break;
+#endif
+#ifdef CONFIG_TOUCHSCREEN_WM9712
+ case 0x12:
+ wm->codec = &wm9712_codec;
+ break;
+#endif
+#ifdef CONFIG_TOUCHSCREEN_WM9713
+ case 0x13:
+ wm->codec = &wm9713_codec;
+ break;
+#endif
+ default:
+ dev_err(wm->dev, "Support for wm97%02x not compiled in.\n",
+ wm->id & 0xff);
+ ret = -ENODEV;
+ goto alloc_err;
+ }
+
+ /* set up physical characteristics */
+ wm->codec->phy_init(wm);
+
+ /* load gpio cache */
+ wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+ wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+ wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
+ wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
+ wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+ wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE);
+
+ wm->input_dev = input_allocate_device();
+ if (wm->input_dev == NULL) {
+ ret = -ENOMEM;
+ goto alloc_err;
+ }
+
+ /* set up touch configuration */
+ wm->input_dev->name = "wm97xx touchscreen";
+ wm->input_dev->phys = "wm97xx";
+ wm->input_dev->open = wm97xx_ts_input_open;
+ wm->input_dev->close = wm97xx_ts_input_close;
+
+ __set_bit(EV_ABS, wm->input_dev->evbit);
+ __set_bit(EV_KEY, wm->input_dev->evbit);
+ __set_bit(BTN_TOUCH, wm->input_dev->keybit);
+
+ input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1],
+ abs_x[2], 0);
+ input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1],
+ abs_y[2], 0);
+ input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1],
+ abs_p[2], 0);
+
+ input_set_drvdata(wm->input_dev, wm);
+ wm->input_dev->dev.parent = dev;
+
+ ret = input_register_device(wm->input_dev);
+ if (ret < 0)
+ goto dev_alloc_err;
+
+ /* register our battery device */
+ wm->battery_dev = platform_device_alloc("wm97xx-battery", -1);
+ if (!wm->battery_dev) {
+ ret = -ENOMEM;
+ goto batt_err;
+ }
+ platform_set_drvdata(wm->battery_dev, wm);
+ wm->battery_dev->dev.parent = dev;
+ wm->battery_dev->dev.platform_data = pdata;
+ ret = platform_device_add(wm->battery_dev);
+ if (ret < 0)
+ goto batt_reg_err;
+
+ /* register our extended touch device (for machine specific
+ * extensions) */
+ wm->touch_dev = platform_device_alloc("wm97xx-touch", -1);
+ if (!wm->touch_dev) {
+ ret = -ENOMEM;
+ goto touch_err;
+ }
+ platform_set_drvdata(wm->touch_dev, wm);
+ wm->touch_dev->dev.parent = dev;
+ wm->touch_dev->dev.platform_data = pdata;
+ ret = platform_device_add(wm->touch_dev);
+ if (ret < 0)
+ goto touch_reg_err;
+
+ return ret;
+
+ touch_reg_err:
+ platform_device_put(wm->touch_dev);
+ touch_err:
+ platform_device_del(wm->battery_dev);
+ batt_reg_err:
+ platform_device_put(wm->battery_dev);
+ batt_err:
+ input_unregister_device(wm->input_dev);
+ wm->input_dev = NULL;
+ dev_alloc_err:
+ input_free_device(wm->input_dev);
+ alloc_err:
+ kfree(wm);
+
+ return ret;
+}
+
+static int wm97xx_remove(struct device *dev)
+{
+ struct wm97xx *wm = dev_get_drvdata(dev);
+
+ platform_device_unregister(wm->battery_dev);
+ platform_device_unregister(wm->touch_dev);
+ input_unregister_device(wm->input_dev);
+ kfree(wm);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm97xx_suspend(struct device *dev, pm_message_t state)
+{
+ struct wm97xx *wm = dev_get_drvdata(dev);
+ u16 reg;
+ int suspend_mode;
+
+ if (device_may_wakeup(&wm->input_dev->dev))
+ suspend_mode = wm->suspend_mode;
+ else
+ suspend_mode = 0;
+
+ if (wm->input_dev->users)
+ cancel_delayed_work_sync(&wm->ts_reader);
+
+ /* Power down the digitiser (bypassing the cache for resume) */
+ reg = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER2);
+ reg &= ~WM97XX_PRP_DET_DIG;
+ if (wm->input_dev->users)
+ reg |= suspend_mode;
+ wm->ac97->bus->ops->write(wm->ac97, AC97_WM97XX_DIGITISER2, reg);
+
+ /* WM9713 has an additional power bit - turn it off if there
+ * are no users or if suspend mode is zero. */
+ if (wm->id == WM9713_ID2 &&
+ (!wm->input_dev->users || !suspend_mode)) {
+ reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) | 0x8000;
+ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg);
+ }
+
+ return 0;
+}
+
+static int wm97xx_resume(struct device *dev)
+{
+ struct wm97xx *wm = dev_get_drvdata(dev);
+
+ /* restore digitiser and gpios */
+ if (wm->id == WM9713_ID2) {
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]);
+ wm97xx_reg_write(wm, 0x5a, wm->misc);
+ if (wm->input_dev->users) {
+ u16 reg;
+ reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff;
+ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg);
+ }
+ }
+
+ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]);
+
+ wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]);
+ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]);
+ wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]);
+ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]);
+ wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]);
+ wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]);
+
+ if (wm->input_dev->users && !wm->pen_irq) {
+ wm->ts_reader_interval = wm->ts_reader_min_interval;
+ queue_delayed_work(wm->ts_workq, &wm->ts_reader,
+ wm->ts_reader_interval);
+ }
+
+ return 0;
+}
+
+#else
+#define wm97xx_suspend NULL
+#define wm97xx_resume NULL
+#endif
+
+/*
+ * Machine specific operations
+ */
+int wm97xx_register_mach_ops(struct wm97xx *wm,
+ struct wm97xx_mach_ops *mach_ops)
+{
+ mutex_lock(&wm->codec_mutex);
+ if (wm->mach_ops) {
+ mutex_unlock(&wm->codec_mutex);
+ return -EINVAL;
+ }
+ wm->mach_ops = mach_ops;
+ mutex_unlock(&wm->codec_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops);
+
+void wm97xx_unregister_mach_ops(struct wm97xx *wm)
+{
+ mutex_lock(&wm->codec_mutex);
+ wm->mach_ops = NULL;
+ mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops);
+
+static struct device_driver wm97xx_driver = {
+ .name = "wm97xx-ts",
+ .bus = &ac97_bus_type,
+ .owner = THIS_MODULE,
+ .probe = wm97xx_probe,
+ .remove = wm97xx_remove,
+ .suspend = wm97xx_suspend,
+ .resume = wm97xx_resume,
+};
+
+static int __init wm97xx_init(void)
+{
+ return driver_register(&wm97xx_driver);
+}
+
+static void __exit wm97xx_exit(void)
+{
+ driver_unregister(&wm97xx_driver);
+}
+
+module_init(wm97xx_init);
+module_exit(wm97xx_exit);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c
new file mode 100644
index 000000000..19880c738
--- /dev/null
+++ b/drivers/input/touchscreen/zforce_ts.c
@@ -0,0 +1,934 @@
+/*
+ * Copyright (C) 2012-2013 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * based in parts on Nook zforce driver
+ *
+ * Copyright (C) 2010 Barnes & Noble, Inc.
+ * Author: Pieter Truter<ptruter@intrinsyc.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/input/mt.h>
+#include <linux/platform_data/zforce_ts.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#define WAIT_TIMEOUT msecs_to_jiffies(1000)
+
+#define FRAME_START 0xee
+#define FRAME_MAXSIZE 257
+
+/* Offsets of the different parts of the payload the controller sends */
+#define PAYLOAD_HEADER 0
+#define PAYLOAD_LENGTH 1
+#define PAYLOAD_BODY 2
+
+/* Response offsets */
+#define RESPONSE_ID 0
+#define RESPONSE_DATA 1
+
+/* Commands */
+#define COMMAND_DEACTIVATE 0x00
+#define COMMAND_INITIALIZE 0x01
+#define COMMAND_RESOLUTION 0x02
+#define COMMAND_SETCONFIG 0x03
+#define COMMAND_DATAREQUEST 0x04
+#define COMMAND_SCANFREQ 0x08
+#define COMMAND_STATUS 0X1e
+
+/*
+ * Responses the controller sends as a result of
+ * command requests
+ */
+#define RESPONSE_DEACTIVATE 0x00
+#define RESPONSE_INITIALIZE 0x01
+#define RESPONSE_RESOLUTION 0x02
+#define RESPONSE_SETCONFIG 0x03
+#define RESPONSE_SCANFREQ 0x08
+#define RESPONSE_STATUS 0X1e
+
+/*
+ * Notifications are sent by the touch controller without
+ * being requested by the driver and include for example
+ * touch indications
+ */
+#define NOTIFICATION_TOUCH 0x04
+#define NOTIFICATION_BOOTCOMPLETE 0x07
+#define NOTIFICATION_OVERRUN 0x25
+#define NOTIFICATION_PROXIMITY 0x26
+#define NOTIFICATION_INVALID_COMMAND 0xfe
+
+#define ZFORCE_REPORT_POINTS 2
+#define ZFORCE_MAX_AREA 0xff
+
+#define STATE_DOWN 0
+#define STATE_MOVE 1
+#define STATE_UP 2
+
+#define SETCONFIG_DUALTOUCH (1 << 0)
+
+struct zforce_point {
+ int coord_x;
+ int coord_y;
+ int state;
+ int id;
+ int area_major;
+ int area_minor;
+ int orientation;
+ int pressure;
+ int prblty;
+};
+
+/*
+ * @client the i2c_client
+ * @input the input device
+ * @suspending in the process of going to suspend (don't emit wakeup
+ * events for commands executed to suspend the device)
+ * @suspended device suspended
+ * @access_mutex serialize i2c-access, to keep multipart reads together
+ * @command_done completion to wait for the command result
+ * @command_mutex serialize commands sent to the ic
+ * @command_waiting the id of the command that is currently waiting
+ * for a result
+ * @command_result returned result of the command
+ */
+struct zforce_ts {
+ struct i2c_client *client;
+ struct input_dev *input;
+ const struct zforce_ts_platdata *pdata;
+ char phys[32];
+
+ struct regulator *reg_vdd;
+
+ bool suspending;
+ bool suspended;
+ bool boot_complete;
+
+ /* Firmware version information */
+ u16 version_major;
+ u16 version_minor;
+ u16 version_build;
+ u16 version_rev;
+
+ struct mutex access_mutex;
+
+ struct completion command_done;
+ struct mutex command_mutex;
+ int command_waiting;
+ int command_result;
+};
+
+static int zforce_command(struct zforce_ts *ts, u8 cmd)
+{
+ struct i2c_client *client = ts->client;
+ char buf[3];
+ int ret;
+
+ dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd);
+
+ buf[0] = FRAME_START;
+ buf[1] = 1; /* data size, command only */
+ buf[2] = cmd;
+
+ mutex_lock(&ts->access_mutex);
+ ret = i2c_master_send(client, &buf[0], ARRAY_SIZE(buf));
+ mutex_unlock(&ts->access_mutex);
+ if (ret < 0) {
+ dev_err(&client->dev, "i2c send data request error: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int zforce_send_wait(struct zforce_ts *ts, const char *buf, int len)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ ret = mutex_trylock(&ts->command_mutex);
+ if (!ret) {
+ dev_err(&client->dev, "already waiting for a command\n");
+ return -EBUSY;
+ }
+
+ dev_dbg(&client->dev, "sending %d bytes for command 0x%x\n",
+ buf[1], buf[2]);
+
+ ts->command_waiting = buf[2];
+
+ mutex_lock(&ts->access_mutex);
+ ret = i2c_master_send(client, buf, len);
+ mutex_unlock(&ts->access_mutex);
+ if (ret < 0) {
+ dev_err(&client->dev, "i2c send data request error: %d\n", ret);
+ goto unlock;
+ }
+
+ dev_dbg(&client->dev, "waiting for result for command 0x%x\n", buf[2]);
+
+ if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0) {
+ ret = -ETIME;
+ goto unlock;
+ }
+
+ ret = ts->command_result;
+
+unlock:
+ mutex_unlock(&ts->command_mutex);
+ return ret;
+}
+
+static int zforce_command_wait(struct zforce_ts *ts, u8 cmd)
+{
+ struct i2c_client *client = ts->client;
+ char buf[3];
+ int ret;
+
+ dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd);
+
+ buf[0] = FRAME_START;
+ buf[1] = 1; /* data size, command only */
+ buf[2] = cmd;
+
+ ret = zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+ if (ret < 0) {
+ dev_err(&client->dev, "i2c send data request error: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int zforce_resolution(struct zforce_ts *ts, u16 x, u16 y)
+{
+ struct i2c_client *client = ts->client;
+ char buf[7] = { FRAME_START, 5, COMMAND_RESOLUTION,
+ (x & 0xff), ((x >> 8) & 0xff),
+ (y & 0xff), ((y >> 8) & 0xff) };
+
+ dev_dbg(&client->dev, "set resolution to (%d,%d)\n", x, y);
+
+ return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+}
+
+static int zforce_scan_frequency(struct zforce_ts *ts, u16 idle, u16 finger,
+ u16 stylus)
+{
+ struct i2c_client *client = ts->client;
+ char buf[9] = { FRAME_START, 7, COMMAND_SCANFREQ,
+ (idle & 0xff), ((idle >> 8) & 0xff),
+ (finger & 0xff), ((finger >> 8) & 0xff),
+ (stylus & 0xff), ((stylus >> 8) & 0xff) };
+
+ dev_dbg(&client->dev,
+ "set scan frequency to (idle: %d, finger: %d, stylus: %d)\n",
+ idle, finger, stylus);
+
+ return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+}
+
+static int zforce_setconfig(struct zforce_ts *ts, char b1)
+{
+ struct i2c_client *client = ts->client;
+ char buf[7] = { FRAME_START, 5, COMMAND_SETCONFIG,
+ b1, 0, 0, 0 };
+
+ dev_dbg(&client->dev, "set config to (%d)\n", b1);
+
+ return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+}
+
+static int zforce_start(struct zforce_ts *ts)
+{
+ struct i2c_client *client = ts->client;
+ const struct zforce_ts_platdata *pdata = ts->pdata;
+ int ret;
+
+ dev_dbg(&client->dev, "starting device\n");
+
+ ret = zforce_command_wait(ts, COMMAND_INITIALIZE);
+ if (ret) {
+ dev_err(&client->dev, "Unable to initialize, %d\n", ret);
+ return ret;
+ }
+
+ ret = zforce_resolution(ts, pdata->x_max, pdata->y_max);
+ if (ret) {
+ dev_err(&client->dev, "Unable to set resolution, %d\n", ret);
+ goto error;
+ }
+
+ ret = zforce_scan_frequency(ts, 10, 50, 50);
+ if (ret) {
+ dev_err(&client->dev, "Unable to set scan frequency, %d\n",
+ ret);
+ goto error;
+ }
+
+ ret = zforce_setconfig(ts, SETCONFIG_DUALTOUCH);
+ if (ret) {
+ dev_err(&client->dev, "Unable to set config\n");
+ goto error;
+ }
+
+ /* start sending touch events */
+ ret = zforce_command(ts, COMMAND_DATAREQUEST);
+ if (ret) {
+ dev_err(&client->dev, "Unable to request data\n");
+ goto error;
+ }
+
+ /*
+ * Per NN, initial cal. take max. of 200msec.
+ * Allow time to complete this calibration
+ */
+ msleep(200);
+
+ return 0;
+
+error:
+ zforce_command_wait(ts, COMMAND_DEACTIVATE);
+ return ret;
+}
+
+static int zforce_stop(struct zforce_ts *ts)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ dev_dbg(&client->dev, "stopping device\n");
+
+ /* Deactivates touch sensing and puts the device into sleep. */
+ ret = zforce_command_wait(ts, COMMAND_DEACTIVATE);
+ if (ret != 0) {
+ dev_err(&client->dev, "could not deactivate device, %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int zforce_touch_event(struct zforce_ts *ts, u8 *payload)
+{
+ struct i2c_client *client = ts->client;
+ const struct zforce_ts_platdata *pdata = ts->pdata;
+ struct zforce_point point;
+ int count, i, num = 0;
+
+ count = payload[0];
+ if (count > ZFORCE_REPORT_POINTS) {
+ dev_warn(&client->dev,
+ "too many coordinates %d, expected max %d\n",
+ count, ZFORCE_REPORT_POINTS);
+ count = ZFORCE_REPORT_POINTS;
+ }
+
+ for (i = 0; i < count; i++) {
+ point.coord_x =
+ payload[9 * i + 2] << 8 | payload[9 * i + 1];
+ point.coord_y =
+ payload[9 * i + 4] << 8 | payload[9 * i + 3];
+
+ if (point.coord_x > pdata->x_max ||
+ point.coord_y > pdata->y_max) {
+ dev_warn(&client->dev, "coordinates (%d,%d) invalid\n",
+ point.coord_x, point.coord_y);
+ point.coord_x = point.coord_y = 0;
+ }
+
+ point.state = payload[9 * i + 5] & 0x03;
+ point.id = (payload[9 * i + 5] & 0xfc) >> 2;
+
+ /* determine touch major, minor and orientation */
+ point.area_major = max(payload[9 * i + 6],
+ payload[9 * i + 7]);
+ point.area_minor = min(payload[9 * i + 6],
+ payload[9 * i + 7]);
+ point.orientation = payload[9 * i + 6] > payload[9 * i + 7];
+
+ point.pressure = payload[9 * i + 8];
+ point.prblty = payload[9 * i + 9];
+
+ dev_dbg(&client->dev,
+ "point %d/%d: state %d, id %d, pressure %d, prblty %d, x %d, y %d, amajor %d, aminor %d, ori %d\n",
+ i, count, point.state, point.id,
+ point.pressure, point.prblty,
+ point.coord_x, point.coord_y,
+ point.area_major, point.area_minor,
+ point.orientation);
+
+ /* the zforce id starts with "1", so needs to be decreased */
+ input_mt_slot(ts->input, point.id - 1);
+
+ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER,
+ point.state != STATE_UP);
+
+ if (point.state != STATE_UP) {
+ input_report_abs(ts->input, ABS_MT_POSITION_X,
+ point.coord_x);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y,
+ point.coord_y);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+ point.area_major);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+ point.area_minor);
+ input_report_abs(ts->input, ABS_MT_ORIENTATION,
+ point.orientation);
+ num++;
+ }
+ }
+
+ input_mt_sync_frame(ts->input);
+
+ input_mt_report_finger_count(ts->input, num);
+
+ input_sync(ts->input);
+
+ return 0;
+}
+
+static int zforce_read_packet(struct zforce_ts *ts, u8 *buf)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ mutex_lock(&ts->access_mutex);
+
+ /* read 2 byte message header */
+ ret = i2c_master_recv(client, buf, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "error reading header: %d\n", ret);
+ goto unlock;
+ }
+
+ if (buf[PAYLOAD_HEADER] != FRAME_START) {
+ dev_err(&client->dev, "invalid frame start: %d\n", buf[0]);
+ ret = -EIO;
+ goto unlock;
+ }
+
+ if (buf[PAYLOAD_LENGTH] == 0) {
+ dev_err(&client->dev, "invalid payload length: %d\n",
+ buf[PAYLOAD_LENGTH]);
+ ret = -EIO;
+ goto unlock;
+ }
+
+ /* read the message */
+ ret = i2c_master_recv(client, &buf[PAYLOAD_BODY], buf[PAYLOAD_LENGTH]);
+ if (ret < 0) {
+ dev_err(&client->dev, "error reading payload: %d\n", ret);
+ goto unlock;
+ }
+
+ dev_dbg(&client->dev, "read %d bytes for response command 0x%x\n",
+ buf[PAYLOAD_LENGTH], buf[PAYLOAD_BODY]);
+
+unlock:
+ mutex_unlock(&ts->access_mutex);
+ return ret;
+}
+
+static void zforce_complete(struct zforce_ts *ts, int cmd, int result)
+{
+ struct i2c_client *client = ts->client;
+
+ if (ts->command_waiting == cmd) {
+ dev_dbg(&client->dev, "completing command 0x%x\n", cmd);
+ ts->command_result = result;
+ complete(&ts->command_done);
+ } else {
+ dev_dbg(&client->dev, "command %d not for us\n", cmd);
+ }
+}
+
+static irqreturn_t zforce_irq(int irq, void *dev_id)
+{
+ struct zforce_ts *ts = dev_id;
+ struct i2c_client *client = ts->client;
+
+ if (ts->suspended && device_may_wakeup(&client->dev))
+ pm_wakeup_event(&client->dev, 500);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
+{
+ struct zforce_ts *ts = dev_id;
+ struct i2c_client *client = ts->client;
+ const struct zforce_ts_platdata *pdata = ts->pdata;
+ int ret;
+ u8 payload_buffer[FRAME_MAXSIZE];
+ u8 *payload;
+
+ /*
+ * When still suspended, return.
+ * Due to the level-interrupt we will get re-triggered later.
+ */
+ if (ts->suspended) {
+ msleep(20);
+ return IRQ_HANDLED;
+ }
+
+ dev_dbg(&client->dev, "handling interrupt\n");
+
+ /* Don't emit wakeup events from commands run by zforce_suspend */
+ if (!ts->suspending && device_may_wakeup(&client->dev))
+ pm_stay_awake(&client->dev);
+
+ while (!gpio_get_value(pdata->gpio_int)) {
+ ret = zforce_read_packet(ts, payload_buffer);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "could not read packet, ret: %d\n", ret);
+ break;
+ }
+
+ payload = &payload_buffer[PAYLOAD_BODY];
+
+ switch (payload[RESPONSE_ID]) {
+ case NOTIFICATION_TOUCH:
+ /*
+ * Always report touch-events received while
+ * suspending, when being a wakeup source
+ */
+ if (ts->suspending && device_may_wakeup(&client->dev))
+ pm_wakeup_event(&client->dev, 500);
+ zforce_touch_event(ts, &payload[RESPONSE_DATA]);
+ break;
+
+ case NOTIFICATION_BOOTCOMPLETE:
+ ts->boot_complete = payload[RESPONSE_DATA];
+ zforce_complete(ts, payload[RESPONSE_ID], 0);
+ break;
+
+ case RESPONSE_INITIALIZE:
+ case RESPONSE_DEACTIVATE:
+ case RESPONSE_SETCONFIG:
+ case RESPONSE_RESOLUTION:
+ case RESPONSE_SCANFREQ:
+ zforce_complete(ts, payload[RESPONSE_ID],
+ payload[RESPONSE_DATA]);
+ break;
+
+ case RESPONSE_STATUS:
+ /*
+ * Version Payload Results
+ * [2:major] [2:minor] [2:build] [2:rev]
+ */
+ ts->version_major = (payload[RESPONSE_DATA + 1] << 8) |
+ payload[RESPONSE_DATA];
+ ts->version_minor = (payload[RESPONSE_DATA + 3] << 8) |
+ payload[RESPONSE_DATA + 2];
+ ts->version_build = (payload[RESPONSE_DATA + 5] << 8) |
+ payload[RESPONSE_DATA + 4];
+ ts->version_rev = (payload[RESPONSE_DATA + 7] << 8) |
+ payload[RESPONSE_DATA + 6];
+ dev_dbg(&ts->client->dev,
+ "Firmware Version %04x:%04x %04x:%04x\n",
+ ts->version_major, ts->version_minor,
+ ts->version_build, ts->version_rev);
+
+ zforce_complete(ts, payload[RESPONSE_ID], 0);
+ break;
+
+ case NOTIFICATION_INVALID_COMMAND:
+ dev_err(&ts->client->dev, "invalid command: 0x%x\n",
+ payload[RESPONSE_DATA]);
+ break;
+
+ default:
+ dev_err(&ts->client->dev,
+ "unrecognized response id: 0x%x\n",
+ payload[RESPONSE_ID]);
+ break;
+ }
+ }
+
+ if (!ts->suspending && device_may_wakeup(&client->dev))
+ pm_relax(&client->dev);
+
+ dev_dbg(&client->dev, "finished interrupt\n");
+
+ return IRQ_HANDLED;
+}
+
+static int zforce_input_open(struct input_dev *dev)
+{
+ struct zforce_ts *ts = input_get_drvdata(dev);
+ int ret;
+
+ ret = zforce_start(ts);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void zforce_input_close(struct input_dev *dev)
+{
+ struct zforce_ts *ts = input_get_drvdata(dev);
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ ret = zforce_stop(ts);
+ if (ret)
+ dev_warn(&client->dev, "stopping zforce failed\n");
+
+ return;
+}
+
+static int __maybe_unused zforce_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct zforce_ts *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+ int ret = 0;
+
+ mutex_lock(&input->mutex);
+ ts->suspending = true;
+
+ /*
+ * When configured as a wakeup source device should always wake
+ * the system, therefore start device if necessary.
+ */
+ if (device_may_wakeup(&client->dev)) {
+ dev_dbg(&client->dev, "suspend while being a wakeup source\n");
+
+ /* Need to start device, if not open, to be a wakeup source. */
+ if (!input->users) {
+ ret = zforce_start(ts);
+ if (ret)
+ goto unlock;
+ }
+
+ enable_irq_wake(client->irq);
+ } else if (input->users) {
+ dev_dbg(&client->dev,
+ "suspend without being a wakeup source\n");
+
+ ret = zforce_stop(ts);
+ if (ret)
+ goto unlock;
+
+ disable_irq(client->irq);
+ }
+
+ ts->suspended = true;
+
+unlock:
+ ts->suspending = false;
+ mutex_unlock(&input->mutex);
+
+ return ret;
+}
+
+static int __maybe_unused zforce_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct zforce_ts *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+ int ret = 0;
+
+ mutex_lock(&input->mutex);
+
+ ts->suspended = false;
+
+ if (device_may_wakeup(&client->dev)) {
+ dev_dbg(&client->dev, "resume from being a wakeup source\n");
+
+ disable_irq_wake(client->irq);
+
+ /* need to stop device if it was not open on suspend */
+ if (!input->users) {
+ ret = zforce_stop(ts);
+ if (ret)
+ goto unlock;
+ }
+ } else if (input->users) {
+ dev_dbg(&client->dev, "resume without being a wakeup source\n");
+
+ enable_irq(client->irq);
+
+ ret = zforce_start(ts);
+ if (ret < 0)
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&input->mutex);
+
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(zforce_pm_ops, zforce_suspend, zforce_resume);
+
+static void zforce_reset(void *data)
+{
+ struct zforce_ts *ts = data;
+
+ gpio_set_value(ts->pdata->gpio_rst, 0);
+
+ udelay(10);
+
+ if (!IS_ERR(ts->reg_vdd))
+ regulator_disable(ts->reg_vdd);
+}
+
+static struct zforce_ts_platdata *zforce_parse_dt(struct device *dev)
+{
+ struct zforce_ts_platdata *pdata;
+ struct device_node *np = dev->of_node;
+
+ if (!np)
+ return ERR_PTR(-ENOENT);
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "failed to allocate platform data\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ pdata->gpio_int = of_get_gpio(np, 0);
+ if (!gpio_is_valid(pdata->gpio_int)) {
+ dev_err(dev, "failed to get interrupt gpio\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata->gpio_rst = of_get_gpio(np, 1);
+ if (!gpio_is_valid(pdata->gpio_rst)) {
+ dev_err(dev, "failed to get reset gpio\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(np, "x-size", &pdata->x_max)) {
+ dev_err(dev, "failed to get x-size property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(np, "y-size", &pdata->y_max)) {
+ dev_err(dev, "failed to get y-size property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ return pdata;
+}
+
+static int zforce_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
+ struct zforce_ts *ts;
+ struct input_dev *input_dev;
+ int ret;
+
+ if (!pdata) {
+ pdata = zforce_parse_dt(&client->dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
+
+ ts = devm_kzalloc(&client->dev, sizeof(struct zforce_ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ ret = devm_gpio_request_one(&client->dev, pdata->gpio_int, GPIOF_IN,
+ "zforce_ts_int");
+ if (ret) {
+ dev_err(&client->dev, "request of gpio %d failed, %d\n",
+ pdata->gpio_int, ret);
+ return ret;
+ }
+
+ ret = devm_gpio_request_one(&client->dev, pdata->gpio_rst,
+ GPIOF_OUT_INIT_LOW, "zforce_ts_rst");
+ if (ret) {
+ dev_err(&client->dev, "request of gpio %d failed, %d\n",
+ pdata->gpio_rst, ret);
+ return ret;
+ }
+
+ ts->reg_vdd = devm_regulator_get_optional(&client->dev, "vdd");
+ if (IS_ERR(ts->reg_vdd)) {
+ ret = PTR_ERR(ts->reg_vdd);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ } else {
+ ret = regulator_enable(ts->reg_vdd);
+ if (ret)
+ return ret;
+
+ /*
+ * according to datasheet add 100us grace time after regular
+ * regulator enable delay.
+ */
+ udelay(100);
+ }
+
+ ret = devm_add_action(&client->dev, zforce_reset, ts);
+ if (ret) {
+ dev_err(&client->dev, "failed to register reset action, %d\n",
+ ret);
+
+ /* hereafter the regulator will be disabled by the action */
+ if (!IS_ERR(ts->reg_vdd))
+ regulator_disable(ts->reg_vdd);
+
+ return ret;
+ }
+
+ snprintf(ts->phys, sizeof(ts->phys),
+ "%s/input0", dev_name(&client->dev));
+
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!input_dev) {
+ dev_err(&client->dev, "could not allocate input device\n");
+ return -ENOMEM;
+ }
+
+ mutex_init(&ts->access_mutex);
+ mutex_init(&ts->command_mutex);
+
+ ts->pdata = pdata;
+ ts->client = client;
+ ts->input = input_dev;
+
+ input_dev->name = "Neonode zForce touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = BUS_I2C;
+
+ input_dev->open = zforce_input_open;
+ input_dev->close = zforce_input_close;
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_SYN, input_dev->evbit);
+ __set_bit(EV_ABS, input_dev->evbit);
+
+ /* For multi touch */
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+ pdata->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+ pdata->y_max, 0, 0);
+
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
+ ZFORCE_MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0,
+ ZFORCE_MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+ input_mt_init_slots(input_dev, ZFORCE_REPORT_POINTS, INPUT_MT_DIRECT);
+
+ input_set_drvdata(ts->input, ts);
+
+ init_completion(&ts->command_done);
+
+ /*
+ * The zforce pulls the interrupt low when it has data ready.
+ * After it is triggered the isr thread runs until all the available
+ * packets have been read and the interrupt is high again.
+ * Therefore we can trigger the interrupt anytime it is low and do
+ * not need to limit it to the interrupt edge.
+ */
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ zforce_irq, zforce_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ input_dev->name, ts);
+ if (ret) {
+ dev_err(&client->dev, "irq %d request failed\n", client->irq);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, ts);
+
+ /* let the controller boot */
+ gpio_set_value(pdata->gpio_rst, 1);
+
+ ts->command_waiting = NOTIFICATION_BOOTCOMPLETE;
+ if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0)
+ dev_warn(&client->dev, "bootcomplete timed out\n");
+
+ /* need to start device to get version information */
+ ret = zforce_command_wait(ts, COMMAND_INITIALIZE);
+ if (ret) {
+ dev_err(&client->dev, "unable to initialize, %d\n", ret);
+ return ret;
+ }
+
+ /* this gets the firmware version among other information */
+ ret = zforce_command_wait(ts, COMMAND_STATUS);
+ if (ret < 0) {
+ dev_err(&client->dev, "couldn't get status, %d\n", ret);
+ zforce_stop(ts);
+ return ret;
+ }
+
+ /* stop device and put it into sleep until it is opened */
+ ret = zforce_stop(ts);
+ if (ret < 0)
+ return ret;
+
+ device_set_wakeup_capable(&client->dev, true);
+
+ ret = input_register_device(input_dev);
+ if (ret) {
+ dev_err(&client->dev, "could not register input device, %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct i2c_device_id zforce_idtable[] = {
+ { "zforce-ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, zforce_idtable);
+
+#ifdef CONFIG_OF
+static const struct of_device_id zforce_dt_idtable[] = {
+ { .compatible = "neonode,zforce" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, zforce_dt_idtable);
+#endif
+
+static struct i2c_driver zforce_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "zforce-ts",
+ .pm = &zforce_pm_ops,
+ .of_match_table = of_match_ptr(zforce_dt_idtable),
+ },
+ .probe = zforce_probe,
+ .id_table = zforce_idtable,
+};
+
+module_i2c_driver(zforce_driver);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("zForce TouchScreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/zylonite-wm97xx.c b/drivers/input/touchscreen/zylonite-wm97xx.c
new file mode 100644
index 000000000..e2ccd683d
--- /dev/null
+++ b/drivers/input/touchscreen/zylonite-wm97xx.c
@@ -0,0 +1,231 @@
+/*
+ * zylonite-wm97xx.c -- Zylonite Continuous Touch screen driver
+ *
+ * Copyright 2004, 2007, 2008 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ * Andrew Zabolotny <zap@homelink.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Notes:
+ * This is a wm97xx extended touch driver supporting interrupt driven
+ * and continuous operation on Marvell Zylonite development systems
+ * (which have a WM9713 on board).
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/wm97xx.h>
+
+#include <mach/hardware.h>
+#include <mach/mfp.h>
+#include <mach/regs-ac97.h>
+
+struct continuous {
+ u16 id; /* codec id */
+ u8 code; /* continuous code */
+ u8 reads; /* number of coord reads per read cycle */
+ u32 speed; /* number of coords per second */
+};
+
+#define WM_READS(sp) ((sp / HZ) + 1)
+
+static const struct continuous cinfo[] = {
+ { WM9713_ID2, 0, WM_READS(94), 94 },
+ { WM9713_ID2, 1, WM_READS(120), 120 },
+ { WM9713_ID2, 2, WM_READS(154), 154 },
+ { WM9713_ID2, 3, WM_READS(188), 188 },
+};
+
+/* continuous speed index */
+static int sp_idx;
+
+/*
+ * Pen sampling frequency (Hz) in continuous mode.
+ */
+static int cont_rate = 200;
+module_param(cont_rate, int, 0);
+MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
+
+/*
+ * Pressure readback.
+ *
+ * Set to 1 to read back pen down pressure
+ */
+static int pressure;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
+
+/*
+ * AC97 touch data slot.
+ *
+ * Touch screen readback data ac97 slot
+ */
+static int ac97_touch_slot = 5;
+module_param(ac97_touch_slot, int, 0);
+MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
+
+
+/* flush AC97 slot 5 FIFO machines */
+static void wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+ int i;
+
+ msleep(1);
+
+ for (i = 0; i < 16; i++)
+ MODR;
+}
+
+static int wm97xx_acc_pen_down(struct wm97xx *wm)
+{
+ u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES;
+ int reads = 0;
+ static u16 last, tries;
+
+ /* When the AC97 queue has been drained we need to allow time
+ * to buffer up samples otherwise we end up spinning polling
+ * for samples. The controller can't have a suitably low
+ * threshold set to use the notifications it gives.
+ */
+ msleep(1);
+
+ if (tries > 5) {
+ tries = 0;
+ return RC_PENUP;
+ }
+
+ x = MODR;
+ if (x == last) {
+ tries++;
+ return RC_AGAIN;
+ }
+ last = x;
+ do {
+ if (reads)
+ x = MODR;
+ y = MODR;
+ if (pressure)
+ p = MODR;
+
+ dev_dbg(wm->dev, "Raw coordinates: x=%x, y=%x, p=%x\n",
+ x, y, p);
+
+ /* are samples valid */
+ if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X ||
+ (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y ||
+ (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES)
+ goto up;
+
+ /* coordinate is good */
+ tries = 0;
+ input_report_abs(wm->input_dev, ABS_X, x & 0xfff);
+ input_report_abs(wm->input_dev, ABS_Y, y & 0xfff);
+ input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff);
+ input_report_key(wm->input_dev, BTN_TOUCH, (p != 0));
+ input_sync(wm->input_dev);
+ reads++;
+ } while (reads < cinfo[sp_idx].reads);
+up:
+ return RC_PENDOWN | RC_AGAIN;
+}
+
+static int wm97xx_acc_startup(struct wm97xx *wm)
+{
+ int idx;
+
+ /* check we have a codec */
+ if (wm->ac97 == NULL)
+ return -ENODEV;
+
+ /* Go you big red fire engine */
+ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
+ if (wm->id != cinfo[idx].id)
+ continue;
+ sp_idx = idx;
+ if (cont_rate <= cinfo[idx].speed)
+ break;
+ }
+ wm->acc_rate = cinfo[sp_idx].code;
+ wm->acc_slot = ac97_touch_slot;
+ dev_info(wm->dev,
+ "zylonite accelerated touchscreen driver, %d samples/sec\n",
+ cinfo[sp_idx].speed);
+
+ return 0;
+}
+
+static void wm97xx_irq_enable(struct wm97xx *wm, int enable)
+{
+ if (enable)
+ enable_irq(wm->pen_irq);
+ else
+ disable_irq_nosync(wm->pen_irq);
+}
+
+static struct wm97xx_mach_ops zylonite_mach_ops = {
+ .acc_enabled = 1,
+ .acc_pen_up = wm97xx_acc_pen_up,
+ .acc_pen_down = wm97xx_acc_pen_down,
+ .acc_startup = wm97xx_acc_startup,
+ .irq_enable = wm97xx_irq_enable,
+ .irq_gpio = WM97XX_GPIO_2,
+};
+
+static int zylonite_wm97xx_probe(struct platform_device *pdev)
+{
+ struct wm97xx *wm = platform_get_drvdata(pdev);
+ int gpio_touch_irq;
+
+ if (cpu_is_pxa320())
+ gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO15);
+ else
+ gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO26);
+
+ wm->pen_irq = gpio_to_irq(gpio_touch_irq);
+ irq_set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH);
+
+ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
+ WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_STICKY,
+ WM97XX_GPIO_WAKE);
+ wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT,
+ WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_NOTSTICKY,
+ WM97XX_GPIO_NOWAKE);
+
+ return wm97xx_register_mach_ops(wm, &zylonite_mach_ops);
+}
+
+static int zylonite_wm97xx_remove(struct platform_device *pdev)
+{
+ struct wm97xx *wm = platform_get_drvdata(pdev);
+
+ wm97xx_unregister_mach_ops(wm);
+
+ return 0;
+}
+
+static struct platform_driver zylonite_wm97xx_driver = {
+ .probe = zylonite_wm97xx_probe,
+ .remove = zylonite_wm97xx_remove,
+ .driver = {
+ .name = "wm97xx-touch",
+ },
+};
+module_platform_driver(zylonite_wm97xx_driver);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("wm97xx continuous touch driver for Zylonite");
+MODULE_LICENSE("GPL");