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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux