linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Input: Add tsc2046 touchscreen driver
@ 2009-12-13 20:39 Cory Maccarrone
       [not found] ` <1260736754-374-1-git-send-email-darkstar6262-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
  2010-04-17  8:28 ` Dmitry Torokhov
  0 siblings, 2 replies; 7+ messages in thread
From: Cory Maccarrone @ 2009-12-13 20:39 UTC (permalink / raw)
  To: linux-input, spi-devel-general; +Cc: Cory Maccarrone

This change implements a new driver for the TSC2046-series
touchscreen found on many HTC OMAP850-based smartphones.
This driver differs from the ADS7846 in that it uses 16-bit
commands instead of 8-bit, and does not include temerature
or voltage sensing capabilities.

This driver was submitted in May of 2007 to the linux-omap
list at:

http://linux.omap.com/pipermail/linux-omap-open-source/2007-May/009745.html

This is a resubmit of this driver, with additions that allows
the injection of pointercal parameters from tslib through
/sys.  This was done for the Wing Linux project primarily to
allow this touchscreen to work with Android, but it's useful for
any userspace that doesn't support tslib calibration.  Pointercal
use is optional -- it produces raw parameters unless the use_pointercal
/sys file is set to 1.

Though this driver was previously submitted to linux-omap, there
is nothing omap-specific in this driver.

Signed-off-by: Cory Maccarrone <darkstar6262@gmail.com>
---
 drivers/input/touchscreen/Kconfig   |   14 +
 drivers/input/touchscreen/Makefile  |    1 +
 drivers/input/touchscreen/tsc2046.c |  740 +++++++++++++++++++++++++++++++++++
 include/linux/input/tsc2046.h       |   26 ++
 4 files changed, 781 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/touchscreen/tsc2046.c
 create mode 100644 include/linux/input/tsc2046.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 8cc453c..1f3273b 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -501,6 +501,20 @@ config TOUCHSCREEN_TOUCHIT213
 	  To compile this driver as a module, choose M here: the
 	  module will be called touchit213.
 
+config TOUCHSCREEN_TSC2046
+	tristate "TSC2046 based touchscreens"
+	depends on SPI_MASTER
+	help
+	  Say Y here if you have a touchscreen interface using the
+	  TI TSC 2046 controller, and your board-specific initialization
+	  code includes that in its table of SPI devices.  Also make
+	  sure the proper SPI controller is selected.
+
+	  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 tsc2046.
+
 config TOUCHSCREEN_TSC2007
 	tristate "TSC2007 based touchscreens"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 15fa62c..3c413f7 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)	+= touchwin.o
 obj-$(CONFIG_TOUCHSCREEN_TSC2007)	+= tsc2007.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2046)	+= tsc2046.o
 obj-$(CONFIG_TOUCHSCREEN_UCB1400)	+= ucb1400_ts.o
 obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001)	+= wacom_w8001.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX)	+= wm97xx-ts.o
