aboutsummaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r--drivers/input/touchscreen/Kconfig37
-rw-r--r--drivers/input/touchscreen/Makefile4
-rw-r--r--drivers/input/touchscreen/ads7846.c25
-rw-r--r--drivers/input/touchscreen/atmel_tsadcc.c47
-rw-r--r--drivers/input/touchscreen/dynapro.c206
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c457
-rw-r--r--drivers/input/touchscreen/ucb1400_ts.c11
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c104
8 files changed, 855 insertions, 36 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 8cc453c85ea..32fc8ba039a 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -111,6 +111,18 @@ config TOUCHSCREEN_DA9034
Say Y here to enable the support for the touchscreen found
on Dialog Semiconductor DA9034 PMIC.
+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_EETI
tristate "EETI touchscreen panel support"
depends on I2C
@@ -133,6 +145,18 @@ config TOUCHSCREEN_FUJITSU
To compile this driver as a module, choose M here: the
module will be called fujitsu-ts.
+config TOUCHSCREEN_S3C2410
+ tristate "Samsung S3C2410 touchscreen input driver"
+ depends on ARCH_S3C2410
+ select S3C24XX_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
@@ -297,7 +321,7 @@ config TOUCHSCREEN_TOUCHWIN
config TOUCHSCREEN_ATMEL_TSADCC
tristate "Atmel Touchscreen Interface"
- depends on ARCH_AT91SAM9RL
+ depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
help
Say Y here if you have a 4-wire touchscreen connected to the
ADC Controller on your Atmel SoC (such as the AT91SAM9RL).
@@ -418,6 +442,7 @@ config TOUCHSCREEN_USB_COMPOSITE
- IdealTEK URTC1000
- GoTop Super_Q2/GogoPen/PenPower tablets
- JASTEC USB Touch Controller/DigiTech DTR-02U
+ - Zytronic controllers
Have a look at <http://linux.chapter7.ch/touchkit/> for
a usage description and the required user-space stuff.
@@ -490,6 +515,16 @@ config TOUCHSCREEN_USB_E2I
bool "e2i Touchscreen controller (e.g. from Mimo 740)"
depends on TOUCHSCREEN_USB_COMPOSITE
+config TOUCHSCREEN_USB_ZYTRONIC
+ default y
+ bool "Zytronic controller" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ETT_TC5UH
+ default y
+ bool "ET&T TC5UH touchscreen controler support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
config TOUCHSCREEN_TOUCHIT213
tristate "Sahara TouchIT-213 touchscreen"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 15fa62cffc7..f1f59c9e121 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o
+obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
@@ -25,7 +26,9 @@ obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_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_S3C2410) += s3c2410_ts.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
@@ -41,4 +44,3 @@ 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_PCAP) += pcap_ts.o
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 09c810999b9..52d2ca147d8 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -29,10 +29,9 @@
#include <linux/spi/ads7846.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).
+ * 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.
@@ -43,7 +42,7 @@
* 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...
+ * 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
@@ -566,10 +565,8 @@ static void ads7846_rx(void *ads)
* once more the measurement
*/
if (packet->tc.ignore || Rt > ts->pressure_max) {
-#ifdef VERBOSE
- pr_debug("%s: ignored %d pressure %d\n",
- dev_name(&ts->spi->dev), packet->tc.ignore, Rt);
-#endif
+ dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n",
+ packet->tc.ignore, Rt);
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
HRTIMER_MODE_REL);
return;
@@ -598,9 +595,7 @@ static void ads7846_rx(void *ads)
if (!ts->pendown) {
input_report_key(input, BTN_TOUCH, 1);
ts->pendown = 1;
-#ifdef VERBOSE
- dev_dbg(&ts->spi->dev, "DOWN\n");
-#endif
+ dev_vdbg(&ts->spi->dev, "DOWN\n");
}
if (ts->swap_xy)
@@ -608,12 +603,10 @@ static void ads7846_rx(void *ads)
input_report_abs(input, ABS_X, x);
input_report_abs(input, ABS_Y, y);
- input_report_abs(input, ABS_PRESSURE, Rt);
+ input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt);
input_sync(input);
-#ifdef VERBOSE
- dev_dbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
-#endif
+ dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
}
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
@@ -723,9 +716,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
input_sync(input);
ts->pendown = 0;
-#ifdef VERBOSE
- dev_dbg(&ts->spi->dev, "UP\n");
-#endif
+ dev_vdbg(&ts->spi->dev, "UP\n");
}
/* measurement cycle ended */
diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c
index 9c7fce4d74d..3d9b5166ebe 100644
--- a/drivers/input/touchscreen/atmel_tsadcc.c
+++ b/drivers/input/touchscreen/atmel_tsadcc.c
@@ -22,6 +22,8 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <mach/board.h>
+#include <mach/cpu.h>
/* Register definitions based on AT91SAM9RL64 preliminary draft datasheet */
@@ -36,7 +38,9 @@
#define ATMEL_TSADCC_LOWRES (1 << 4) /* Resolution selection */
#define ATMEL_TSADCC_SLEEP (1 << 5) /* Sleep mode */
#define ATMEL_TSADCC_PENDET (1 << 6) /* Pen Detect selection */
+#define ATMEL_TSADCC_PRES (1 << 7) /* Pressure Measurement Selection */
#define ATMEL_TSADCC_PRESCAL (0x3f << 8) /* Prescalar Rate Selection */
+#define ATMEL_TSADCC_EPRESCAL (0xff << 8) /* Prescalar Rate Selection (Extended) */
#define ATMEL_TSADCC_STARTUP (0x7f << 16) /* Start Up time */
#define ATMEL_TSADCC_SHTIM (0xf << 24) /* Sample & Hold time */
#define ATMEL_TSADCC_PENDBC (0xf << 28) /* Pen Detect debouncing time */
@@ -84,7 +88,13 @@
#define ATMEL_TSADCC_CDR4 0x40 /* Channel Data 4 */
#define ATMEL_TSADCC_CDR5 0x44 /* Channel Data 5 */
-#define ADC_CLOCK 1000000
+#define ATMEL_TSADCC_XPOS 0x50
+#define ATMEL_TSADCC_Z1DAT 0x54
+#define ATMEL_TSADCC_Z2DAT 0x58
+
+#define PRESCALER_VAL(x) ((x) >> 8)
+
+#define ADC_DEFAULT_CLOCK 100000
struct atmel_tsadcc {
struct input_dev *input;
@@ -172,6 +182,7 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev)
struct atmel_tsadcc *ts_dev;
struct input_dev *input_dev;
struct resource *res;
+ struct at91_tsadcc_data *pdata = pdev->dev.platform_data;
int err = 0;
unsigned int prsc;
unsigned int reg;
@@ -242,31 +253,49 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev)
input_dev->phys = ts_dev->phys;
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);
-
+ __set_bit(EV_ABS, input_dev->evbit);
input_set_abs_params(input_dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, 0x3FF, 0, 0);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+
/* clk_enable() always returns 0, no need to check it */
clk_enable(ts_dev->clk);
prsc = clk_get_rate(ts_dev->clk);
dev_info(&pdev->dev, "Master clock is set at: %d Hz\n", prsc);
- prsc = prsc / ADC_CLOCK / 2 - 1;
+ if (!pdata)
+ goto err_fail;
+
+ if (!pdata->adc_clock)
+ pdata->adc_clock = ADC_DEFAULT_CLOCK;
+
+ prsc = (prsc / (2 * pdata->adc_clock)) - 1;
+
+ /* saturate if this value is too high */
+ if (cpu_is_at91sam9rl()) {
+ if (prsc > PRESCALER_VAL(ATMEL_TSADCC_PRESCAL))
+ prsc = PRESCALER_VAL(ATMEL_TSADCC_PRESCAL);
+ } else {
+ if (prsc > PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL))
+ prsc = PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL);
+ }
+
+ dev_info(&pdev->dev, "Prescaler is set at: %d\n", prsc);
reg = ATMEL_TSADCC_TSAMOD_TS_ONLY_MODE |
((0x00 << 5) & ATMEL_TSADCC_SLEEP) | /* Normal Mode */
((0x01 << 6) & ATMEL_TSADCC_PENDET) | /* Enable Pen Detect */
- ((prsc << 8) & ATMEL_TSADCC_PRESCAL) | /* PRESCAL */
- ((0x13 << 16) & ATMEL_TSADCC_STARTUP) | /* STARTUP */
- ((0x0F << 28) & ATMEL_TSADCC_PENDBC); /* PENDBC */
+ (prsc << 8) |
+ ((0x26 << 16) & ATMEL_TSADCC_STARTUP) |
+ ((pdata->pendet_debounce << 28) & ATMEL_TSADCC_PENDBC);
atmel_tsadcc_write(ATMEL_TSADCC_CR, ATMEL_TSADCC_SWRST);
atmel_tsadcc_write(ATMEL_TSADCC_MR, reg);
atmel_tsadcc_write(ATMEL_TSADCC_TRGR, ATMEL_TSADCC_TRGMOD_NONE);
- atmel_tsadcc_write(ATMEL_TSADCC_TSR, (0x3 << 24) & ATMEL_TSADCC_TSSHTIM);
+ atmel_tsadcc_write(ATMEL_TSADCC_TSR,
+ (pdata->ts_sample_hold_time << 24) & ATMEL_TSADCC_TSSHTIM);
atmel_tsadcc_read(ATMEL_TSADCC_SR);
atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT);
diff --git a/drivers/input/touchscreen/dynapro.c b/drivers/input/touchscreen/dynapro.c
new file mode 100644
index 00000000000..455353908bd
--- /dev/null
+++ b/drivers/input/touchscreen/dynapro.c
@@ -0,0 +1,206 @@
+/*
+ * 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>
+#include <linux/init.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,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init dynapro_init(void)
+{
+ return serio_register_driver(&dynapro_drv);
+}
+
+static void __exit dynapro_exit(void)
+{
+ serio_unregister_driver(&dynapro_drv);
+}
+
+module_init(dynapro_init);
+module_exit(dynapro_exit);
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
new file mode 100644
index 00000000000..6386b441ef8
--- /dev/null
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -0,0 +1,457 @@
+/*
+ * 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/slab.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/init.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 <mach/regs-gpio.h>
+#include <mach/ts.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))
+
+/* 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.
+ */
+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;
+};
+
+static struct s3c2410ts ts;
+
+/**
+ * s3c2410_ts_connect - configure gpio for s3c2410 systems
+ *
+ * Configure the GPIO for the S3C2410 system, where we have external FETs
+ * connected to the device (later systems such as the S3C2440 integrate
+ * these into the device).
+*/
+static inline void s3c2410_ts_connect(void)
+{
+ s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPG12_XMON);
+ s3c2410_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPG13_nXPON);
+ s3c2410_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPG14_YMON);
+ s3c2410_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPG15_nYPON);
+}
+
+/**
+ * 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 (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;
+ }
+
+ if (down) {
+ s3c_adc_start(ts.client, 0, 1 << ts.shift);
+ } else {
+ 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_info(ts.dev, "%s: count=%d\n", __func__, ts.count);
+
+ 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 __devinit 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 = pdev->dev.platform_data;
+ 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_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;
+ }
+
+ /* Configure the touchscreen external FETs on the S3C2410 */
+ if (!platform_get_device_id(pdev)->driver_data)
+ s3c2410_ts_connect();
+
+ 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;
+
+ ret = request_irq(ts.irq_tc, stylus_irq, IRQF_DISABLED,
+ "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_unregister_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 __devexit s3c2410ts_remove(struct platform_device *pdev)
+{
+ free_irq(ts.irq_tc, ts.input);
+ del_timer_sync(&touch_timer);
+
+ clk_disable(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 = pdev->dev.platform_data;
+
+ clk_enable(ts.clock);
+
+ /* 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 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", 1 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, s3cts_driver_ids);
+
+static struct platform_driver s3c_ts_driver = {
+ .driver = {
+ .name = "s3c24xx-ts",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &s3c_ts_pmops,
+#endif
+ },
+ .id_table = s3cts_driver_ids,
+ .probe = s3c2410ts_probe,
+ .remove = __devexit_p(s3c2410ts_remove),
+};
+
+static int __init s3c2410ts_init(void)
+{
+ return platform_driver_register(&s3c_ts_driver);
+}
+
+static void __exit s3c2410ts_exit(void)
+{
+ platform_driver_unregister(&s3c_ts_driver);
+}
+
+module_init(s3c2410ts_init);
+module_exit(s3c2410ts_exit);
+
+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/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c
index 095f84b1f56..89dcbe7b4b0 100644
--- a/drivers/input/touchscreen/ucb1400_ts.c
+++ b/drivers/input/touchscreen/ucb1400_ts.c
@@ -355,10 +355,13 @@ static int ucb1400_ts_probe(struct platform_device *dev)
goto err;
}
- error = ucb1400_ts_detect_irq(ucb);
- if (error) {
- printk(KERN_ERR "UCB1400: IRQ probe failed\n");
- goto err_free_devs;
+ /* Only in case the IRQ line wasn't supplied, try detecting it */
+ if (ucb->irq < 0) {
+ error = ucb1400_ts_detect_irq(ucb);
+ if (error) {
+ printk(KERN_ERR "UCB1400: IRQ probe failed\n");
+ goto err_free_devs;
+ }
}
init_waitqueue_head(&ucb->ts_wait);
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index 68ece5801a5..09a5e7341bd 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -14,6 +14,7 @@
* - General Touch
* - GoTop Super_Q2/GogoPen/PenPower tablets
* - JASTEC USB touch controller/DigiTech DTR-02U
+ * - Zytronic capacitive touchscreen
*
* Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch>
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
@@ -73,6 +74,15 @@ struct usbtouch_device_info {
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);
/*
@@ -121,6 +131,8 @@ enum {
DEVTYPE_GOTOP,
DEVTYPE_JASTEC,
DEVTYPE_E2I,
+ DEVTYPE_ZYTRONIC,
+ DEVTYPE_TC5UH,
};
#define USB_DEVICE_HID_CLASS(vend, prod) \
@@ -201,6 +213,15 @@ static struct usb_device_id usbtouch_devices[] = {
#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_TC5UH
+ {USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC5UH},
+#endif
+
{}
};
@@ -538,6 +559,19 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
}
#endif
+/*****************************************************************************
+ * ET&T TC5UH part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH
+static int tc5uh_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
@@ -621,6 +655,39 @@ static int jastec_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
}
#endif
+/*****************************************************************************
+ * Zytronic Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+static int zytronic_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ switch (pkt[0]) {
+ case 0x3A: /* command response */
+ dbg("%s: Command response %d", __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;
+ dbg("%s: down %d,%d", __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;
+ dbg("%s: up %d,%d", __func__, dev->x, dev->y);
+ return 1;
+
+ default:
+ dbg("%s: Unknown return %d", __func__, pkt[0]);
+ break;
+ }
+
+ return 0;
+}
+#endif
/*****************************************************************************
* the different device descriptors
@@ -783,6 +850,29 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.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_TC5UH
+ [DEVTYPE_TC5UH] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 5,
+ .read_data = tc5uh_read_data,
+ },
+#endif
};
@@ -933,8 +1023,10 @@ static int usbtouch_open(struct input_dev *input)
usbtouch->irq->dev = usbtouch->udev;
- if (usb_submit_urb(usbtouch->irq, GFP_KERNEL))
- return -EIO;
+ if (!usbtouch->type->irq_always) {
+ if (usb_submit_urb(usbtouch->irq, GFP_KERNEL))
+ return -EIO;
+ }
return 0;
}
@@ -943,7 +1035,8 @@ static void usbtouch_close(struct input_dev *input)
{
struct usbtouch_usb *usbtouch = input_get_drvdata(input);
- usb_kill_urb(usbtouch->irq);
+ if (!usbtouch->type->irq_always)
+ usb_kill_urb(usbtouch->irq);
}
@@ -1066,6 +1159,9 @@ static int usbtouch_probe(struct usb_interface *intf,
usb_set_intfdata(intf, usbtouch);
+ if (usbtouch->type->irq_always)
+ usb_submit_urb(usbtouch->irq, GFP_KERNEL);
+
return 0;
out_free_buffers:
@@ -1087,7 +1183,7 @@ static void usbtouch_disconnect(struct usb_interface *intf)
dbg("%s - usbtouch is initialized, cleaning up", __func__);
usb_set_intfdata(intf, NULL);
- usb_kill_urb(usbtouch->irq);
+ /* this will stop IO via close */
input_unregister_device(usbtouch->input);
usb_free_urb(usbtouch->irq);
usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch);