From 8b98df915258fd39d3a710979f3cc6af308cafc2 Mon Sep 17 00:00:00 2001
From: Tuomas Kulve <tuomas@kulve.fi>
Date: Sun, 27 Apr 2008 18:37:11 +0300
Subject: [PATCH] Applied tsc2003-config.diff, added tsc2003.c.

---
 drivers/i2c/chips/Kconfig   |   14 ++
 drivers/i2c/chips/Makefile  |    1 +
 drivers/i2c/chips/tsc2003.c |  472 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 487 insertions(+), 0 deletions(-)
 create mode 100644 drivers/i2c/chips/tsc2003.c

diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index 2da2edf..bb1d7a6 100644
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -139,4 +139,18 @@ config MENELAUS
 	  and other features that are often used in portable devices like
 	  cell phones and PDAs.
 
+config SENSORS_TSC2003
+	tristate "TI TSC2003"
+	depends on I2C && EXPERIMENTAL
+	default n
+	help
+	  Driver for TI tsc2003 touchscreen and sensor chip/
+
+config SENSORS_TSC2003_SYSFS
+	tristate "TI TSC2003 sysfs interface
+	depends on SENSORS_TSC2003
+	default n
+	help
+	  Enabled the sysfs interface for tsc2003
+
 endmenu
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
index e47aca0..e7c3345 100644
--- a/drivers/i2c/chips/Makefile
+++ b/drivers/i2c/chips/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o
 obj-$(CONFIG_TPS65010)		+= tps65010.o
 obj-$(CONFIG_MENELAUS)		+= menelaus.o
 obj-$(CONFIG_SENSORS_TSL2550)	+= tsl2550.o
+obj-$(CONFIG_SENSORS_TSC2003)   += tsc2003.o
 
 ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/chips/tsc2003.c b/drivers/i2c/chips/tsc2003.c