diff --git a/drivers/input/touchscreen/tsc2046.c b/drivers/input/touchscreen/tsc2046.c
new file mode 100644
index 0000000..e45d251
--- /dev/null
+++ b/drivers/input/touchscreen/tsc2046.c
@@ -0,0 +1,740 @@
+/*
+ * TSC2046 Touchscreen driver
+ *
+ * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com>
+ *
+ * Communication details from original TI driver
+ * Copyright (C) 2004-2005 Texas Instruments, Inc.
+ *
+ * Structure based heavily on TSC2301 driver
+ * Copyright (C) 2005-2006 Nokia Corporation
+ *
+ * 2007 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+
+#include <linux/input/tsc2046.h>
+
+/* TSC2046 commands */
+#define START_BYTE		0x8000
+#define X_CMDWD			0xd300
+#define Y_CMDWD			0x9300
+#define Z1_CMDWD		0xb300
+#define Z2_CMDWD		0xc300
+
+#define TSC2046_TS_SCAN_TIME	1
+#define MAX_12BIT		((1 << 12) - 1)
+
+#define MIN_ABS_X    528
+#define MAX_ABS_X    3760
+#define MIN_ABS_Y    624
+#define MAX_ABS_Y    3760
+
+/* The TSC2046 operates at a maximum speed of 2MHz */
+#define TSC2046_HZ	2000000
+
+#define CONV_DATA(d1, d2) \
+  (((d1 & 0x7f) << 5) | ((d2 >> 11) & 0x1f))
+
+/* Convenience macros for pointercal calibration calculation */
+#define PA ts->pointercal[0]
+#define PB ts->pointercal[1]
+#define PC ts->pointercal[2]
+#define PD ts->pointercal[3]
+#define PE ts->pointercal[4]
+#define PF ts->pointercal[5]
+#define PS ts->pointercal[6]
+
+struct tsc2046_ts {
+	struct input_dev	*idev;
+	char			phys[32];
+	struct timer_list	timer;
+	spinlock_t		lock;
+
+	struct spi_transfer	read_xfer[3];
+	struct spi_message	read_msg;
+	struct spi_message	enable_msg;
+	u16                     data[5];
+
+	u16			x;
+	u16			y;
+	u16			p;
+	u16			old_x;
+	u16			old_y;
+	u16			old_pressure;
+	int			sample_cnt;
+
+	int			ignore_last : 1;
+	u16			x_plate_ohm;
+	int			max_pressure;
+	int			touch_pressure;
+	int			pressure_limit;
+
+	int			pointercal[7];
+	u16			x_res;
+	u16			y_res;
+
+	u16			irq_enabled:1;
+	u16			pen_down:1;
+	u16			disabled:1;
+	u16			pending:1;
+	u16			use_pointercal:1;
+
+	s16			dav_gpio;
+	int			(*get_pendown_state)(void);
+	int			irq;
+};
+
+struct tsc2046 {
+	struct spi_device	*spi;
+	struct tsc2046_ts	*ts;
+};
+
+static const u16 tsc2046_ts_cmd_data[] = {
+	START_BYTE, X_CMDWD, Y_CMDWD, Z1_CMDWD, Z2_CMDWD,
+};
+
+static int tsc2046_device_suspended(struct device *dev)
+{
+	struct tsc2046 *tsc = dev_get_drvdata(dev);
+	return dev->power.power_state.event != PM_EVENT_ON || tsc->ts->disabled;
+}
+
+static void tsc2046_update_pen_state(struct tsc2046 *tsc, int x, int y, int pressure)
+{
+	struct tsc2046_ts *ts = tsc->ts;
+
+	if (x <= ts->old_x - 4 || x >= ts->old_x + 4 ||
+	    y <= ts->old_y - 4 || y >= ts->old_y + 4 ||
+	    pressure <= ts->old_pressure - 4 || pressure >= ts->old_pressure + 4)
+	{
+		if (pressure) {
+			if (!ts->use_pointercal || (ts->use_pointercal &&
+			    x >= 0 &&
+			    x <= ts->x_res - 1 &&
+			    y >= 0 &&
+			    y <= ts->y_res - 1))
+			{
+				input_report_abs(ts->idev, ABS_X, x);
+				input_report_abs(ts->idev, ABS_Y, y);
+				input_report_abs(ts->idev, ABS_PRESSURE, 1);
+				input_report_key(ts->idev, BTN_TOUCH, 1);
+				ts->pen_down = 1;
+			} else {
+				input_report_abs(ts->idev, ABS_PRESSURE, 0);
+				input_report_key(ts->idev, BTN_TOUCH, 0);
+				ts->pen_down = 0;
+			}
+		} else {
+			input_report_abs(ts->idev, ABS_PRESSURE, 0);
+			input_report_key(ts->idev, BTN_TOUCH, 0);
+			ts->pen_down = 0;
+		}
+
+		ts->old_x = x;
+		ts->old_y = y;
+		ts->old_pressure = pressure;
+
+		input_sync(ts->idev);
+
+#ifdef VERBOSE
+		dev_dbg(&tsc->spi->dev, "x %4d y %4d p %4d\n", x, y, pressure);
+#endif
+	}
+}
+
+/*
+ * This procedure is called by the SPI framework after the coordinates
+ * have been read from TSC2046
+ */
+static void tsc2046_ts_rx(void *arg)
+{
+	struct tsc2046 *tsc = arg;
+	struct tsc2046_ts *ts = tsc->ts;
+	unsigned int x, y, z1, z2, pressure;
+	int xN, yN;
+
+	x  = CONV_DATA(ts->data[2], ts->data[3]);
+	y  = CONV_DATA(ts->data[1], ts->data[2]);
+	z1 = CONV_DATA(ts->data[3], ts->data[4]);
+	z2 = CONV_DATA(ts->data[4], 0);
+
+	if (z1) {
+		pressure = ts->x_plate_ohm * x;
+		pressure /= 4096;
+		pressure *= z2 - z1;
+		pressure /= z1;
+	} else
+		pressure = 0;
+
+	if (ts->use_pointercal) {
+		/* Transform pen state according to calibration parameters.  This
+		 * algorithm is the same used by tslib and Qt to do 7-value
+		 * screen transforms.  See:
+		 *
+		 * http://doc.trolltech.com/4.3/qwscalibratedmousehandler.html#transform
+		 */
+		xN = ((PA * x) + (PB * y) + PC) / PS;
+		yN = ((PD * x) + (PE * y) + PF) / PS;
+
+		x = (xN < 0) ? 0 : (xN > ts->x_res ? ts->x_res - 1 : (unsigned int)xN);
+		y = (yN < 0) ? 0 : (yN > ts->y_res ? ts->y_res - 1 : (unsigned int)yN);
+	}
+
+	/* If pressure value is above a preset limit (pen is barely
+	 * touching the screen) we can't trust the coordinate values.
+	 */
+	if (pressure < ts->pressure_limit && x < MAX_12BIT && y < MAX_12BIT) {
+		ts->pressure_limit = ts->max_pressure;
+		if (ts->ignore_last) {
+			if (ts->sample_cnt)
+				tsc2046_update_pen_state(tsc, ts->x, ts->y, ts->p);
+			ts->x = x;
+			ts->y = y;
+			ts->p = pressure;
+		} else
+			tsc2046_update_pen_state(tsc, x, y, pressure);
+		ts->sample_cnt++;
+	}
+
+	mod_timer(&ts->timer,
+		  jiffies + msecs_to_jiffies(TSC2046_TS_SCAN_TIME));
+}
+
+static int tsc2046_is_pen_down(struct tsc2046_ts *ts)
+{
+	return ts->pen_down;
+}
+
+static int tsc2046_get_pendown_state(struct tsc2046_ts *ts)
+{
+	if (ts->get_pendown_state)
+		return ts->get_pendown_state();
+
+	return !gpio_get_value(ts->dav_gpio);
+}
+
+/*
+ * Timer is called every TSC2046_TS_SCAN_TIME when the pen is down
+ */
+static void tsc2046_ts_timer(unsigned long arg)
+{
+	struct tsc2046 *tsc = (void *) arg;
+	struct tsc2046_ts *ts = tsc->ts;
+	unsigned long flags;
+	int ndav;
+	int r;
+
+	spin_lock_irqsave(&ts->lock, flags);
+	ndav = tsc2046_get_pendown_state(ts);
+	if (!ndav || tsc2046_device_suspended(&tsc->spi->dev)) {
+		/* Pen has been lifted */
+		if (!tsc2046_device_suspended(&tsc->spi->dev) &&
+		    !ts->irq_enabled)
+		{
+			ts->irq_enabled = 1;
+			enable_irq(ts->irq);
+		}
+		tsc2046_update_pen_state(tsc, 0, 0, 0);
+		ts->pending = 0;
+		spin_unlock_irqrestore(&ts->lock, flags);
+
+	} else {
+		ts->pen_down = 1;
+		spin_unlock_irqrestore(&ts->lock, flags);
+
+		r = spi_async(tsc->spi, &ts->read_msg);
+		if (r)
+			dev_err(&tsc->spi->dev, "ts: spi_async() failed");
+	}
+}
+
+/*
+ * This interrupt is called when pen is down and first coordinates are
+ * available. That is indicated by a falling edge on DEV line.  IRQ is
+ * disabled here because while the pen is down the coordinates are
+ * read by a timer.
+ */
+static irqreturn_t tsc2046_ts_irq_handler(int irq, void *dev_id)
+{
+	struct tsc2046 *tsc = dev_id;
+	struct tsc2046_ts *ts = tsc->ts;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ts->lock, flags);
+	if (ts->irq_enabled) {
+		ts->irq_enabled = 0;
+		disable_irq_nosync(ts->irq);
+		ts->pending = 1;
+		ts->pressure_limit = ts->touch_pressure;
+		ts->sample_cnt = 0;
+		mod_timer(&ts->timer,
+			  jiffies + msecs_to_jiffies(TSC2046_TS_SCAN_TIME));
+	}
+	spin_unlock_irqrestore(&ts->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+/* Must be called with ts->lock held */
+static void tsc2046_ts_disable(struct tsc2046 *tsc)
+{
+	struct tsc2046_ts *ts = tsc->ts;
+
+	if (ts->disabled)
+		return;
+
+	ts->disabled = 1;
+	if (!ts->pending) {
+		ts->irq_enabled = 0;
+		disable_irq(ts->irq);
+	} else {
+		while (ts->pending) {
+			spin_unlock_irq(&ts->lock);
+			msleep(1);
+			spin_lock_irq(&ts->lock);
+		}
+	}
+}
+
+static void tsc2046_ts_enable(struct tsc2046 *tsc)
+{
+	struct tsc2046_ts *ts = tsc->ts;
+
+	if (!ts->disabled)
+		return;
+
+	ts->disabled = 0;
+	ts->irq_enabled = 1;
+	enable_irq(ts->irq);
+}
+
+#ifdef CONFIG_PM
+int tsc2046_ts_suspend(struct tsc2046 *tsc)
+{
+	struct tsc2046_ts *ts = tsc->ts;
+
+	spin_lock_irq(&ts->lock);
+	tsc2046_ts_disable(tsc);
+	spin_unlock_irq(&ts->lock);
+
+	return 0;
+}
+
+void tsc2046_ts_resume(struct tsc2046 *tsc)
+{
+	struct tsc2046_ts *ts = tsc->ts;
+
+	spin_lock_irq(&ts->lock);
+	tsc2046_ts_enable(tsc);
+	spin_unlock_irq(&ts->lock);
+}
+#endif
+
+static void tsc2046_ts_setup_spi_xfer(struct tsc2046 *tsc)
+{
+	struct tsc2046_ts *ts = tsc->ts;
+	struct spi_message *m = &ts->read_msg;
+	struct spi_transfer *x = &ts->read_xfer[1];
+
+	spi_message_init(m);
+
+	/* read and write data in one transaction */
+	x->tx_buf = &tsc2046_ts_cmd_data;
+	x->rx_buf = &ts->data;
+	x->len    = 10;
+	spi_message_add_tail(x, m);
+
+	/* send another START_BYTE to (re)enable pen interrupts */
+	x++;
+	x->tx_buf = &tsc2046_ts_cmd_data[0];
+	x->rx_buf = &ts->data;
+	x->len = 2;
+	spi_message_add_tail(x, m);
+
+	m->complete = tsc2046_ts_rx;
+	m->context = tsc;
+}
+
+/* Attribute functions */
+
+static ssize_t tsc2046_ts_use_pointercal_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct tsc2046		*tsc = dev_get_drvdata(dev);
+	struct tsc2046_ts	*ts = tsc->ts;
+
+	return sprintf(buf, "%u\n", ts->use_pointercal);
+}
+
+static ssize_t tsc2046_ts_use_pointercal_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct tsc2046		*tsc = dev_get_drvdata(dev);
+	struct tsc2046_ts	*ts = tsc->ts;
+	char *endp;
+	int i;
+
+	i = simple_strtoul(buf, &endp, 10);
+	spin_lock_irq(&ts->lock);
+
+	if (i)
+		ts->use_pointercal = 1;
+	else
+		ts->use_pointercal = 0;
+
+	spin_unlock_irq(&ts->lock);
+
+	return count;
+}
+
+static DEVICE_ATTR(use_pointercal, 0644, tsc2046_ts_use_pointercal_show, tsc2046_ts_use_pointercal_store);
+
+static ssize_t tsc2046_ts_pointercal_show(struct device *dev,
+		   struct device_attribute *attr, char *buf)
+{
+	struct tsc2046		*tsc = dev_get_drvdata(dev);
+	struct tsc2046_ts	*ts = tsc->ts;
+
+	return sprintf(buf, "%d %d %d %d %d %d %d\n", PA, PB, PC, PD, PE, PF, PS);
+}
+
+static ssize_t tsc2046_ts_pointercal_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct tsc2046		*tsc = dev_get_drvdata(dev);
+	struct tsc2046_ts	*ts = tsc->ts;
+	char *endp;
+	int i;
+	unsigned int index = 0, offset = 0;
+
+	spin_lock_irq(&ts->lock);
+
+	for (index = 0; index < 7; index++) {
+		/* Grab the numbers */
+		i = simple_strtol(buf + offset, &endp, 10);
+		ts->pointercal[index] = i;
+
+		/* Skip the space */
+		offset = endp - buf + 1;
+	}
+
+	spin_unlock_irq(&ts->lock);
+
+	return count;
+}
+
+static DEVICE_ATTR(pointercal, 0644, tsc2046_ts_pointercal_show, tsc2046_ts_pointercal_store);
+
+static ssize_t tsc2046_ts_screen_res_show(struct device *dev,
+		   struct device_attribute *attr, char *buf)
+{
+	struct tsc2046		*tsc = dev_get_drvdata(dev);
+	struct tsc2046_ts	*ts = tsc->ts;
+
+	return sprintf(buf, "%u %u\n", ts->x_res, ts->y_res);
+}
+
+static ssize_t tsc2046_ts_screen_res_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct tsc2046		*tsc = dev_get_drvdata(dev);
+	struct tsc2046_ts	*ts = tsc->ts;
+	char *endp;
+	unsigned int i, offset = 0;
+
+	spin_lock_irq(&ts->lock);
+
+	/* Grab the x res */
+	i = simple_strtoul(buf, &endp, 10);
+	ts->x_res = i;
+
+	/* Skip the space */
+	offset = endp - buf + 1;
+
+	/* Grab the y res */
+	i = simple_strtoul(buf + offset, &endp, 10);
+	ts->y_res = i;
+
+	/* Update the device parameters */
+	input_set_abs_params(ts->idev, ABS_X, 0, ts->x_res - 1, 0, 0);
+	input_set_abs_params(ts->idev, ABS_Y, 0, ts->y_res - 1, 0, 0);
+
+	spin_unlock_irq(&ts->lock);
+
+	return count;
+}
+
+static DEVICE_ATTR(screen_res, 0644, tsc2046_ts_screen_res_show, tsc2046_ts_screen_res_store);
+
+static ssize_t tsc2046_ts_pen_down_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct tsc2046 *tsc = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", tsc2046_is_pen_down(tsc->ts));
+}
+
+static DEVICE_ATTR(pen_down, S_IRUGO, tsc2046_ts_pen_down_show, NULL);
+
+static ssize_t tsc2046_ts_disable_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct tsc2046		*tsc = dev_get_drvdata(dev);
+	struct tsc2046_ts	*ts = tsc->ts;
+
+	return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t tsc2046_ts_disable_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct tsc2046		*tsc = dev_get_drvdata(dev);
+	struct tsc2046_ts	*ts = tsc->ts;
+	char *endp;
+	int i;
+
+	i = simple_strtoul(buf, &endp, 10);
+	spin_lock_irq(&ts->lock);
+
+	if (i)
+		tsc2046_ts_disable(tsc);
+	else
+		tsc2046_ts_enable(tsc);
+
+	spin_unlock_irq(&ts->lock);
+
+	return count;
+}
+
+static DEVICE_ATTR(disable_ts, 0664, tsc2046_ts_disable_show,
+		   tsc2046_ts_disable_store);
+
+/* Init functions */
+
+static int __devinit tsc2046_probe(struct spi_device *spi)
+{
+	struct tsc2046			*tsc;
+	struct tsc2046_ts		*ts;
+	struct tsc2046_platform_data	*pdata = spi->dev.platform_data;
+	struct input_dev		*idev;
+	int r = -ENODEV;
+
+	dev_dbg(&spi->dev, "%s\n", __FUNCTION__);
+
+	if (!pdata) {
+		dev_dbg(&spi->dev, "no platform data?\n");
+		return -ENODEV;
+	}
+
+	tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
+	if (tsc == NULL)
+		return -ENOMEM;
+
+	dev_set_drvdata(&spi->dev, tsc);
+	tsc->spi = spi;
+	spi->dev.power.power_state = PMSG_ON;
+
+	spi->mode = SPI_MODE_1;
+	spi->bits_per_word = 16;
+
+	/* The max speed might've been defined by the board-specific
+	 * struct */
+	if (!spi->max_speed_hz)
+		spi->max_speed_hz = TSC2046_HZ;
+	spi_setup(spi);
+
+	if (spi->irq < 0) {
+		dev_err(&tsc->spi->dev, "need DAV IRQ/GPIO");
+		return -EINVAL;
+	}
+
+	if (!pdata->get_pendown_state && !gpio_is_valid(pdata->gpio_pendown)) {
+		dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n");
+		return -EINVAL;
+	}
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (ts == NULL)
+		return -ENOMEM;
+	tsc->ts = ts;
+
+	if (pdata->get_pendown_state)
+		ts->get_pendown_state = pdata->get_pendown_state;
+	else {
+		ts->dav_gpio = pdata->gpio_pendown;
+		r = gpio_request(ts->dav_gpio, dev_name(&tsc->spi->dev));
+		if (r < 0) {
+			dev_err(&tsc->spi->dev, "unable to get DAV GPIO");
+			goto err1;
+		}
+		gpio_direction_input(ts->dav_gpio);
+	}
+
+	ts->irq = spi->irq;
+
+	init_timer(&ts->timer);
+	ts->timer.data = (unsigned long) tsc;
+	ts->timer.function = tsc2046_ts_timer;
+
+	spin_lock_init(&ts->lock);
+
+	ts->x_plate_ohm	= pdata->ts_x_plate_ohm ? : 280;
+	ts->max_pressure = pdata->ts_max_pressure ? : MAX_12BIT;
+	ts->touch_pressure = pdata->ts_touch_pressure ? : ts->max_pressure;
+	ts->ignore_last	= pdata->ts_ignore_last;
+
+	idev = input_allocate_device();
+	if (idev == NULL) {
+		r = -ENOMEM;
+		goto err2;
+	}
+	idev->name = "TSC2046 touchscreen";
+	snprintf(ts->phys, sizeof(ts->phys),
+		 "%s/input-ts", dev_name(&tsc->spi->dev));
+	idev->phys = ts->phys;
+
+	idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+	idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+	ts->idev = idev;
+
+	tsc2046_ts_setup_spi_xfer(tsc);
+
+	/* X and Y res default to 240x320 */
+	ts->x_res = pdata->ts_x_res ? : 240;
+	ts->y_res = pdata->ts_y_res ? : 320;
+
+	input_set_abs_params(idev, ABS_X, 0, ts->x_res - 1, 0, 0);
+	input_set_abs_params(idev, ABS_Y, 0, ts->y_res - 1, 0, 0);
+	input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0);
+
+	set_bit(BTN_TOUCH, idev->keybit);
+
+	r = request_irq(ts->irq, tsc2046_ts_irq_handler,
+			IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
+			"tsc2046-ts", tsc);
+	if (r < 0) {
+		dev_err(&tsc->spi->dev, "unable to get DAV IRQ");
+		goto err3;
+	}
+	set_irq_wake(ts->irq, 1);
+	ts->irq_enabled = 1;
+
+	/* Create the various driver attributes */
+	if (device_create_file(&tsc->spi->dev, &dev_attr_pen_down) < 0)
+		goto err4;
+	if (device_create_file(&tsc->spi->dev, &dev_attr_disable_ts) < 0)
+		goto err5;
+	if (device_create_file(&tsc->spi->dev, &dev_attr_use_pointercal) < 0)
+		goto err6;
+	if (device_create_file(&tsc->spi->dev, &dev_attr_pointercal) < 0)
+		goto err7;
+	if (device_create_file(&tsc->spi->dev, &dev_attr_screen_res) < 0)
+		goto err8;
+
+	r = input_register_device(idev);
+	if (r < 0) {
+		dev_err(&tsc->spi->dev, "can't register touchscreen device\n");
+		goto err9;
+	}
+
+	/* kick off a transaction to enable pen interrupts */
+	spi_async(tsc->spi, &ts->read_msg);
+
+	return 0;
+err9:
+	device_remove_file(&tsc->spi->dev, &dev_attr_screen_res);
+err8:
+	device_remove_file(&tsc->spi->dev, &dev_attr_pointercal);
+err7:
+	device_remove_file(&tsc->spi->dev, &dev_attr_use_pointercal);
+err6:
+	device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
+err5:
+	device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
+err4:
+	free_irq(ts->irq, tsc);
+err3:
+	input_free_device(idev);
+err2:
+	if (ts->dav_gpio)
+		gpio_free(ts->dav_gpio);
+err1:
+	kfree(ts);
+	return r;
+}
+
+static int __devexit tsc2046_remove(struct spi_device *spi)
+{
+	struct tsc2046 *tsc = dev_get_drvdata(&spi->dev);
+	struct tsc2046_ts *ts = tsc->ts;
+	unsigned long flags;
+
+	dev_dbg(&tsc->spi->dev, "%s\n", __FUNCTION__);
+
+	spin_lock_irqsave(&ts->lock, flags);
+	tsc2046_ts_disable(tsc);
+	spin_unlock_irqrestore(&ts->lock, flags);
+
+	device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
+	device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
+	device_remove_file(&tsc->spi->dev, &dev_attr_use_pointercal);
+	device_remove_file(&tsc->spi->dev, &dev_attr_pointercal);
+	device_remove_file(&tsc->spi->dev, &dev_attr_screen_res);
+
+	free_irq(ts->irq, tsc);
+	input_unregister_device(ts->idev);
+
+	if (ts->dav_gpio)
+		gpio_free(ts->dav_gpio);
+
+	kfree(ts);
+	kfree(tsc);
+
+	return 0;
+}
+
+static struct spi_driver tsc2046_driver = {
+	.driver = {
+		   .name = "tsc2046",
+		   .bus = &spi_bus_type,
+		   .owner = THIS_MODULE,
+	},
+	.probe = tsc2046_probe,
+	.remove = __devexit_p(tsc2046_remove),
+};
+
+static int __init tsc2046_init(void)
+{
+	printk(KERN_INFO "TSC2046 driver initializing\n");
+
+	return spi_register_driver(&tsc2046_driver);
+}
+
+static void __exit tsc2046_exit(void)
+{
+	spi_unregister_driver(&tsc2046_driver);
+}
+
+module_init(tsc2046_init);
+module_exit(tsc2046_exit);
+
+MODULE_AUTHOR("Kevin Hilman <khilman@mvista.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/include/linux/input/tsc2046.h b/include/linux/input/tsc2046.h
new file mode 100644
index 0000000..acb8fed
--- /dev/null
+++ b/include/linux/input/tsc2046.h
@@ -0,0 +1,26 @@
+#ifndef _LINUX_INPUT_TSC2046_H
+#define _LINUX_INPUT_TSC2046_H
+
+#include <linux/types.h>
+#include <linux/timer.h>
+
+struct tsc2046_platform_data {
+	int	(*get_pendown_state)(void);
+	int	gpio_pendown;
+	s16	gpio_debounce;
+	u16	ts_x_plate_ohm;
+	u32	ts_max_pressure;  /* Samples with bigger pressure value will
+				     be ignored, since the corresponding X, Y
+				     values are unreliable */
+	u32	ts_touch_pressure;/* Pressure limit until we report a
+				     touch event. After that we switch
+				     to ts_max_pressure. */
+	u32	ts_x_res;
+	u32	ts_y_res;
+	unsigned ts_ignore_last : 1;
+
+};
+
+#endif
+
+
-- 
1.6.3.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] Input: Add tsc2046 touchscreen driver
       [not found] ` <1260736754-374-1-git-send-email-darkstar6262-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
@ 2010-01-10 23:07   ` Cory Maccarrone
  2010-03-07 17:54     ` Cory Maccarrone
  0 siblings, 1 reply; 7+ messages in thread
From: Cory Maccarrone @ 2010-01-10 23:07 UTC (permalink / raw)
  To: linux-input-u79uwXL29TY76Z2rM5mHXA,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Sun, Dec 13, 2009 at 12:39 PM, Cory Maccarrone
<darkstar6262@gmail.com> wrote:
> This change implements a new driver for the TSC2046-series
> touchscreen found on many HTC OMAP850-based smartphones.
> This driver differs from the ADS7846 in that it uses 16-bit
> commands instead of 8-bit, and does not include temerature
> or voltage sensing capabilities.
>
> This driver was submitted in May of 2007 to the linux-omap
> list at:
>
> http://linux.omap.com/pipermail/linux-omap-open-source/2007-May/009745.html
>
> This is a resubmit of this driver, with additions that allows
> the injection of pointercal parameters from tslib through
> /sys.  This was done for the Wing Linux project primarily to
> allow this touchscreen to work with Android, but it's useful for
> any userspace that doesn't support tslib calibration.  Pointercal
> use is optional -- it produces raw parameters unless the use_pointercal
> /sys file is set to 1.
>
> Though this driver was previously submitted to linux-omap, there
> is nothing omap-specific in this driver.
>
> Signed-off-by: Cory Maccarrone <darkstar6262@gmail.com>

I haven't heard anything on this yet, so I want to give a gentle poke.
 Any chance of this making it in for .33, or has this patch already
missed that window?

Thanks
Cory

------------------------------------------------------------------------------
This SF.Net email is sponsored by the Verizon Developer Community
Take advantage of Verizon's best-in-class app development support
A streamlined, 14 day to market process makes app distribution fast and easy
Join now and get one step closer to millions of Verizon customers
http://p.sf.net/sfu/verizon-dev2dev 
_______________________________________________
spi-devel-general mailing list
spi-devel-general@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] Input: Add tsc2046 touchscreen driver
  2010-01-10 23:07   ` Cory Maccarrone
