[PATCH] input: Add support for the TSC2003 controller.

From: Thierry Reding
Date: Wed Apr 29 2009 - 08:03:49 EST


This patch implements touchscreen support for the TSC2003 controller. There is
no support for the temperature sensor yet.

Signed-off-by: Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx>

---
drivers/input/touchscreen/Kconfig | 8 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/tsc2003.c | 416 +++++++++++++++++++++++++++++++++++
include/linux/i2c/tsc2003.h | 28 +++
4 files changed, 453 insertions(+), 0 deletions(-)

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index b01fd61..87b980c 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -455,6 +455,14 @@ config TOUCHSCREEN_TOUCHIT213
To compile this driver as a module, choose M here: the
module will be called touchit213.

+config TOUCHSCREEN_TSC2003
+ tristate "Texas Instruments TSC2003 Touchscreen Controller"
+ depends on I2C
+ ---help---
+ If you say yes here you get support for the Texas Instruments
+ TSC2003 Touchscreen Controller with on-chip temperature
+ measurement.
+
config TOUCHSCREEN_TSC2007
tristate "TSC2007 based touchscreens"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6700f7b..e965422 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2003) += tsc2003.o
obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
diff --git a/drivers/input/touchscreen/tsc2003.c b/drivers/input/touchscreen/tsc2003.c
new file mode 100644
index 0000000..5acaa0d
--- /dev/null
+++ b/drivers/input/touchscreen/tsc2003.c
@@ -0,0 +1,416 @@
+/*
+ * linux/drivers/input/touchscreen/tsc2003.c
+ *
+ * Copyright (C) 2007-2008 Avionic Design Development GmbH
+ * Copyright (C) 2008-2009 Avionic Design GmbH
+ *
+ * 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.
+ *
+ * Written by Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx>
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c/tsc2003.h>
+
+#define DRIVER_NAME "tsc2003"
+#define DRIVER_VERSION "1"
+
+/* basic commands */
+#define CMD_MEASURE_TEMPERATURE_0 0x00
+#define CMD_MEASURE_BATTERY_1 0x10
+#define CMD_MEASURE_INPUT_1 0x20
+#define CMD_MEASURE_TEMPERATURE_1 0x40
+#define CMD_MEASURE_BATTERY_2 0x50
+#define CMD_MEASURE_INPUT_2 0x60
+#define CMD_ACTIVATE_X 0x80
+#define CMD_ACTIVATE_Y 0x90
+#define CMD_ACTIVATE_XY 0xA0
+#define CMD_MEASURE_X 0xC0
+#define CMD_MEASURE_Y 0xD0
+#define CMD_MEASURE_Z1 0xE0
+#define CMD_MEASURE_Z2 0xF0
+
+/* powerdown modes */
+#define PDM_POWERDOWN 0x00
+#define PDM_IR_OFF_ADC_ON 0x04
+#define PDM_IR_ON_ADC_OFF 0x08
+#define PDM_IR_ON_ADC_ON 0x0A
+#define PDM_TOUCH_IRQ_OFF 0x04
+
+/* data width modes */
+#define MODE_12BIT 0x00
+#define MODE_8BIT 0x02
+
+/* periodic polling delay and period */
+#define TS_POLL_DELAY (1 * 1000000)
+#define TS_POLL_PERIOD (5 * 1000000)
+
+/**
+ * struct ts_event - touchscreen event structure
+ * @pendown: state of the pen
+ * @x: X-coordinate of the event
+ * @y: Y-coordinate of the event
+ * @z: pressure of the event
+ */
+struct ts_event {
+ short pendown;
+ short x;
+ short y;
+ short z;
+};
+
+/**
+ * struct tsc2003 - touchscreen controller context
+ * @client: I2C client
+ * @input: touchscreen input device
+ * @lock: lock for resource protection
+ * @timer: timer for periodical polling
+ * @work: workqueue structure
+ * @pendown: current pen state
+ * @event: current touchscreen event
+ * @pdata: platform-specific information
+ */
+struct tsc2003 {
+ struct i2c_client *client;
+ struct input_dev *input;
+ spinlock_t lock;
+ struct hrtimer timer;
+ struct work_struct work;
+
+ struct ts_event event;
+ unsigned pendown:1;
+
+ struct tsc2003_platform_data *pdata;
+};
+
+/**
+ * tsc2003_get_pendown_state() - obtain the current pen state
+ * @ts: touchscreen controller context
+ */
+static int tsc2003_get_pendown_state(struct tsc2003 *ts)
+{
+ int state = 0;
+
+ if (ts && ts->pdata && ts->pdata->get_irq_level)
+ state = !ts->pdata->get_irq_level();
+
+ return state;
+}
+
+/**
+ * tsc2003_read() - send a command and read the response
+ * @client: I2C client
+ * @command: command to send
+ */
+static int tsc2003_read(struct i2c_client *client, u8 command)
+{
+ u8 value[2] = { 0, 0 };
+ int size = 2;
+ int status;
+
+ command &= ~MODE_8BIT;
+
+ status = i2c_master_send(client, &command, 1);
+ if (status < 0)
+ return status;
+
+ if (command & MODE_8BIT)
+ size = 1;
+
+ status = i2c_master_recv(client, value, size);
+ if (status < 0)
+ return status;
+
+ if (command & MODE_8BIT)
+ return value[0];
+
+ return (value[0] << 4) | (value[1] >> 4);
+}
+
+/**
+ * tsc2003_read_x() - read touch screen X-coordinate
+ * @client: I2C client
+ * @pdm: powerdown mode
+ * @samples: number of samples to average over
+ */
+static int tsc2003_read_x(struct i2c_client *client, u8 pdm, int samples)
+{
+ return tsc2003_read(client, CMD_MEASURE_X | pdm);
+}
+
+/**
+ * tsc2003_read_y() - read touch screen Y-coordinate
+ * @client: I2C client
+ * @pdm: powerdown mode
+ * @samples: number of samples to average over
+ */
+static int tsc2003_read_y(struct i2c_client *client, u8 pdm, int samples)
+{
+ return tsc2003_read(client, CMD_MEASURE_Y | pdm);
+}
+
+/**
+ * tsc2003_read_z() - read touch screen pressure
+ * @client: I2C client
+ * @pdm: powerdown mode
+ * @samples: number of samples to average over
+ */
+static int tsc2003_read_z(struct i2c_client *client, u8 pdm, int samples)
+{
+ int p1 = tsc2003_read(client, CMD_MEASURE_Z1 | pdm);
+ int p2 = tsc2003_read(client, CMD_MEASURE_Z2 | pdm);
+
+ p2 = 0;
+
+ return p1;
+}
+
+/**
+ * tsc2003_timer() - timer callback function
+ * @timer: timer that caused this function call
+ */
+static enum hrtimer_restart tsc2003_timer(struct hrtimer *timer)
+{
+ struct tsc2003 *ts = container_of(timer, struct tsc2003, timer);
+ unsigned long flags = 0;
+ int state = 0;
+
+ spin_lock_irqsave(&ts->lock, flags);
+
+ state = tsc2003_get_pendown_state(ts);
+ if (state) {
+ /* reset if this is the first pen down event */
+ if (!ts->pendown) {
+ ts->event.pendown = 0;
+ ts->pendown = 1;
+ }
+
+ schedule_work(&ts->work);
+ } else {
+ /* enable IRQ after the pen was lifted */
+ if (ts->pendown)
+ ts->pendown = 0;
+
+ input_report_key(ts->input, BTN_TOUCH, 0);
+ input_report_abs(ts->input, ABS_PRESSURE, 0);
+ input_sync(ts->input);
+
+ enable_irq(ts->client->irq);
+ }
+
+ spin_unlock_irqrestore(&ts->lock, flags);
+ return HRTIMER_NORESTART;
+}
+
+/**
+ * tsc2003_work() - work queue handler (initiated by the interrupt handler)
+ * @work: work queue to handle
+ */
+static void tsc2003_work(struct work_struct *work)
+{
+ struct tsc2003 *ts = container_of(work, struct tsc2003, work);
+
+ /* report only the first pen down event */
+ if (!ts->event.pendown) {
+ input_report_key(ts->input, BTN_TOUCH, 1);
+ ts->event.pendown = 1;
+ }
+
+ /* read X- and Y-coordinates and the pressure */
+ ts->event.x = tsc2003_read_x(ts->client, PDM_POWERDOWN, 1);
+ ts->event.y = tsc2003_read_y(ts->client, PDM_POWERDOWN, 1);
+ ts->event.z = tsc2003_read_z(ts->client, PDM_POWERDOWN, 1);
+
+ /* report X- and Y-coordinates and the pressure */
+ input_report_abs(ts->input, ABS_X, ts->event.x);
+ input_report_abs(ts->input, ABS_Y, ts->event.y);
+ input_report_abs(ts->input, ABS_PRESSURE, ts->event.z);
+ input_sync(ts->input);
+
+ /* restart the timer */
+ hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
+ HRTIMER_MODE_REL);
+}
+
+/**
+ * tsc2003_interrupt() - interrupt handler for touch events
+ * @irq: interrupt to handle
+ * @dev_id: device-specific information
+ */
+static irqreturn_t tsc2003_interrupt(int irq, void *dev_id)
+{
+ struct i2c_client *client = (struct i2c_client *)dev_id;
+ struct tsc2003 *ts = i2c_get_clientdata(client);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ts->lock, flags);
+
+ /* if the pen is down, disable IRQ and start timer chain */
+ if (tsc2003_get_pendown_state(ts)) {
+ disable_irq_nosync(client->irq);
+ hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY),
+ HRTIMER_MODE_REL);
+ }
+
+ spin_unlock_irqrestore(&ts->lock, flags);
+ return IRQ_HANDLED;
+}
+
+/**
+ * tsc2003_probe() - initialize the I2C client
+ * @client: client to initialize
+ * @id: I2C device ID
+ */
+static int tsc2003_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tsc2003 *ts;
+ int err = 0;
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (!ts) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ ts->client = client;
+
+ ts->input = input_allocate_device();
+ if (!ts->input) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ /* setup the input device */
+ ts->input->name = "Texas Instruments TSC2003 I2C Touchscreen";
+ ts->input->phys = DRIVER_NAME "/input0";
+ ts->input->id.bustype = BUS_I2C;
+ ts->input->dev.parent = &client->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);
+ input_set_abs_params(ts->input, ABS_PRESSURE, 0, 0x3ff, 0, 0);
+
+ /* setup platform-specific hooks */
+ ts->pdata = client->dev.platform_data;
+ if (!ts->pdata || !ts->pdata->init_irq || !ts->pdata->get_irq_level) {
+ dev_err(&client->dev, "no platform-specific callbacks "
+ "provided\n");
+ err = -ENXIO;
+ goto fail;
+ }
+
+ if (ts->pdata->init_irq) {
+ err = ts->pdata->init_irq();
+ if (err < 0) {
+ dev_err(&client->dev, "failed to initialize IRQ#%d: "
+ "%d\n", client->irq, err);
+ goto fail;
+ }
+ }
+
+ spin_lock_init(&ts->lock);
+ hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ts->timer.function = tsc2003_timer;
+ INIT_WORK(&ts->work, tsc2003_work);
+ ts->pendown = 0;
+
+ err = request_irq(client->irq, tsc2003_interrupt, IRQF_SHARED,
+ "TSC2003 Touch Screen", client);
+ if (err) {
+ dev_err(&client->dev, "failed to request IRQ#%d: %d\n",
+ client->irq, err);
+ goto fail;
+ }
+
+ i2c_set_clientdata(client, ts);
+
+ err = input_register_device(ts->input);
+ if (err) {
+ dev_err(&client->dev, "failed to register input device: %d\n",
+ err);
+ goto fail_irq;
+ }
+
+ /* dummy read; necessary to enable the pen IRQ */
+ (void)tsc2003_read_z(client, PDM_POWERDOWN, 1);
+ err = 0;
+ goto out;
+
+fail_irq:
+ free_irq(client->irq, client);
+
+fail:
+ if (ts) {
+ input_free_device(ts->input);
+ kfree(ts);
+ }
+
+ i2c_set_clientdata(client, NULL);
+out:
+ return err;
+}
+
+/**
+ * tsc2003_remove() - cleanup the I2C client
+ * @client: client to clean up
+ */
+static int tsc2003_remove(struct i2c_client *client)
+{
+ struct tsc2003 *priv = i2c_get_clientdata(client);
+
+ free_irq(client->irq, client);
+ i2c_set_clientdata(client, NULL);
+ input_unregister_device(priv->input);
+ kfree(priv);
+
+ return 0;
+}
+
+static const struct i2c_device_id tsc2003_ids[] = {
+ { DRIVER_NAME, 0 },
+ { }
+};
+
+/* TSC2003 I2C driver */
+static struct i2c_driver tsc2003_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = tsc2003_probe,
+ .remove = __devexit_p(tsc2003_remove),
+ .id_table = tsc2003_ids,
+};
+
+/**
+ * tsc2003_init() - module initialization
+ */
+static int __init tsc2003_init(void)
+{
+ return i2c_add_driver(&tsc2003_driver);
+}
+
+/**
+ * tsc2003_exit() - module cleanup
+ */
+static void __exit tsc2003_exit(void)
+{
+ i2c_del_driver(&tsc2003_driver);
+}
+
+module_init(tsc2003_init);
+module_exit(tsc2003_exit);
+
+MODULE_AUTHOR("Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Texas Instruments TSC2003 I2C Touch Screen driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
+
diff --git a/include/linux/i2c/tsc2003.h b/include/linux/i2c/tsc2003.h
new file mode 100644
index 0000000..45e6100
--- /dev/null
+++ b/include/linux/i2c/tsc2003.h
@@ -0,0 +1,28 @@
+/**
+ * linux/include/i2c/tsc2003.h
+ *
+ * Copyright (C) 2007-2008 Avionic Design Development GmbH
+ * Copyright (C) 2008-2009 Avionic Design GmbH
+ *
+ * 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.
+ *
+ * Written by Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx>
+ */
+
+#ifndef LINUX_I2C_TSC2003_H
+#define LINUX_I2C_TSC2003_H
+
+/**
+ * struct tsc2003_platform_data - platform-specific TSC2003 data
+ * @init_irq: initialize interrupt
+ * @get_irq_level: obtain current interrupt level
+ */
+struct tsc2003_platform_data {
+ int (*init_irq)(void);
+ int (*get_irq_level)(void);
+};
+
+#endif /* !LINUX_I2C_TSC2003_H */
+
--
tg: (fb160d7..) adx/i2c/tsc2003 (depends on: adx/master)
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/