new file mode 100644
index 0000000..a4a68b7
--- /dev/null
+++ b/drivers/i2c/chips/tsc2003.c
@@ -0,0 +1,472 @@
+/*
+ *  linux/drivers/i2c/chips/tsc2003.c
+ *
+ *  Copyright (C) 2005 Bill Gatliff <bgat at billgatliff.com>
+ *  Changes for 2.6.20 kernel by Nicholas Chen <nchen at cs.umd.edu>
+ *  Changes for 2.6.21 kernel by Chris Dollar <chris.dollar at 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.
+ *
+ *  Driver for TI's TSC2003 I2C Touch Screen Controller
+ */
+
+//#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/string.h>
+#include <linux/bcd.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <asm-arm/arch-pxa/irqs.h>
+
+#define DRIVER_NAME "tsc2003"
+#define TSC2003_CMD(cn,pdn,m) (((cn) << 4) | ((pdn) << 2) | ((m) << 1))
+#define ADC_MAX ((1 << 12) - 1)
+
+//#define CONFIG_I2C_DEBUG_CHIP 1
+
+static unsigned short normal_i2c[] = { 0x48, I2C_CLIENT_END };
+
+I2C_CLIENT_INSMOD_1(tsc2003);
+
+enum tsc2003_pd {
+	PD_POWERDOWN = 0, /* penirq */
+	PD_IREFOFF_ADCON = 1, /* no penirq */
+	PD_IREFON_ADCOFF = 2, /* penirq */
+	PD_IREFON_ADCON = 3, /* no penirq */
+	PD_PENIRQ_ARM = PD_IREFON_ADCOFF,
+	PD_PENIRQ_DISARM = PD_IREFON_ADCON,
+};
+
+enum tsc2003_m {
+	M_12BIT = 0,
+	M_8BIT = 1
+};
+
+enum tsc2003_cmd {
+	MEAS_TEMP0 = 0,
+	MEAS_VBAT1 = 1,
+	MEAS_IN1 = 2,
+	MEAS_TEMP1 = 4,
+	MEAS_VBAT2 = 5,
+	MEAS_IN2 = 6,
+	ACTIVATE_NX_DRIVERS = 8,
+	ACTIVATE_NY_DRIVERS = 9,
+	ACTIVATE_YNX_DRIVERS = 10,
+	MEAS_XPOS = 12,
+	MEAS_YPOS = 13,
+	MEAS_Z1POS = 14,
+	MEAS_Z2POS = 15
+};
+
+struct tsc2003_data {
+	struct i2c_client client;
+	struct device_driver driver;
+	struct input_dev *idev;
+	struct semaphore sem;
+	struct task_struct *tstask;
+	struct completion tstask_completion;
+	struct completion penirq_completion;
+	enum tsc2003_pd pd;
+	enum tsc2003_m m;
+	int penirq;
+
+	int vbat1;
+	int vbat2;
+	int temp0;
+	int temp1;
+	int in1;
+	int in2;
+};
+
+static int
+		tsc2003_i2c_detect(struct i2c_adapter *adapter, int address, int kind);
+
+static int tsc2003_read(struct tsc2003_data *data, enum tsc2003_cmd cmd,
+		enum tsc2003_pd pd, int *val) {
+	char c;
+	char d[2];
+	int ret;
+
+	c = TSC2003_CMD(cmd, pd, data->m);
+	ret = i2c_master_send(&data->client, &c, 1);
+	if (ret <= 0)
+		goto err;
+
+	udelay(20);
+	ret = i2c_master_recv(&data->client, d, data->m == M_12BIT ? 2 : 1);
+	if (ret <= 0)
+		goto err;
+
+	if (val) {
+		*val = d[0];
+		*val <<= 4;
+		if (data->m == M_12BIT)
+			*val += (d[1] >> 4);
+	}
+
+#if defined(CONFIG_I2C_DEBUG_CHIP)
+	printk(KERN_ERR "%s: val[%x] = %d\n",
+			__FUNCTION__, cmd, (((int)d[0]) << 8) + d[1]);
+#endif
+
+	return 0;
+	err: if (!ret)
+		ret = -ENODEV;
+	return ret;
+}
+
+static inline int tsc2003_read_temp0(struct tsc2003_data *d, enum
+tsc2003_pd pd, int *t) {
+	return tsc2003_read(d, MEAS_TEMP0, pd, t);
+}
+
+static inline int tsc2003_read_temp1(struct tsc2003_data *d, enum
+tsc2003_pd pd, int *t) {
+	return tsc2003_read(d, MEAS_TEMP1, pd, t);
+}
+
+static inline int tsc2003_read_xpos(struct tsc2003_data *d, enum
+tsc2003_pd pd, int *x) {
+	return tsc2003_read(d, MEAS_XPOS, pd, x);
+}
+
+static inline int tsc2003_read_ypos(struct tsc2003_data *d, enum
+tsc2003_pd pd, int *y) {
+	return tsc2003_read(d, MEAS_YPOS, pd, y);
+}
+
+static inline int tsc2003_read_pressure(struct tsc2003_data *d, enum
+tsc2003_pd pd, int *p) {
+	return tsc2003_read(d, MEAS_Z1POS, pd, p);
+}
+
+static inline int tsc2003_read_in1(struct tsc2003_data *d, enum
+tsc2003_pd pd, int *t) {
+	return tsc2003_read(d, MEAS_IN1, pd, t);
+}
+
+static inline int tsc2003_read_in2(struct tsc2003_data *d, enum
+tsc2003_pd pd, int *t) {
+	return tsc2003_read(d, MEAS_IN2, pd, t);
+}
+
+static inline int tsc2003_read_vbat1(struct tsc2003_data *d, enum
+tsc2003_pd pd, int *t) {
+	return tsc2003_read(d, MEAS_VBAT1, pd, t);
+}
+
+static inline int tsc2003_read_vbat2(struct tsc2003_data *d, enum
+tsc2003_pd pd, int *t) {
+	return tsc2003_read(d, MEAS_VBAT2, pd, t);
+}
+
+static inline int tsc2003_powerdown(struct tsc2003_data *d) {
+	/* we don't have a distinct powerdown command,
+	 so do a benign read with the PD bits cleared */
+	return tsc2003_read(d, MEAS_IN1, PD_POWERDOWN, 0);
+}
+
+void tsc2003_init_client(struct i2c_client *client) {
+	struct tsc2003_data *data = i2c_get_clientdata(client);
+
+	data->pd = PD_PENIRQ_DISARM;
+	data->m = M_8BIT;
+	return;
+}
+
+static irqreturn_t tsc2003_penirq(int irq, void *v) {
+	struct tsc2003_data *d = v;
+
+	printk(KERN_INFO "penirq: %Ld\n", jiffies);
+
+    // disable the penirq while we take the sample					
+	disable_irq_nosync(d->penirq);
+
+	complete(&d->penirq_completion);
+	return IRQ_HANDLED;
+}
+
+static void tsc2003_pen_up(unsigned long v) {
+    printk(KERN_INFO "tsc2003_pen_up - re-enable irq now\n");
+	struct tsc2003_data *d = (struct tsc2003_data *)v;
+
+    enable_irq(d->penirq);
+
+	input_report_abs(d->idev, ABS_PRESSURE, 0);
+	input_sync(d->idev);
+	return;
+}
+
+static int tsc2003ts_thread(void *v) {
+	struct tsc2003_data *d = v;
+	struct task_struct *tsk = current;
+	int ret;
+	int err = 0;
+
+	d->tstask = tsk;
+
+	daemonize(DRIVER_NAME "tsd");
+	allow_signal(SIGKILL);
+
+	complete(&d->tstask_completion);
+
+#if defined(CONFIG_I2C_DEBUG_CHIP)
+	printk(KERN_INFO "%s: address 0x%x\n",
+			__FUNCTION__, d->client.addr);
+#endif		
+			
+	do {
+      unsigned int x, y, p, fp;
+      int pen_is_up = 0;
+      
+      down(&d->sem);         
+
+      // take the sample
+      d->pd = PD_PENIRQ_DISARM;
+      tsc2003_read_xpos(d, PD_PENIRQ_DISARM, &x);
+      tsc2003_read_ypos(d, PD_PENIRQ_DISARM, &y);
+      tsc2003_read_pressure(d, PD_PENIRQ_DISARM, &p);
+
+      if (p < 64) {
+        p=0;
+      }
+
+#if defined(CONFIG_I2C_DEBUG_CHIP)
+      printk(KERN_INFO "TSD X: %d Y: %d P: %d\n", x, y, p);
+#endif		
+
+      if (!pen_is_up) {
+        // report our touch to the input layer
+        input_report_abs(d->idev, ABS_X, 4096 - x);
+        input_report_abs(d->idev, ABS_Y, 4096 - y);
+        input_report_abs(d->idev, ABS_PRESSURE, p);
+        input_sync(d->idev);
+      }
+
+      if(p == 0) {
+#if defined(CONFIG_I2C_DEBUG_CHIP)
+        printk(KERN_INFO "No pressure - pen is up!\n");
+#endif		
+        // set our pen as up
+        pen_is_up = 1;                            
+      }                    
+      else {                    
+#if defined(CONFIG_I2C_DEBUG_CHIP)
+        printk(KERN_INFO "Pen is still down - sleeping and will re-sample!\n");
+#endif		
+        // set our pen as down
+        pen_is_up = 0;                            
+      }
+
+      // sleep for 3 jiffies to give us about 30 updates/sec
+      msleep (3);
+
+      up(&d->sem);
+
+	} while (!signal_pending(tsk));
+
+	d->tstask = NULL;
+	complete_and_exit(&d->tstask_completion, 0);
+}
+
+static int tsc2003_idev_open(struct input_dev *idev) {
+	struct tsc2003_data *d = idev->private;
+	int ret = 0;
+
+	if (down_interruptible(&d->sem))
+		return -EINTR;
+
+	if (d->tstask)
+		panic(DRIVER_NAME "tsd already running (!). abort.");
+
+	ret = kernel_thread(tsc2003ts_thread, d, CLONE_KERNEL);
+	if (ret >= 0) {
+		wait_for_completion(&d->tstask_completion);
+		ret = 0;
+	}
+
+	up(&d->sem);
+	return ret;
+}
+
+static void tsc2003_idev_close(struct input_dev *idev) {
+	struct tsc2003_data *d = idev->private;
+	down_interruptible(&d->sem);
+	if (d->tstask) {
+		send_sig(SIGKILL, d->tstask, 1);
+		wait_for_completion(&d->tstask_completion);
+	}
+
+	up(&d->sem);
+	return;
+}
+
+static int tsc2003_detect_irq(struct tsc2003_data *d) {
+	d->penirq = IRQ_GPIO(16); //PWM0 GPIO
+	return 0;
+}
+
+static int tsc2003_driver_register(struct tsc2003_data *data) {
+	struct input_dev *idev;
+	int ret = 0;
+	int error;
+
+	init_MUTEX(&data->sem);
+
+	error = tsc2003_detect_irq(data);
+	if (error) {
+		printk(KERN_ERR "TSC2003: IRQ probe failed\n");
+	}
+
+	if (data->penirq) {
+		ret = request_irq(data->penirq, tsc2003_penirq, SA_INTERRUPT | IRQF_TRIGGER_LOW,
+				DRIVER_NAME, data);
+		if (!ret) {
+			printk(KERN_INFO "%s: irq %d\n", __FUNCTION__, data->penirq);
+			init_completion(&data->tstask_completion);
+			init_completion(&data->penirq_completion);
+		} else {
+			printk(KERN_ERR "%s: cannot grab irq %d\n", __FUNCTION__, data->penirq);
+		}
+	}
+
+	idev = input_allocate_device();
+	data->idev = idev;
+
+	idev->private = data;
+	idev->name = DRIVER_NAME;
+	idev->evbit[0] = BIT(EV_ABS);
+	idev->open = tsc2003_idev_open;
+	idev->close = tsc2003_idev_close;
+	idev->absbit[LONG(ABS_X)] = BIT(ABS_X);
+	idev->absbit[LONG(ABS_Y)] = BIT(ABS_Y);
+	idev->absbit[LONG(ABS_PRESSURE)] = BIT(ABS_PRESSURE);
+	input_set_abs_params(idev, ABS_X, 0, ADC_MAX, 0, 0);
+	input_set_abs_params(idev, ABS_Y, 0, ADC_MAX, 0, 0);
+	input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0);
+
+	if (!ret) {
+		input_register_device(idev);
+		printk(KERN_INFO "tsc2003 - registering input device\n");
+	}
+
+	return ret;
+}
+
+static int tsc2003_i2c_attach_adapter(struct i2c_adapter *adapter) {
+	printk(KERN_INFO "tsc2003 i2c touch screen controller\n");
+	printk(KERN_INFO "Bill Gatliff <bgat at billgatliff.com\n");
+    printk(KERN_INFO "Nicholas Chen <nchen at cs.umd.edu>\n");
+
+    return i2c_probe(adapter, &addr_data, tsc2003_i2c_detect);
+}
+
+static int tsc2003_i2c_detach_client(struct i2c_client *client) {
+	int err;
+	struct tsc2003_data *d = i2c_get_clientdata(client);
+
+	free_irq(d->penirq, d);
+	input_unregister_device(d->idev);
+
+	if ((err = i2c_detach_client(client))) {
+		dev_err(&client->dev, "Client deregistration failed, "
+			"client not detached.\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static struct i2c_driver tsc2003_driver = { 
+    .driver = {
+	    .owner = THIS_MODULE,
+	    .name = DRIVER_NAME,
+    }, 
+    .attach_adapter = tsc2003_i2c_attach_adapter,
+    .detach_client = tsc2003_i2c_detach_client, 
+};
+
+static int tsc2003_i2c_detect(struct i2c_adapter *adapter, int address, int kind) {
+	struct i2c_client *new_client;
+	struct tsc2003_data *data;
+
+	int err = 0;
+	const char *name = "";
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
+			| I2C_FUNC_I2C | I2C_FUNC_SMBUS_WORD_DATA))
+		goto exit;
+
+	data = kcalloc(1, sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto exit;
+	}
+
+	new_client = &data->client;
+	i2c_set_clientdata(new_client, data);
+	new_client->addr = address;
+	new_client->adapter = adapter;
+	new_client->driver = &tsc2003_driver;
+	new_client->flags = 0;
+
+	/* TODO: I'm pretty sure I'm not dealing with kind correctly */
+	if (kind == 0 /* identification */|| kind < 0 /* detection */)
+		kind = tsc2003;
+
+	if (kind == tsc2003)
+		name = DRIVER_NAME;
+
+	/* try a command, see if we get an ack;
+	 if we do, assume it's our device */
+	printk(KERN_INFO "%s: probing address 0x%x\n",
+			__FUNCTION__, address);
+	err = tsc2003_powerdown(data);
+	if (err >= 0) {
+		strlcpy(new_client->name, name, I2C_NAME_SIZE);
+		err = i2c_attach_client(new_client);
+		if (err)
+			goto exit_free;
+
+		tsc2003_init_client(new_client);
+
+		err = tsc2003_driver_register(data);
+		if (err < 0)
+			goto exit_free;
+
+		printk(KERN_INFO "%s: device address 0x%x attached.\n",
+				__FUNCTION__, address);
+		return 0;
+	}
+	/* failure to detect when everything else is ok isn't an error */
+	else
+		err = 0;
+
+	exit_free: kfree(new_client);
+	exit: return err;
+}
+
+static int __init tsc2003_init(void)
+{
+	return i2c_add_driver(&tsc2003_driver);
+}
+
+static void __exit tsc2003_exit(void)
+{
+	i2c_del_driver(&tsc2003_driver);
+}
+
+MODULE_AUTHOR("Bill Gatliff <bgat at billgatliff.com>");
+MODULE_DESCRIPTION("TSC2003 Touch Screen Controller driver");
+MODULE_LICENSE("GPL");
+
+module_init(tsc2003_init);
+module_exit(tsc2003_exit);
-- 
1.5.3.7