@ 2010-03-07 17:54     ` Cory Maccarrone
  0 siblings, 0 replies; 7+ messages in thread
From: Cory Maccarrone @ 2010-03-07 17:54 UTC (permalink / raw)
  To: linux-input, spi-devel-general, Dmitry Torokhov; +Cc: Cory Maccarrone

On Sun, Jan 10, 2010 at 3:07 PM, Cory Maccarrone <darkstar6262@gmail.com> wrote:
> On Sun, Dec 13, 2009 at 12:39 PM, Cory Maccarrone
> <darkstar6262@gmail.com> wrote:
>> This change implements a new driver for the TSC2046-series
>> touchscreen found on many HTC OMAP850-based smartphones.
>> This driver differs from the ADS7846 in that it uses 16-bit
>> commands instead of 8-bit, and does not include temerature
>> or voltage sensing capabilities.
>>
>> This driver was submitted in May of 2007 to the linux-omap
>> list at:
>>
>> http://linux.omap.com/pipermail/linux-omap-open-source/2007-May/009745.html
>>
>> This is a resubmit of this driver, with additions that allows
>> the injection of pointercal parameters from tslib through
>> /sys.  This was done for the Wing Linux project primarily to
>> allow this touchscreen to work with Android, but it's useful for
>> any userspace that doesn't support tslib calibration.  Pointercal
>> use is optional -- it produces raw parameters unless the use_pointercal
>> /sys file is set to 1.
>>
>> Though this driver was previously submitted to linux-omap, there
>> is nothing omap-specific in this driver.
>>
>> Signed-off-by: Cory Maccarrone <darkstar6262@gmail.com>
>
> I haven't heard anything on this yet, so I want to give a gentle poke.
>  Any chance of this making it in for .33, or has this patch already
> missed that window?
>
> Thanks
> Cory
>

I've still heard nothing on this for several months.  Can someone
please take a look at it and provide feedback?  Can this patch be
included soon?

- Cory
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] Input: Add tsc2046 touchscreen driver
  2009-12-13 20:39 [PATCH] Input: Add tsc2046 touchscreen driver Cory Maccarrone
       [not found] ` <1260736754-374-1-git-send-email-darkstar6262-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
@ 2010-04-17  8:28 ` Dmitry Torokhov
  2010-05-30 20:21   ` Cory Maccarrone
  1 sibling, 1 reply; 7+ messages in thread
From: Dmitry Torokhov @ 2010-04-17  8:28 UTC (permalink / raw)
  To: Cory Maccarrone; +Cc: linux-input, spi-devel-general

Hi Cory,

Sorry for not reviewing the driver earlier.

On Sun, Dec 13, 2009 at 12:39:14PM -0800, Cory Maccarrone wrote:
> This change implements a new driver for the TSC2046-series
> touchscreen found on many HTC OMAP850-based smartphones.
> This driver differs from the ADS7846 in that it uses 16-bit
> commands instead of 8-bit, and does not include temerature
> or voltage sensing capabilities.
> 
> This driver was submitted in May of 2007 to the linux-omap
> list at:
> 
> http://linux.omap.com/pipermail/linux-omap-open-source/2007-May/009745.html
> 
> This is a resubmit of this driver, with additions that allows
> the injection of pointercal parameters from tslib through
> /sys.  This was done for the Wing Linux project primarily to
> allow this touchscreen to work with Android, but it's useful for
> any userspace that doesn't support tslib calibration.  Pointercal
> use is optional -- it produces raw parameters unless the use_pointercal
> /sys file is set to 1.

The current poiicy is simply use tslib and push the filtering and
scaling out of the kernel. I do not see the reason for this driver be an
exception.

> 
> Though this driver was previously submitted to linux-omap, there
> is nothing omap-specific in this driver.
> 

Overall I would like to see this being not a standalone driver but
instead tsc2046 support being plugged into existing tsc2007, extending
it. There are a few of TSC drivers floating around, all of them very
similar, with the difference of the transport (i2c vs spi) and register
sets but all having very similar core interrupt handling, suspend and
resume algorithms... This is a waste.

> Signed-off-by: Cory Maccarrone <darkstar6262@gmail.com>
> ---
>  drivers/input/touchscreen/Kconfig   |   14 +
>  drivers/input/touchscreen/Makefile  |    1 +
>  drivers/input/touchscreen/tsc2046.c |  740 +++++++++++++++++++++++++++++++++++
>  include/linux/input/tsc2046.h       |   26 ++
>  4 files changed, 781 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/input/touchscreen/tsc2046.c
>  create mode 100644 include/linux/input/tsc2046.h
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 8cc453c..1f3273b 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -501,6 +501,20 @@ config TOUCHSCREEN_TOUCHIT213
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called touchit213.
>  
> +config TOUCHSCREEN_TSC2046
> +	tristate "TSC2046 based touchscreens"
> +	depends on SPI_MASTER
> +	help
> +	  Say Y here if you have a touchscreen interface using the
> +	  TI TSC 2046 controller, and your board-specific initialization
> +	  code includes that in its table of SPI devices.  Also make
> +	  sure the proper SPI controller is selected.
> +
> +	  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 tsc2046.
> +
>  config TOUCHSCREEN_TSC2007
>  	tristate "TSC2007 based touchscreens"
>  	depends on I2C
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 15fa62c..3c413f7 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o
>  obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o
>  obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)	+= touchwin.o
>  obj-$(CONFIG_TOUCHSCREEN_TSC2007)	+= tsc2007.o
> +obj-$(CONFIG_TOUCHSCREEN_TSC2046)	+= tsc2046.o
>  obj-$(CONFIG_TOUCHSCREEN_UCB1400)	+= ucb1400_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001)	+= wacom_w8001.o
>  obj-$(CONFIG_TOUCHSCREEN_WM97XX)	+= wm97xx-ts.o
> diff --git a/drivers/input/touchscreen/tsc2046.c b/drivers/input/touchscreen/tsc2046.c
> new file mode 100644
> index 0000000..e45d251
> --- /dev/null
> +++ b/drivers/input/touchscreen/tsc2046.c
> @@ -0,0 +1,740 @@
> +/*
> + * TSC2046 Touchscreen driver
> + *
> + * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com>
> + *
> + * Communication details from original TI driver
> + * Copyright (C) 2004-2005 Texas Instruments, Inc.
> + *
> + * Structure based heavily on TSC2301 driver
> + * Copyright (C) 2005-2006 Nokia Corporation
> + *
> + * 2007 (c) MontaVista Software, Inc. This file is licensed under
> + * the terms of the GNU General Public License version 2. This program
> + * is licensed "as is" without any warranty of any kind, whether express
> + * or implied.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/spi/spi.h>
> +#include <linux/gpio.h>
> +
> +#include <linux/input/tsc2046.h>
> +
> +/* TSC2046 commands */
> +#define START_BYTE		0x8000
> +#define X_CMDWD			0xd300
> +#define Y_CMDWD			0x9300
> +#define Z1_CMDWD		0xb300
> +#define Z2_CMDWD		0xc300
> +
> +#define TSC2046_TS_SCAN_TIME	1
> +#define MAX_12BIT		((1 << 12) - 1)
> +
> +#define MIN_ABS_X    528
> +#define MAX_ABS_X    3760
> +#define MIN_ABS_Y    624
> +#define MAX_ABS_Y    3760
> +
> +/* The TSC2046 operates at a maximum speed of 2MHz */
> +#define TSC2046_HZ	2000000
> +
> +#define CONV_DATA(d1, d2) \
> +  (((d1 & 0x7f) << 5) | ((d2 >> 11) & 0x1f))
> +
> +/* Convenience macros for pointercal calibration calculation */
> +#define PA ts->pointercal[0]
> +#define PB ts->pointercal[1]
> +#define PC ts->pointercal[2]
> +#define PD ts->pointercal[3]
> +#define PE ts->pointercal[4]
> +#define PF ts->pointercal[5]
> +#define PS ts->pointercal[6]
> +
> +struct tsc2046_ts {
> +	struct input_dev	*idev;
> +	char			phys[32];
> +	struct timer_list	timer;
> +	spinlock_t		lock;
> +
> +	struct spi_transfer	read_xfer[3];
> +	struct spi_message	read_msg;
> +	struct spi_message	enable_msg;
> +	u16                     data[5];
> +
> +	u16			x;
> +	u16			y;
> +	u16			p;
> +	u16			old_x;
> +	u16			old_y;
> +	u16			old_pressure;
> +	int			sample_cnt;
> +
> +	int			ignore_last : 1;
> +	u16			x_plate_ohm;
> +	int			max_pressure;
> +	int			touch_pressure;
> +	int			pressure_limit;
> +
> +	int			pointercal[7];
> +	u16			x_res;
> +	u16			y_res;
> +
> +	u16			irq_enabled:1;
> +	u16			pen_down:1;
> +	u16			disabled:1;
> +	u16			pending:1;
> +	u16			use_pointercal:1;
> +
> +	s16			dav_gpio;
> +	int			(*get_pendown_state)(void);
> +	int			irq;
> +};
> +
> +struct tsc2046 {
> +	struct spi_device	*spi;
> +	struct tsc2046_ts	*ts;
> +};

Why is such a split?

> +
> +static const u16 tsc2046_ts_cmd_data[] = {
> +	START_BYTE, X_CMDWD, Y_CMDWD, Z1_CMDWD, Z2_CMDWD,
> +};
> +
> +static int tsc2046_device_suspended(struct device *dev)
> +{
> +	struct tsc2046 *tsc = dev_get_drvdata(dev);
> +	return dev->power.power_state.event != PM_EVENT_ON || tsc->ts->disabled;
> +}
> +
> +static void tsc2046_update_pen_state(struct tsc2046 *tsc, int x, int y, int pressure)
> +{
> +	struct tsc2046_ts *ts = tsc->ts;
> +
> +	if (x <= ts->old_x - 4 || x >= ts->old_x + 4 ||
> +	    y <= ts->old_y - 4 || y >= ts->old_y + 4 ||
> +	    pressure <= ts->old_pressure - 4 || pressure >= ts->old_pressure + 4)
> +	{
> +		if (pressure) {
> +			if (!ts->use_pointercal || (ts->use_pointercal &&
> +			    x >= 0 &&
> +			    x <= ts->x_res - 1 &&
> +			    y >= 0 &&
> +			    y <= ts->y_res - 1))
> +			{
> +				input_report_abs(ts->idev, ABS_X, x);
> +				input_report_abs(ts->idev, ABS_Y, y);
> +				input_report_abs(ts->idev, ABS_PRESSURE, 1);

Why don't you report true pressure reading instead of clamping to [0,1]
range?

> +				input_report_key(ts->idev, BTN_TOUCH, 1);
> +				ts->pen_down = 1;
> +			} else {
> +				input_report_abs(ts->idev, ABS_PRESSURE, 0);
> +				input_report_key(ts->idev, BTN_TOUCH, 0);
> +				ts->pen_down = 0;
> +			}
> +		} else {
> +			input_report_abs(ts->idev, ABS_PRESSURE, 0);
> +			input_report_key(ts->idev, BTN_TOUCH, 0);
> +			ts->pen_down = 0;
> +		}
> +
> +		ts->old_x = x;
> +		ts->old_y = y;
> +		ts->old_pressure = pressure;
> +
> +		input_sync(ts->idev);
> +
> +#ifdef VERBOSE
> +		dev_dbg(&tsc->spi->dev, "x %4d y %4d p %4d\n", x, y, pressure);
> +#endif

dev_vdbg().


> +	}
> +}
> +
> +/*
> + * This procedure is called by the SPI framework after the coordinates
> + * have been read from TSC2046
> + */
> +static void tsc2046_ts_rx(void *arg)
> +{
> +	struct tsc2046 *tsc = arg;
> +	struct tsc2046_ts *ts = tsc->ts;
> +	unsigned int x, y, z1, z2, pressure;
> +	int xN, yN;
> +
> +	x  = CONV_DATA(ts->data[2], ts->data[3]);
> +	y  = CONV_DATA(ts->data[1], ts->data[2]);
> +	z1 = CONV_DATA(ts->data[3], ts->data[4]);
> +	z2 = CONV_DATA(ts->data[4], 0);
> +
> +	if (z1) {
> +		pressure = ts->x_plate_ohm * x;
> +		pressure /= 4096;
> +		pressure *= z2 - z1;
> +		pressure /= z1;
> +	} else
> +		pressure = 0;
> +
> +	if (ts->use_pointercal) {
> +		/* Transform pen state according to calibration parameters.  This
> +		 * algorithm is the same used by tslib and Qt to do 7-value
> +		 * screen transforms.  See:
> +		 *
> +		 * http://doc.trolltech.com/4.3/qwscalibratedmousehandler.html#transform
> +		 */
> +		xN = ((PA * x) + (PB * y) + PC) / PS;
> +		yN = ((PD * x) + (PE * y) + PF) / PS;
> +
> +		x = (xN < 0) ? 0 : (xN > ts->x_res ? ts->x_res - 1 : (unsigned int)xN);
> +		y = (yN < 0) ? 0 : (yN > ts->y_res ? ts->y_res - 1 : (unsigned int)yN);
> +	}
> +
> +	/* If pressure value is above a preset limit (pen is barely
> +	 * touching the screen) we can't trust the coordinate values.
> +	 */
> +	if (pressure < ts->pressure_limit && x < MAX_12BIT && y < MAX_12BIT) {
> +		ts->pressure_limit = ts->max_pressure;
> +		if (ts->ignore_last) {
> +			if (ts->sample_cnt)
> +				tsc2046_update_pen_state(tsc, ts->x, ts->y, ts->p);
> +			ts->x = x;
> +			ts->y = y;
> +			ts->p = pressure;
> +		} else
> +			tsc2046_update_pen_state(tsc, x, y, pressure);
> +		ts->sample_cnt++;
> +	}
> +
> +	mod_timer(&ts->timer,
> +		  jiffies + msecs_to_jiffies(TSC2046_TS_SCAN_TIME));
> +}
> +
> +static int tsc2046_is_pen_down(struct tsc2046_ts *ts)
> +{
> +	return ts->pen_down;
> +}
> +
> +static int tsc2046_get_pendown_state(struct tsc2046_ts *ts)
> +{
> +	if (ts->get_pendown_state)
> +		return ts->get_pendown_state();
> +
> +	return !gpio_get_value(ts->dav_gpio);
> +}
> +
> +/*
> + * Timer is called every TSC2046_TS_SCAN_TIME when the pen is down
> + */
> +static void tsc2046_ts_timer(unsigned long arg)
> +{
> +	struct tsc2046 *tsc = (void *) arg;
> +	struct tsc2046_ts *ts = tsc->ts;
> +	unsigned long flags;
> +	int ndav;
> +	int r;
> +
> +	spin_lock_irqsave(&ts->lock, flags);
> +	ndav = tsc2046_get_pendown_state(ts);
> +	if (!ndav || tsc2046_device_suspended(&tsc->spi->dev)) {
> +		/* Pen has been lifted */
> +		if (!tsc2046_device_suspended(&tsc->spi->dev) &&
> +		    !ts->irq_enabled)
> +		{
> +			ts->irq_enabled = 1;
> +			enable_irq(ts->irq);
> +		}
> +		tsc2046_update_pen_state(tsc, 0, 0, 0);
> +		ts->pending = 0;
> +		spin_unlock_irqrestore(&ts->lock, flags);
> +
> +	} else {
> +		ts->pen_down = 1;
> +		spin_unlock_irqrestore(&ts->lock, flags);
> +
> +		r = spi_async(tsc->spi, &ts->read_msg);
> +		if (r)
> +			dev_err(&tsc->spi->dev, "ts: spi_async() failed");
> +	}
> +}
> +
> +/*
> + * This interrupt is called when pen is down and first coordinates are
> + * available. That is indicated by a falling edge on DEV line.  IRQ is
> + * disabled here because while the pen is down the coordinates are
> + * read by a timer.
> + */
> +static irqreturn_t tsc2046_ts_irq_handler(int irq, void *dev_id)
> +{
> +	struct tsc2046 *tsc = dev_id;
> +	struct tsc2046_ts *ts = tsc->ts;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&ts->lock, flags);
> +	if (ts->irq_enabled) {
> +		ts->irq_enabled = 0;
> +		disable_irq_nosync(ts->irq);
> +		ts->pending = 1;
> +		ts->pressure_limit = ts->touch_pressure;
> +		ts->sample_cnt = 0;
> +		mod_timer(&ts->timer,
> +			  jiffies + msecs_to_jiffies(TSC2046_TS_SCAN_TIME));
> +	}
> +	spin_unlock_irqrestore(&ts->lock, flags);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/* Must be called with ts->lock held */
> +static void tsc2046_ts_disable(struct tsc2046 *tsc)
> +{
> +	struct tsc2046_ts *ts = tsc->ts;
> +
> +	if (ts->disabled)
> +		return;
> +
> +	ts->disabled = 1;
> +	if (!ts->pending) {
> +		ts->irq_enabled = 0;
> +		disable_irq(ts->irq);
> +	} else {
> +		while (ts->pending) {
> +			spin_unlock_irq(&ts->lock);
> +			msleep(1);
> +			spin_lock_irq(&ts->lock);
> +		}
> +	}
> +}
> +
> +static void tsc2046_ts_enable(struct tsc2046 *tsc)
> +{
> +	struct tsc2046_ts *ts = tsc->ts;
> +
> +	if (!ts->disabled)
> +		return;
> +
> +	ts->disabled = 0;
> +	ts->irq_enabled = 1;
> +	enable_irq(ts->irq);
> +}
> +
> +#ifdef CONFIG_PM
> +int tsc2046_ts_suspend(struct tsc2046 *tsc)
> +{
> +	struct tsc2046_ts *ts = tsc->ts;
> +
> +	spin_lock_irq(&ts->lock);
> +	tsc2046_ts_disable(tsc);
> +	spin_unlock_irq(&ts->lock);
> +
> +	return 0;
> +}
> +
> +void tsc2046_ts_resume(struct tsc2046 *tsc)
> +{
> +	struct tsc2046_ts *ts = tsc->ts;
> +
> +	spin_lock_irq(&ts->lock);
> +	tsc2046_ts_enable(tsc);
> +	spin_unlock_irq(&ts->lock);
> +}
> +#endif
> +
> +static void tsc2046_ts_setup_spi_xfer(struct tsc2046 *tsc)
> +{
> +	struct tsc2046_ts *ts = tsc->ts;
> +	struct spi_message *m = &ts->read_msg;
> +	struct spi_transfer *x = &ts->read_xfer[1];
> +
> +	spi_message_init(m);
> +
> +	/* read and write data in one transaction */
> +	x->tx_buf = &tsc2046_ts_cmd_data;
> +	x->rx_buf = &ts->data;
> +	x->len    = 10;
> +	spi_message_add_tail(x, m);
> +
> +	/* send another START_BYTE to (re)enable pen interrupts */
> +	x++;
> +	x->tx_buf = &tsc2046_ts_cmd_data[0];
> +	x->rx_buf = &ts->data;
> +	x->len = 2;
> +	spi_message_add_tail(x, m);
> +
> +	m->complete = tsc2046_ts_rx;
> +	m->context = tsc;
> +}
> +
> +/* Attribute functions */
> +
> +static ssize_t tsc2046_ts_use_pointercal_show(struct device *dev,
> +				       struct device_attribute *attr, char *buf)
> +{
> +	struct tsc2046		*tsc = dev_get_drvdata(dev);
> +	struct tsc2046_ts	*ts = tsc->ts;
> +
> +	return sprintf(buf, "%u\n", ts->use_pointercal);
> +}
> +
> +static ssize_t tsc2046_ts_use_pointercal_store(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t count)
> +{
> +	struct tsc2046		*tsc = dev_get_drvdata(dev);
> +	struct tsc2046_ts	*ts = tsc->ts;
> +	char *endp;
> +	int i;
> +
> +	i = simple_strtoul(buf, &endp, 10);
> +	spin_lock_irq(&ts->lock);
> +
> +	if (i)
> +		ts->use_pointercal = 1;
> +	else
> +		ts->use_pointercal = 0;
> +
> +	spin_unlock_irq(&ts->lock);
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(use_pointercal, 0644, tsc2046_ts_use_pointercal_show, tsc2046_ts_use_pointercal_store);
> +
> +static ssize_t tsc2046_ts_pointercal_show(struct device *dev,
> +		   struct device_attribute *attr, char *buf)
> +{
> +	struct tsc2046		*tsc = dev_get_drvdata(dev);
> +	struct tsc2046_ts	*ts = tsc->ts;
> +
> +	return sprintf(buf, "%d %d %d %d %d %d %d\n", PA, PB, PC, PD, PE, PF, PS);
> +}
> +
> +static ssize_t tsc2046_ts_pointercal_store(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t count)
> +{
> +	struct tsc2046		*tsc = dev_get_drvdata(dev);
> +	struct tsc2046_ts	*ts = tsc->ts;
> +	char *endp;
> +	int i;
> +	unsigned int index = 0, offset = 0;
> +
> +	spin_lock_irq(&ts->lock);
> +
> +	for (index = 0; index < 7; index++) {
> +		/* Grab the numbers */
> +		i = simple_strtol(buf + offset, &endp, 10);
> +		ts->pointercal[index] = i;
> +
> +		/* Skip the space */
> +		offset = endp - buf + 1;
> +	}
> +
> +	spin_unlock_irq(&ts->lock);
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(pointercal, 0644, tsc2046_ts_pointercal_show, tsc2046_ts_pointercal_store);
> +
> +static ssize_t tsc2046_ts_screen_res_show(struct device *dev,
> +		   struct device_attribute *attr, char *buf)
> +{
> +	struct tsc2046		*tsc = dev_get_drvdata(dev);
> +	struct tsc2046_ts	*ts = tsc->ts;
> +
> +	return sprintf(buf, "%u %u\n", ts->x_res, ts->y_res);
> +}
> +
> +static ssize_t tsc2046_ts_screen_res_store(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t count)
> +{
> +	struct tsc2046		*tsc = dev_get_drvdata(dev);
> +	struct tsc2046_ts	*ts = tsc->ts;
> +	char *endp;
> +	unsigned int i, offset = 0;
> +
> +	spin_lock_irq(&ts->lock);
> +
> +	/* Grab the x res */
> +	i = simple_strtoul(buf, &endp, 10);
> +	ts->x_res = i;
> +
> +	/* Skip the space */
> +	offset = endp - buf + 1;
> +
> +	/* Grab the y res */
> +	i = simple_strtoul(buf + offset, &endp, 10);
> +	ts->y_res = i;
> +
> +	/* Update the device parameters */
> +	input_set_abs_params(ts->idev, ABS_X, 0, ts->x_res - 1, 0, 0);
> +	input_set_abs_params(ts->idev, ABS_Y, 0, ts->y_res - 1, 0, 0);
> +
> +	spin_unlock_irq(&ts->lock);
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(screen_res, 0644, tsc2046_ts_screen_res_show, tsc2046_ts_screen_res_store);
> +
> +static ssize_t tsc2046_ts_pen_down_show(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	struct tsc2046 *tsc = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%u\n", tsc2046_is_pen_down(tsc->ts));
> +}
> +
> +static DEVICE_ATTR(pen_down, S_IRUGO, tsc2046_ts_pen_down_show, NULL);
> +
> +static ssize_t tsc2046_ts_disable_show(struct device *dev,
> +				       struct device_attribute *attr, char *buf)
> +{
> +	struct tsc2046		*tsc = dev_get_drvdata(dev);
> +	struct tsc2046_ts	*ts = tsc->ts;
> +
> +	return sprintf(buf, "%u\n", ts->disabled);
> +}
> +
> +static ssize_t tsc2046_ts_disable_store(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t count)
> +{
> +	struct tsc2046		*tsc = dev_get_drvdata(dev);
> +	struct tsc2046_ts	*ts = tsc->ts;
> +	char *endp;
> +	int i;
> +
> +	i = simple_strtoul(buf, &endp, 10);
> +	spin_lock_irq(&ts->lock);
> +
> +	if (i)
> +		tsc2046_ts_disable(tsc);
> +	else
> +		tsc2046_ts_enable(tsc);
> +
> +	spin_unlock_irq(&ts->lock);
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(disable_ts, 0664, tsc2046_ts_disable_show,
> +		   tsc2046_ts_disable_store);
> +
> +/* Init functions */
> +
> +static int __devinit tsc2046_probe(struct spi_device *spi)
> +{
> +	struct tsc2046			*tsc;
> +	struct tsc2046_ts		*ts;
> +	struct tsc2046_platform_data	*pdata = spi->dev.platform_data;
> +	struct input_dev		*idev;
> +	int r = -ENODEV;
> +
> +	dev_dbg(&spi->dev, "%s\n", __FUNCTION__);
> +
> +	if (!pdata) {
> +		dev_dbg(&spi->dev, "no platform data?\n");
> +		return -ENODEV;
> +	}
> +
> +	tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
> +	if (tsc == NULL)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(&spi->dev, tsc);
> +	tsc->spi = spi;
> +	spi->dev.power.power_state = PMSG_ON;
> +
> +	spi->mode = SPI_MODE_1;
> +	spi->bits_per_word = 16;
> +
> +	/* The max speed might've been defined by the board-specific
> +	 * struct */
> +	if (!spi->max_speed_hz)
> +		spi->max_speed_hz = TSC2046_HZ;
> +	spi_setup(spi);
> +
> +	if (spi->irq < 0) {
> +		dev_err(&tsc->spi->dev, "need DAV IRQ/GPIO");
> +		return -EINVAL;
> +	}
> +
> +	if (!pdata->get_pendown_state && !gpio_is_valid(pdata->gpio_pendown)) {
> +		dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n");
> +		return -EINVAL;
> +	}
> +
> +	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
> +	if (ts == NULL)
> +		return -ENOMEM;
> +	tsc->ts = ts;
> +
> +	if (pdata->get_pendown_state)
> +		ts->get_pendown_state = pdata->get_pendown_state;
> +	else {
> +		ts->dav_gpio = pdata->gpio_pendown;
> +		r = gpio_request(ts->dav_gpio, dev_name(&tsc->spi->dev));
> +		if (r < 0) {
> +			dev_err(&tsc->spi->dev, "unable to get DAV GPIO");
> +			goto err1;
> +		}
> +		gpio_direction_input(ts->dav_gpio);
> +	}
> +
> +	ts->irq = spi->irq;
> +
> +	init_timer(&ts->timer);
> +	ts->timer.data = (unsigned long) tsc;
> +	ts->timer.function = tsc2046_ts_timer;
> +
> +	spin_lock_init(&ts->lock);
> +
> +	ts->x_plate_ohm	= pdata->ts_x_plate_ohm ? : 280;
> +	ts->max_pressure = pdata->ts_max_pressure ? : MAX_12BIT;
> +	ts->touch_pressure = pdata->ts_touch_pressure ? : ts->max_pressure;
> +	ts->ignore_last	= pdata->ts_ignore_last;
> +
> +	idev = input_allocate_device();
> +	if (idev == NULL) {
> +		r = -ENOMEM;
> +		goto err2;
> +	}
> +	idev->name = "TSC2046 touchscreen";
> +	snprintf(ts->phys, sizeof(ts->phys),
> +		 "%s/input-ts", dev_name(&tsc->spi->dev));
> +	idev->phys = ts->phys;
> +
> +	idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
> +	idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
> +	ts->idev = idev;
> +
> +	tsc2046_ts_setup_spi_xfer(tsc);
> +
> +	/* X and Y res default to 240x320 */
> +	ts->x_res = pdata->ts_x_res ? : 240;
> +	ts->y_res = pdata->ts_y_res ? : 320;
> +
> +	input_set_abs_params(idev, ABS_X, 0, ts->x_res - 1, 0, 0);
> +	input_set_abs_params(idev, ABS_Y, 0, ts->y_res - 1, 0, 0);
> +	input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0);
> +
> +	set_bit(BTN_TOUCH, idev->keybit);
> +
> +	r = request_irq(ts->irq, tsc2046_ts_irq_handler,
> +			IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
> +			"tsc2046-ts", tsc);
> +	if (r < 0) {
> +		dev_err(&tsc->spi->dev, "unable to get DAV IRQ");
> +		goto err3;
> +	}
> +	set_irq_wake(ts->irq, 1);
> +	ts->irq_enabled = 1;
> +
> +	/* Create the various driver attributes */
> +	if (device_create_file(&tsc->spi->dev, &dev_attr_pen_down) < 0)
> +		goto err4;
> +	if (device_create_file(&tsc->spi->dev, &dev_attr_disable_ts) < 0)
> +		goto err5;
> +	if (device_create_file(&tsc->spi->dev, &dev_attr_use_pointercal) < 0)
> +		goto err6;
> +	if (device_create_file(&tsc->spi->dev, &dev_attr_pointercal) < 0)
> +		goto err7;
> +	if (device_create_file(&tsc->spi->dev, &dev_attr_screen_res) < 0)
> +		goto err8;

If there are still more than 1 attriute left then anonymous attribute
group should be used. HOwever if you remove pointercal support that
basically leaves disable methid and I am not too keen on having it
either - I believe we need a generic solution for shutting off an
arbitrary device and not somethign that has to be reimplemented in every
driver.

> +
> +	r = input_register_device(idev);
> +	if (r < 0) {
> +		dev_err(&tsc->spi->dev, "can't register touchscreen device\n");
> +		goto err9;
> +	}
> +
> +	/* kick off a transaction to enable pen interrupts */
> +	spi_async(tsc->spi, &ts->read_msg);

Should be done in open() method.

> +
> +	return 0;
> +err9:
> +	device_remove_file(&tsc->spi->dev, &dev_attr_screen_res);
> +err8:
> +	device_remove_file(&tsc->spi->dev, &dev_attr_pointercal);
> +err7:
> +	device_remove_file(&tsc->spi->dev, &dev_attr_use_pointercal);
> +err6:
> +	device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
> +err5:
> +	device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
> +err4:
> +	free_irq(ts->irq, tsc);
> +err3:
> +	input_free_device(idev);
> +err2:
> +	if (ts->dav_gpio)
> +		gpio_free(ts->dav_gpio);
> +err1:
> +	kfree(ts);
> +	return r;
> +}
> +
> +static int __devexit tsc2046_remove(struct spi_device *spi)
> +{
> +	struct tsc2046 *tsc = dev_get_drvdata(&spi->dev);
> +	struct tsc2046_ts *ts = tsc->ts;
> +	unsigned long flags;
> +
> +	dev_dbg(&tsc->spi->dev, "%s\n", __FUNCTION__);
> +
> +	spin_lock_irqsave(&ts->lock, flags);
> +	tsc2046_ts_disable(tsc);
> +	spin_unlock_irqrestore(&ts->lock, flags);
> +
> +	device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
> +	device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
> +	device_remove_file(&tsc->spi->dev, &dev_attr_use_pointercal);
> +	device_remove_file(&tsc->spi->dev, &dev_attr_pointercal);
> +	device_remove_file(&tsc->spi->dev, &dev_attr_screen_res);
> +
> +	free_irq(ts->irq, tsc);
> +	input_unregister_device(ts->idev);
> +
> +	if (ts->dav_gpio)
> +		gpio_free(ts->dav_gpio);
> +
> +	kfree(ts);
> +	kfree(tsc);
> +
> +	return 0;
> +}
> +
> +static struct spi_driver tsc2046_driver = {
> +	.driver = {
> +		   .name = "tsc2046",
> +		   .bus = &spi_bus_type,
> +		   .owner = THIS_MODULE,
> +	},
> +	.probe = tsc2046_probe,
> +	.remove = __devexit_p(tsc2046_remove),
> +};
> +
> +static int __init tsc2046_init(void)
> +{
> +	printk(KERN_INFO "TSC2046 driver initializing\n");
> +
> +	return spi_register_driver(&tsc2046_driver);
> +}
> +
> +static void __exit tsc2046_exit(void)
> +{
> +	spi_unregister_driver(&tsc2046_driver);
> +}
> +
> +module_init(tsc2046_init);
> +module_exit(tsc2046_exit);
> +
> +MODULE_AUTHOR("Kevin Hilman <khilman@mvista.com>");
> +MODULE_LICENSE("GPL");
> +
> diff --git a/include/linux/input/tsc2046.h b/include/linux/input/tsc2046.h
> new file mode 100644
> index 0000000..acb8fed
> --- /dev/null
> +++ b/include/linux/input/tsc2046.h
> @@ -0,0 +1,26 @@
> +#ifndef _LINUX_INPUT_TSC2046_H
> +#define _LINUX_INPUT_TSC2046_H
> +
> +#include <linux/types.h>
> +#include <linux/timer.h>
> +
> +struct tsc2046_platform_data {
> +	int	(*get_pendown_state)(void);
> +	int	gpio_pendown;
> +	s16	gpio_debounce;
> +	u16	ts_x_plate_ohm;
> +	u32	ts_max_pressure;  /* Samples with bigger pressure value will
> +				     be ignored, since the corresponding X, Y
> +				     values are unreliable */
> +	u32	ts_touch_pressure;/* Pressure limit until we report a
> +				     touch event. After that we switch
> +				     to ts_max_pressure. */
> +	u32	ts_x_res;
> +	u32	ts_y_res;
> +	unsigned ts_ignore_last : 1;
> +
> +};
> +
> +#endif
> +
> +

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] Input: Add tsc2046 touchscreen driver
  2010-04-17  8:28 ` Dmitry Torokhov
@ 2010-05-30 20:21   ` Cory Maccarrone
  2010-05-30 21:08     ` [spi-devel-general] " David Brownell
  0 siblings, 1 reply; 7+ messages in thread
From: Cory Maccarrone @ 2010-05-30 20:21 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, spi-devel-general

On Sat, Apr 17, 2010 at 1:28 AM, Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> Hi Cory,
>
> Sorry for not reviewing the driver earlier.
>
> On Sun, Dec 13, 2009 at 12:39:14PM -0800, Cory Maccarrone wrote:
>> This change implements a new driver for the TSC2046-series
>> touchscreen found on many HTC OMAP850-based smartphones.
>> This driver differs from the ADS7846 in that it uses 16-bit
>> commands instead of 8-bit, and does not include temerature
>> or voltage sensing capabilities.
>>
>> This driver was submitted in May of 2007 to the linux-omap
>> list at:
>>
>> http://linux.omap.com/pipermail/linux-omap-open-source/2007-May/009745.html
>>
>> This is a resubmit of this driver, with additions that allows
>> the injection of pointercal parameters from tslib through
>> /sys.  This was done for the Wing Linux project primarily to
>> allow this touchscreen to work with Android, but it's useful for
>> any userspace that doesn't support tslib calibration.  Pointercal
>> use is optional -- it produces raw parameters unless the use_pointercal
>> /sys file is set to 1.
>
> The current poiicy is simply use tslib and push the filtering and
> scaling out of the kernel. I do not see the reason for this driver be an
> exception.
>
>>
>> Though this driver was previously submitted to linux-omap, there
>> is nothing omap-specific in this driver.
>>
>
> Overall I would like to see this being not a standalone driver but
> instead tsc2046 support being plugged into existing tsc2007, extending
> it. There are a few of TSC drivers floating around, all of them very
> similar, with the difference of the transport (i2c vs spi) and register
> sets but all having very similar core interrupt handling, suspend and
> resume algorithms... This is a waste.
>
>> Signed-off-by: Cory Maccarrone <darkstar6262@gmail.com>
>> ---
>>  drivers/input/touchscreen/Kconfig   |   14 +
>>  drivers/input/touchscreen/Makefile  |    1 +
>>  drivers/input/touchscreen/tsc2046.c |  740 +++++++++++++++++++++++++++++++++++
>>  include/linux/input/tsc2046.h       |   26 ++
>>  4 files changed, 781 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/input/touchscreen/tsc2046.c
>>  create mode 100644 include/linux/input/tsc2046.h
>>
>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>> index 8cc453c..1f3273b 100644
>> --- a/drivers/input/touchscreen/Kconfig
>> +++ b/drivers/input/touchscreen/Kconfig
>> @@ -501,6 +501,20 @@ config TOUCHSCREEN_TOUCHIT213
>>         To compile this driver as a module, choose M here: the
>>         module will be called touchit213.
>>
>> +config TOUCHSCREEN_TSC2046
>> +     tristate "TSC2046 based touchscreens"
>> +     depends on SPI_MASTER
>> +     help
>> +       Say Y here if you have a touchscreen interface using the
>> +       TI TSC 2046 controller, and your board-specific initialization
>> +       code includes that in its table of SPI devices.  Also make
>> +       sure the proper SPI controller is selected.
>> +
>> +       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 tsc2046.
>> +
>>  config TOUCHSCREEN_TSC2007
>>       tristate "TSC2007 based touchscreens"
>>       depends on I2C
>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>> index 15fa62c..3c413f7 100644
>> --- a/drivers/input/touchscreen/Makefile
>> +++ b/drivers/input/touchscreen/Makefile
>> @@ -30,6 +30,7 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)        += touchit213.o
>>  obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
>>  obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)   += touchwin.o
>>  obj-$(CONFIG_TOUCHSCREEN_TSC2007)    += tsc2007.o
>> +obj-$(CONFIG_TOUCHSCREEN_TSC2046)    += tsc2046.o
>>  obj-$(CONFIG_TOUCHSCREEN_UCB1400)    += ucb1400_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001)        += wacom_w8001.o
>>  obj-$(CONFIG_TOUCHSCREEN_WM97XX)     += wm97xx-ts.o
>> diff --git a/drivers/input/touchscreen/tsc2046.c b/drivers/input/touchscreen/tsc2046.c
>> new file mode 100644
>> index 0000000..e45d251
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/tsc2046.c
>> @@ -0,0 +1,740 @@
>> +/*
>> + * TSC2046 Touchscreen driver
>> + *
>> + * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com>
>> + *
>> + * Communication details from original TI driver
>> + * Copyright (C) 2004-2005 Texas Instruments, Inc.
>> + *
>> + * Structure based heavily on TSC2301 driver
>> + * Copyright (C) 2005-2006 Nokia Corporation
>> + *
>> + * 2007 (c) MontaVista Software, Inc. This file is licensed under
>> + * the terms of the GNU General Public License version 2. This program
>> + * is licensed "as is" without any warranty of any kind, whether express
>> + * or implied.
>> + *
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/input.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/delay.h>
>> +#include <linux/spi/spi.h>
>> +#include <linux/gpio.h>
>> +
>> +#include <linux/input/tsc2046.h>
>> +
>> +/* TSC2046 commands */
>> +#define START_BYTE           0x8000
>> +#define X_CMDWD                      0xd300
>> +#define Y_CMDWD                      0x9300
>> +#define Z1_CMDWD             0xb300
>> +#define Z2_CMDWD             0xc300
>> +
>> +#define TSC2046_TS_SCAN_TIME 1
>> +#define MAX_12BIT            ((1 << 12) - 1)
>> +
>> +#define MIN_ABS_X    528
>> +#define MAX_ABS_X    3760
>> +#define MIN_ABS_Y    624
>> +#define MAX_ABS_Y    3760
>> +
>> +/* The TSC2046 operates at a maximum speed of 2MHz */
>> +#define TSC2046_HZ   2000000
>> +
>> +#define CONV_DATA(d1, d2) \
>> +  (((d1 & 0x7f) << 5) | ((d2 >> 11) & 0x1f))
>> +
>> +/* Convenience macros for pointercal calibration calculation */
>> +#define PA ts->pointercal[0]
>> +#define PB ts->pointercal[1]
>> +#define PC ts->pointercal[2]
>> +#define PD ts->pointercal[3]
>> +#define PE ts->pointercal[4]
>> +#define PF ts->pointercal[5]
>> +#define PS ts->pointercal[6]
>> +
>> +struct tsc2046_ts {
>> +     struct input_dev        *idev;
>> +     char                    phys[32];
>> +     struct timer_list       timer;
>> +     spinlock_t              lock;
>> +
>> +     struct spi_transfer     read_xfer[3];
>> +     struct spi_message      read_msg;
>> +     struct spi_message      enable_msg;
>> +     u16                     data[5];
>> +
>> +     u16                     x;
>> +     u16                     y;
>> +     u16                     p;
>> +     u16                     old_x;
>> +     u16                     old_y;
>> +     u16                     old_pressure;
>> +     int                     sample_cnt;
>> +
>> +     int                     ignore_last : 1;
>> +     u16                     x_plate_ohm;
>> +     int                     max_pressure;
>> +     int                     touch_pressure;
>> +     int                     pressure_limit;
>> +
>> +     int                     pointercal[7];
>> +     u16                     x_res;
>> +     u16                     y_res;
>> +
>> +     u16                     irq_enabled:1;
>> +     u16                     pen_down:1;
>> +     u16                     disabled:1;
>> +     u16                     pending:1;
>> +     u16                     use_pointercal:1;
>> +
>> +     s16                     dav_gpio;
>> +     int                     (*get_pendown_state)(void);
>> +     int                     irq;
>> +};
>> +
>> +struct tsc2046 {
>> +     struct spi_device       *spi;
>> +     struct tsc2046_ts       *ts;
>> +};
>
> Why is such a split?
>
>> +
>> +static const u16 tsc2046_ts_cmd_data[] = {
>> +     START_BYTE, X_CMDWD, Y_CMDWD, Z1_CMDWD, Z2_CMDWD,
>> +};
>> +
>> +static int tsc2046_device_suspended(struct device *dev)
>> +{
>> +     struct tsc2046 *tsc = dev_get_drvdata(dev);
>> +     return dev->power.power_state.event != PM_EVENT_ON || tsc->ts->disabled;
>> +}
>> +
>> +static void tsc2046_update_pen_state(struct tsc2046 *tsc, int x, int y, int pressure)
>> +{
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +
>> +     if (x <= ts->old_x - 4 || x >= ts->old_x + 4 ||
>> +         y <= ts->old_y - 4 || y >= ts->old_y + 4 ||
>> +         pressure <= ts->old_pressure - 4 || pressure >= ts->old_pressure + 4)
>> +     {
>> +             if (pressure) {
>> +                     if (!ts->use_pointercal || (ts->use_pointercal &&
>> +                         x >= 0 &&
>> +                         x <= ts->x_res - 1 &&
>> +                         y >= 0 &&
>> +                         y <= ts->y_res - 1))
>> +                     {
>> +                             input_report_abs(ts->idev, ABS_X, x);
>> +                             input_report_abs(ts->idev, ABS_Y, y);
>> +                             input_report_abs(ts->idev, ABS_PRESSURE, 1);
>
> Why don't you report true pressure reading instead of clamping to [0,1]
> range?
>
>> +                             input_report_key(ts->idev, BTN_TOUCH, 1);
>> +                             ts->pen_down = 1;
>> +                     } else {
>> +                             input_report_abs(ts->idev, ABS_PRESSURE, 0);
>> +                             input_report_key(ts->idev, BTN_TOUCH, 0);
>> +                             ts->pen_down = 0;
>> +                     }
>> +             } else {
>> +                     input_report_abs(ts->idev, ABS_PRESSURE, 0);
>> +                     input_report_key(ts->idev, BTN_TOUCH, 0);
>> +                     ts->pen_down = 0;
>> +             }
>> +
>> +             ts->old_x = x;
>> +             ts->old_y = y;
>> +             ts->old_pressure = pressure;
>> +
>> +             input_sync(ts->idev);
>> +
>> +#ifdef VERBOSE
>> +             dev_dbg(&tsc->spi->dev, "x %4d y %4d p %4d\n", x, y, pressure);
>> +#endif
>
> dev_vdbg().
>
>
>> +     }
>> +}
>> +
>> +/*
>> + * This procedure is called by the SPI framework after the coordinates
>> + * have been read from TSC2046
>> + */
>> +static void tsc2046_ts_rx(void *arg)
>> +{
>> +     struct tsc2046 *tsc = arg;
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +     unsigned int x, y, z1, z2, pressure;
>> +     int xN, yN;
>> +
>> +     x  = CONV_DATA(ts->data[2], ts->data[3]);
>> +     y  = CONV_DATA(ts->data[1], ts->data[2]);
>> +     z1 = CONV_DATA(ts->data[3], ts->data[4]);
>> +     z2 = CONV_DATA(ts->data[4], 0);
>> +
>> +     if (z1) {
>> +             pressure = ts->x_plate_ohm * x;
>> +             pressure /= 4096;
>> +             pressure *= z2 - z1;
>> +             pressure /= z1;
>> +     } else
>> +             pressure = 0;
>> +
>> +     if (ts->use_pointercal) {
>> +             /* Transform pen state according to calibration parameters.  This
>> +              * algorithm is the same used by tslib and Qt to do 7-value
>> +              * screen transforms.  See:
>> +              *
>> +              * http://doc.trolltech.com/4.3/qwscalibratedmousehandler.html#transform
>> +              */
>> +             xN = ((PA * x) + (PB * y) + PC) / PS;
>> +             yN = ((PD * x) + (PE * y) + PF) / PS;
>> +
>> +             x = (xN < 0) ? 0 : (xN > ts->x_res ? ts->x_res - 1 : (unsigned int)xN);
>> +             y = (yN < 0) ? 0 : (yN > ts->y_res ? ts->y_res - 1 : (unsigned int)yN);
>> +     }
>> +
>> +     /* If pressure value is above a preset limit (pen is barely
>> +      * touching the screen) we can't trust the coordinate values.
>> +      */
>> +     if (pressure < ts->pressure_limit && x < MAX_12BIT && y < MAX_12BIT) {
>> +             ts->pressure_limit = ts->max_pressure;
>> +             if (ts->ignore_last) {
>> +                     if (ts->sample_cnt)
>> +                             tsc2046_update_pen_state(tsc, ts->x, ts->y, ts->p);
>> +                     ts->x = x;
>> +                     ts->y = y;
>> +                     ts->p = pressure;
>> +             } else
>> +                     tsc2046_update_pen_state(tsc, x, y, pressure);
>> +             ts->sample_cnt++;
>> +     }
>> +
>> +     mod_timer(&ts->timer,
>> +               jiffies + msecs_to_jiffies(TSC2046_TS_SCAN_TIME));
>> +}
>> +
>> +static int tsc2046_is_pen_down(struct tsc2046_ts *ts)
>> +{
>> +     return ts->pen_down;
>> +}
>> +
>> +static int tsc2046_get_pendown_state(struct tsc2046_ts *ts)
>> +{
>> +     if (ts->get_pendown_state)
>> +             return ts->get_pendown_state();
>> +
>> +     return !gpio_get_value(ts->dav_gpio);
>> +}
>> +
>> +/*
>> + * Timer is called every TSC2046_TS_SCAN_TIME when the pen is down
>> + */
>> +static void tsc2046_ts_timer(unsigned long arg)
>> +{
>> +     struct tsc2046 *tsc = (void *) arg;
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +     unsigned long flags;
>> +     int ndav;
>> +     int r;
>> +
>> +     spin_lock_irqsave(&ts->lock, flags);
>> +     ndav = tsc2046_get_pendown_state(ts);
>> +     if (!ndav || tsc2046_device_suspended(&tsc->spi->dev)) {
>> +             /* Pen has been lifted */
>> +             if (!tsc2046_device_suspended(&tsc->spi->dev) &&
>> +                 !ts->irq_enabled)
>> +             {
>> +                     ts->irq_enabled = 1;
>> +                     enable_irq(ts->irq);
>> +             }
>> +             tsc2046_update_pen_state(tsc, 0, 0, 0);
>> +             ts->pending = 0;
>> +             spin_unlock_irqrestore(&ts->lock, flags);
>> +
>> +     } else {
>> +             ts->pen_down = 1;
>> +             spin_unlock_irqrestore(&ts->lock, flags);
>> +
>> +             r = spi_async(tsc->spi, &ts->read_msg);
>> +             if (r)
>> +                     dev_err(&tsc->spi->dev, "ts: spi_async() failed");
>> +     }
>> +}
>> +
>> +/*
>> + * This interrupt is called when pen is down and first coordinates are
>> + * available. That is indicated by a falling edge on DEV line.  IRQ is
>> + * disabled here because while the pen is down the coordinates are
>> + * read by a timer.
>> + */
>> +static irqreturn_t tsc2046_ts_irq_handler(int irq, void *dev_id)
>> +{
>> +     struct tsc2046 *tsc = dev_id;
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +     unsigned long flags;
>> +
>> +     spin_lock_irqsave(&ts->lock, flags);
>> +     if (ts->irq_enabled) {
>> +             ts->irq_enabled = 0;
>> +             disable_irq_nosync(ts->irq);
>> +             ts->pending = 1;
>> +             ts->pressure_limit = ts->touch_pressure;
>> +             ts->sample_cnt = 0;
>> +             mod_timer(&ts->timer,
>> +                       jiffies + msecs_to_jiffies(TSC2046_TS_SCAN_TIME));
>> +     }
>> +     spin_unlock_irqrestore(&ts->lock, flags);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +/* Must be called with ts->lock held */
>> +static void tsc2046_ts_disable(struct tsc2046 *tsc)
>> +{
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +
>> +     if (ts->disabled)
>> +             return;
>> +
>> +     ts->disabled = 1;
>> +     if (!ts->pending) {
>> +             ts->irq_enabled = 0;
>> +             disable_irq(ts->irq);
>> +     } else {
>> +             while (ts->pending) {
>> +                     spin_unlock_irq(&ts->lock);
>> +                     msleep(1);
>> +                     spin_lock_irq(&ts->lock);
>> +             }
>> +     }
>> +}
>> +
>> +static void tsc2046_ts_enable(struct tsc2046 *tsc)
>> +{
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +
>> +     if (!ts->disabled)
>> +             return;
>> +
>> +     ts->disabled = 0;
>> +     ts->irq_enabled = 1;
>> +     enable_irq(ts->irq);
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +int tsc2046_ts_suspend(struct tsc2046 *tsc)
>> +{
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +
>> +     spin_lock_irq(&ts->lock);
>> +     tsc2046_ts_disable(tsc);
>> +     spin_unlock_irq(&ts->lock);
>> +
>> +     return 0;
>> +}
>> +
>> +void tsc2046_ts_resume(struct tsc2046 *tsc)
>> +{
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +
>> +     spin_lock_irq(&ts->lock);
>> +     tsc2046_ts_enable(tsc);
>> +     spin_unlock_irq(&ts->lock);
>> +}
>> +#endif
>> +
>> +static void tsc2046_ts_setup_spi_xfer(struct tsc2046 *tsc)
>> +{
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +     struct spi_message *m = &ts->read_msg;
>> +     struct spi_transfer *x = &ts->read_xfer[1];
>> +
>> +     spi_message_init(m);
>> +
>> +     /* read and write data in one transaction */
>> +     x->tx_buf = &tsc2046_ts_cmd_data;
>> +     x->rx_buf = &ts->data;
>> +     x->len    = 10;
>> +     spi_message_add_tail(x, m);
>> +
>> +     /* send another START_BYTE to (re)enable pen interrupts */
>> +     x++;
>> +     x->tx_buf = &tsc2046_ts_cmd_data[0];
>> +     x->rx_buf = &ts->data;
>> +     x->len = 2;
>> +     spi_message_add_tail(x, m);
>> +
>> +     m->complete = tsc2046_ts_rx;
>> +     m->context = tsc;
>> +}
>> +
>> +/* Attribute functions */
>> +
>> +static ssize_t tsc2046_ts_use_pointercal_show(struct device *dev,
>> +                                    struct device_attribute *attr, char *buf)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +
>> +     return sprintf(buf, "%u\n", ts->use_pointercal);
>> +}
>> +
>> +static ssize_t tsc2046_ts_use_pointercal_store(struct device *dev,
>> +                                     struct device_attribute *attr,
>> +                                     const char *buf, size_t count)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +     char *endp;
>> +     int i;
>> +
>> +     i = simple_strtoul(buf, &endp, 10);
>> +     spin_lock_irq(&ts->lock);
>> +
>> +     if (i)
>> +             ts->use_pointercal = 1;
>> +     else
>> +             ts->use_pointercal = 0;
>> +
>> +     spin_unlock_irq(&ts->lock);
>> +
>> +     return count;
>> +}
>> +
>> +static DEVICE_ATTR(use_pointercal, 0644, tsc2046_ts_use_pointercal_show, tsc2046_ts_use_pointercal_store);
>> +
>> +static ssize_t tsc2046_ts_pointercal_show(struct device *dev,
>> +                struct device_attribute *attr, char *buf)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +
>> +     return sprintf(buf, "%d %d %d %d %d %d %d\n", PA, PB, PC, PD, PE, PF, PS);
>> +}
>> +
>> +static ssize_t tsc2046_ts_pointercal_store(struct device *dev,
>> +                                     struct device_attribute *attr,
>> +                                     const char *buf, size_t count)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +     char *endp;
>> +     int i;
>> +     unsigned int index = 0, offset = 0;
>> +
>> +     spin_lock_irq(&ts->lock);
>> +
>> +     for (index = 0; index < 7; index++) {
>> +             /* Grab the numbers */
>> +             i = simple_strtol(buf + offset, &endp, 10);
>> +             ts->pointercal[index] = i;
>> +
>> +             /* Skip the space */
>> +             offset = endp - buf + 1;
>> +     }
>> +
>> +     spin_unlock_irq(&ts->lock);
>> +
>> +     return count;
>> +}
>> +
>> +static DEVICE_ATTR(pointercal, 0644, tsc2046_ts_pointercal_show, tsc2046_ts_pointercal_store);
>> +
>> +static ssize_t tsc2046_ts_screen_res_show(struct device *dev,
>> +                struct device_attribute *attr, char *buf)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +
>> +     return sprintf(buf, "%u %u\n", ts->x_res, ts->y_res);
>> +}
>> +
>> +static ssize_t tsc2046_ts_screen_res_store(struct device *dev,
>> +                                     struct device_attribute *attr,
>> +                                     const char *buf, size_t count)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +     char *endp;
>> +     unsigned int i, offset = 0;
>> +
>> +     spin_lock_irq(&ts->lock);
>> +
>> +     /* Grab the x res */
>> +     i = simple_strtoul(buf, &endp, 10);
>> +     ts->x_res = i;
>> +
>> +     /* Skip the space */
>> +     offset = endp - buf + 1;
>> +
>> +     /* Grab the y res */
>> +     i = simple_strtoul(buf + offset, &endp, 10);
>> +     ts->y_res = i;
>> +
>> +     /* Update the device parameters */
>> +     input_set_abs_params(ts->idev, ABS_X, 0, ts->x_res - 1, 0, 0);
>> +     input_set_abs_params(ts->idev, ABS_Y, 0, ts->y_res - 1, 0, 0);
>> +
>> +     spin_unlock_irq(&ts->lock);
>> +
>> +     return count;
>> +}
>> +
>> +static DEVICE_ATTR(screen_res, 0644, tsc2046_ts_screen_res_show, tsc2046_ts_screen_res_store);
>> +
>> +static ssize_t tsc2046_ts_pen_down_show(struct device *dev,
>> +                                     struct device_attribute *attr,
>> +                                     char *buf)
>> +{
>> +     struct tsc2046 *tsc = dev_get_drvdata(dev);
>> +
>> +     return sprintf(buf, "%u\n", tsc2046_is_pen_down(tsc->ts));
>> +}
>> +
>> +static DEVICE_ATTR(pen_down, S_IRUGO, tsc2046_ts_pen_down_show, NULL);
>> +
>> +static ssize_t tsc2046_ts_disable_show(struct device *dev,
>> +                                    struct device_attribute *attr, char *buf)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +
>> +     return sprintf(buf, "%u\n", ts->disabled);
>> +}
>> +
>> +static ssize_t tsc2046_ts_disable_store(struct device *dev,
>> +                                     struct device_attribute *attr,
>> +                                     const char *buf, size_t count)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +     char *endp;
>> +     int i;
>> +
>> +     i = simple_strtoul(buf, &endp, 10);
>> +     spin_lock_irq(&ts->lock);
>> +
>> +     if (i)
>> +             tsc2046_ts_disable(tsc);
>> +     else
>> +             tsc2046_ts_enable(tsc);
>> +
>> +     spin_unlock_irq(&ts->lock);
>> +
>> +     return count;
>> +}
>> +
>> +static DEVICE_ATTR(disable_ts, 0664, tsc2046_ts_disable_show,
>> +                tsc2046_ts_disable_store);
>> +
>> +/* Init functions */
>> +
>> +static int __devinit tsc2046_probe(struct spi_device *spi)
>> +{
>> +     struct tsc2046                  *tsc;
>> +     struct tsc2046_ts               *ts;
>> +     struct tsc2046_platform_data    *pdata = spi->dev.platform_data;
>> +     struct input_dev                *idev;
>> +     int r = -ENODEV;
>> +
>> +     dev_dbg(&spi->dev, "%s\n", __FUNCTION__);
>> +
>> +     if (!pdata) {
>> +             dev_dbg(&spi->dev, "no platform data?\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
>> +     if (tsc == NULL)
>> +             return -ENOMEM;
>> +
>> +     dev_set_drvdata(&spi->dev, tsc);
>> +     tsc->spi = spi;
>> +     spi->dev.power.power_state = PMSG_ON;
>> +
>> +     spi->mode = SPI_MODE_1;
>> +     spi->bits_per_word = 16;
>> +
>> +     /* The max speed might've been defined by the board-specific
>> +      * struct */
>> +     if (!spi->max_speed_hz)
>> +             spi->max_speed_hz = TSC2046_HZ;
>> +     spi_setup(spi);
>> +
>> +     if (spi->irq < 0) {
>> +             dev_err(&tsc->spi->dev, "need DAV IRQ/GPIO");
>> +             return -EINVAL;
>> +     }
>> +
>> +     if (!pdata->get_pendown_state && !gpio_is_valid(pdata->gpio_pendown)) {
>> +             dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     ts = kzalloc(sizeof(*ts), GFP_KERNEL);
>> +     if (ts == NULL)
>> +             return -ENOMEM;
>> +     tsc->ts = ts;
>> +
>> +     if (pdata->get_pendown_state)
>> +             ts->get_pendown_state = pdata->get_pendown_state;
>> +     else {
>> +             ts->dav_gpio = pdata->gpio_pendown;
>> +             r = gpio_request(ts->dav_gpio, dev_name(&tsc->spi->dev));
>> +             if (r < 0) {
>> +                     dev_err(&tsc->spi->dev, "unable to get DAV GPIO");
>> +                     goto err1;
>> +             }
>> +             gpio_direction_input(ts->dav_gpio);
>> +     }
>> +
>> +     ts->irq = spi->irq;
>> +
>> +     init_timer(&ts->timer);
>> +     ts->timer.data = (unsigned long) tsc;
>> +     ts->timer.function = tsc2046_ts_timer;
>> +
>> +     spin_lock_init(&ts->lock);
>> +
>> +     ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280;
>> +     ts->max_pressure = pdata->ts_max_pressure ? : MAX_12BIT;
>> +     ts->touch_pressure = pdata->ts_touch_pressure ? : ts->max_pressure;
>> +     ts->ignore_last = pdata->ts_ignore_last;
>> +
>> +     idev = input_allocate_device();
>> +     if (idev == NULL) {
>> +             r = -ENOMEM;
>> +             goto err2;
>> +     }
>> +     idev->name = "TSC2046 touchscreen";
>> +     snprintf(ts->phys, sizeof(ts->phys),
>> +              "%s/input-ts", dev_name(&tsc->spi->dev));
>> +     idev->phys = ts->phys;
>> +
>> +     idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
>> +     idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
>> +     ts->idev = idev;
>> +
>> +     tsc2046_ts_setup_spi_xfer(tsc);
>> +
>> +     /* X and Y res default to 240x320 */
>> +     ts->x_res = pdata->ts_x_res ? : 240;
>> +     ts->y_res = pdata->ts_y_res ? : 320;
>> +
>> +     input_set_abs_params(idev, ABS_X, 0, ts->x_res - 1, 0, 0);
>> +     input_set_abs_params(idev, ABS_Y, 0, ts->y_res - 1, 0, 0);
>> +     input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0);
>> +
>> +     set_bit(BTN_TOUCH, idev->keybit);
>> +
>> +     r = request_irq(ts->irq, tsc2046_ts_irq_handler,
>> +                     IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
>> +                     "tsc2046-ts", tsc);
>> +     if (r < 0) {
>> +             dev_err(&tsc->spi->dev, "unable to get DAV IRQ");
>> +             goto err3;
>> +     }
>> +     set_irq_wake(ts->irq, 1);
>> +     ts->irq_enabled = 1;
>> +
>> +     /* Create the various driver attributes */
>> +     if (device_create_file(&tsc->spi->dev, &dev_attr_pen_down) < 0)
>> +             goto err4;
>> +     if (device_create_file(&tsc->spi->dev, &dev_attr_disable_ts) < 0)
>> +             goto err5;
>> +     if (device_create_file(&tsc->spi->dev, &dev_attr_use_pointercal) < 0)
>> +             goto err6;
>> +     if (device_create_file(&tsc->spi->dev, &dev_attr_pointercal) < 0)
>> +             goto err7;
>> +     if (device_create_file(&tsc->spi->dev, &dev_attr_screen_res) < 0)
>> +             goto err8;
>
> If there are still more than 1 attriute left then anonymous attribute
> group should be used. HOwever if you remove pointercal support that
> basically leaves disable methid and I am not too keen on having it
> either - I believe we need a generic solution for shutting off an
> arbitrary device and not somethign that has to be reimplemented in every
> driver.
>
>> +
>> +     r = input_register_device(idev);
>> +     if (r < 0) {
>> +             dev_err(&tsc->spi->dev, "can't register touchscreen device\n");
>> +             goto err9;
>> +     }
>> +
>> +     /* kick off a transaction to enable pen interrupts */
>> +     spi_async(tsc->spi, &ts->read_msg);
>
> Should be done in open() method.
>
>> +
>> +     return 0;
>> +err9:
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_screen_res);
>> +err8:
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_pointercal);
>> +err7:
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_use_pointercal);
>> +err6:
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
>> +err5:
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
>> +err4:
>> +     free_irq(ts->irq, tsc);
>> +err3:
>> +     input_free_device(idev);
>> +err2:
>> +     if (ts->dav_gpio)
>> +             gpio_free(ts->dav_gpio);
>> +err1:
>> +     kfree(ts);
>> +     return r;
>> +}
>> +
>> +static int __devexit tsc2046_remove(struct spi_device *spi)
>> +{
>> +     struct tsc2046 *tsc = dev_get_drvdata(&spi->dev);
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +     unsigned long flags;
>> +
>> +     dev_dbg(&tsc->spi->dev, "%s\n", __FUNCTION__);
>> +
>> +     spin_lock_irqsave(&ts->lock, flags);
>> +     tsc2046_ts_disable(tsc);
>> +     spin_unlock_irqrestore(&ts->lock, flags);
>> +
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_use_pointercal);
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_pointercal);
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_screen_res);
>> +
>> +     free_irq(ts->irq, tsc);
>> +     input_unregister_device(ts->idev);
>> +
>> +     if (ts->dav_gpio)
>> +             gpio_free(ts->dav_gpio);
>> +
>> +     kfree(ts);
>> +     kfree(tsc);
>> +
>> +     return 0;
>> +}
>> +
>> +static struct spi_driver tsc2046_driver = {
>> +     .driver = {
>> +                .name = "tsc2046",
>> +                .bus = &spi_bus_type,
>> +                .owner = THIS_MODULE,
>> +     },
>> +     .probe = tsc2046_probe,
>> +     .remove = __devexit_p(tsc2046_remove),
>> +};
>> +
>> +static int __init tsc2046_init(void)
>> +{
>> +     printk(KERN_INFO "TSC2046 driver initializing\n");
>> +
>> +     return spi_register_driver(&tsc2046_driver);
>> +}
>> +
>> +static void __exit tsc2046_exit(void)
>> +{
>> +     spi_unregister_driver(&tsc2046_driver);
>> +}
>> +
>> +module_init(tsc2046_init);
>> +module_exit(tsc2046_exit);
>> +
>> +MODULE_AUTHOR("Kevin Hilman <khilman@mvista.com>");
>> +MODULE_LICENSE("GPL");
>> +
>> diff --git a/include/linux/input/tsc2046.h b/include/linux/input/tsc2046.h
>> new file mode 100644
>> index 0000000..acb8fed
>> --- /dev/null
>> +++ b/include/linux/input/tsc2046.h
>> @@ -0,0 +1,26 @@
>> +#ifndef _LINUX_INPUT_TSC2046_H
>> +#define _LINUX_INPUT_TSC2046_H
>> +
>> +#include <linux/types.h>
>> +#include <linux/timer.h>
>> +
>> +struct tsc2046_platform_data {
>> +     int     (*get_pendown_state)(void);
>> +     int     gpio_pendown;
>> +     s16     gpio_debounce;
>> +     u16     ts_x_plate_ohm;
>> +     u32     ts_max_pressure;  /* Samples with bigger pressure value will
>> +                                  be ignored, since the corresponding X, Y
>> +                                  values are unreliable */
>> +     u32     ts_touch_pressure;/* Pressure limit until we report a
>> +                                  touch event. After that we switch
>> +                                  to ts_max_pressure. */
>> +     u32     ts_x_res;
>> +     u32     ts_y_res;
>> +     unsigned ts_ignore_last : 1;
>> +
>> +};
>> +
>> +#endif
>> +
>> +
>
> Thanks.
>
> --
> Dmitry
>

Turns out the ads7846 driver works fine for this hardware -- there was
a bug in the SPI100k driver we were using that caused a problem with
it.  With that fixed, there's no more need for this driver.

Thanks for reviewing it.
- Cory
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [spi-devel-general] [PATCH] Input: Add tsc2046 touchscreen driver
  2010-05-30 20:21   ` Cory Maccarrone
@ 2010-05-30 21:08     ` David Brownell
  2010-05-30 21:15       ` Cory Maccarrone
  0 siblings, 1 reply; 7+ messages in thread
From: David Brownell @ 2010-05-30 21:08 UTC (permalink / raw)
  To: Dmitry Torokhov, Cory Maccarrone; +Cc: spi-devel-general, linux-input

--- On Sun, 5/30/10, Cory Maccarrone <darkstar6262@gmail.com> wrote:

I don't see the point in having a separate driver
for this chip ... it's just an updated ADS7846,
and the ads7846 driver has handled it for quite
a few years now, as I recall.  There's no need for
 second driver.

> >> This driver differs from the ADS7846 in that it
> >> uses 16-bit commands instead of 8-bit,

The reason the ads7846 code uses 8-bit messaging
is portability ... it works with SPI controllers
which don't support 16-bit words.  The commands are
of necessity 16-bits.  Using 16-bit words means this
driver won't run on as many systems; folk will still
need to use the ads7846 driver with tsc2046 chips..
 


 and does not include
> temerature
> >> or voltage sensing capabilities.

Another reason not to have a separate driver: this
one is less capable (as well as less portable)


> >> additions that allow
> >> the injection of pointercal parameters from tslib through /sys. 

Something the ads7846 driver could benefit from
too ... again, two drivers is counterproductive.

- Dave


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [spi-devel-general] [PATCH] Input: Add tsc2046 touchscreen driver
  2010-05-30 21:08     ` [spi-devel-general] " David Brownell
@ 2010-05-30 21:15       ` Cory Maccarrone
  0 siblings, 0 replies; 7+ messages in thread
From: Cory Maccarrone @ 2010-05-30 21:15 UTC (permalink / raw)
  To: David Brownell; +Cc: Dmitry Torokhov, spi-devel-general, linux-input

On Sun, May 30, 2010 at 2:08 PM, David Brownell <david-b@pacbell.net> wrote:
> --- On Sun, 5/30/10, Cory Maccarrone <darkstar6262@gmail.com> wrote:
>
> I don't see the point in having a separate driver
> for this chip ... it's just an updated ADS7846,
> and the ads7846 driver has handled it for quite
> a few years now, as I recall.  There's no need for
>  second driver.
>
>> >> This driver differs from the ADS7846 in that it
>> >> uses 16-bit commands instead of 8-bit,
>
> The reason the ads7846 code uses 8-bit messaging
> is portability ... it works with SPI controllers
> which don't support 16-bit words.  The commands are
> of necessity 16-bits.  Using 16-bit words means this
> driver won't run on as many systems; folk will still
> need to use the ads7846 driver with tsc2046 chips..
>
>
>
>  and does not include
>> temerature
>> >> or voltage sensing capabilities.
>
> Another reason not to have a separate driver: this
> one is less capable (as well as less portable)
>
>
>> >> additions that allow
>> >> the injection of pointercal parameters from tslib through /sys.
>
> Something the ads7846 driver could benefit from
> too ... again, two drivers is counterproductive.
>
> - Dave
>
>

I totally agree, thus my previous message.  This submitted patch is no
longer relevant.

- Cory
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2010-05-30 21:15 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-12-13 20:39 [PATCH] Input: Add tsc2046 touchscreen driver Cory Maccarrone
     [not found] ` <1260736754-374-1-git-send-email-darkstar6262-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2010-01-10 23:07   ` Cory Maccarrone
2010-03-07 17:54     ` Cory Maccarrone
2010-04-17  8:28 ` Dmitry Torokhov
2010-05-30 20:21   ` Cory Maccarrone
2010-05-30 21:08     ` [spi-devel-general] " David Brownell
2010-05-30 21:15       ` Cory Maccarrone

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).