From d3339ebf194efc5a6c9f53686cc03ce7cc7f0def Mon Sep 17 00:00:00 2001
From: Theodore A. Roth <troth@openavr.org>
Date: Fri, 15 May 2009 11:07:06 -0700
Subject: [PATCH] Add touchscreen support for ts7350/ts7370/ts7390 boards.

* This was pulled directly from the 2.6.21 kernel from Technologic Systems.
  Minor modifications were made to port to 2.6.29.1 kernel.
---
 drivers/input/touchscreen/Kconfig  |   37 +++++
 drivers/input/touchscreen/Makefile |    1 +
 drivers/input/touchscreen/ts_lcd.c |  301 ++++++++++++++++++++++++++++++++++++
 3 files changed, 339 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/touchscreen/ts_lcd.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index bb6486a..f8a034c 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -151,6 +151,43 @@ config TOUCHSCREEN_MK712
 	  To compile this driver as a module, choose M here: the
 	  module will be called mk712.
 
+config TOUCHSCREEN_TSLCD
+	tristate "TS-LCD"
+	help
+	  Say Y here if you have the TS-LCD.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tslcd.
+
+config TOUCHSCREEN_TSLCD_SWAPXY
+	bool "Swap X/Y axes"
+	depends on TOUCHSCREEN_TSLCD
+	help
+	  Select this if the X and Y axes are reversed from the
+	  expected polarity.  This option is provided for compiling
+	  the TSLCD driver into the kernel; when loading as a module
+	  you can use the swapxy parameter to override this default.
+
+config TOUCHSCREEN_TSLCD_NEGX
+	bool "Reverse X"
+	depends on TOUCHSCREEN_TSLCD
+	help
+	  Select this if the X axis is backwards. This option is provided
+	  for compiling the TSLCD driver into the kernel; when loading
+	  as a module you can use the swapxy parameter to override this
+	  default.
+
+config TOUCHSCREEN_TSLCD_NEGY
+	bool "Reverse Y"
+	depends on TOUCHSCREEN_TSLCD
+	help
+	  Select this if the Y axis is backwards. This option is provided
+	  for compiling the TSLCD driver into the kernel; when loading
+	  as a module you can use the swapxy parameter to override this
+	  default.
+
 config TOUCHSCREEN_HP600
 	tristate "HP Jornada 6xx touchscreen"
 	depends on SH_HP6XX && SH_ADC
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index d3375af..fe65855 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -34,3 +34,4 @@ wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705)	+= wm9705.o
 wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712)	+= wm9712.o
 wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713)	+= wm9713.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)	+= mainstone-wm97xx.o
+obj-$(CONFIG_TOUCHSCREEN_TSLCD)		+= ts_lcd.o
diff --git a/drivers/input/touchscreen/ts_lcd.c b/drivers/input/touchscreen/ts_lcd.c
new file mode 100644
index 0000000..82e98f4
--- /dev/null
+++ b/drivers/input/touchscreen/ts_lcd.c
@@ -0,0 +1,301 @@
+/*
+ * TS-LCD touchscreen controller driver
+ *
+ * adapted from mk712.c
+ * Copyright (c) 1999-2002 Transmeta Corporation
+ * Copyright (c) 2005 Rick Koch <n1gp@hotmail.com>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * This driver supports the TS-LCD
+ */
+
+/*
+ * 1999-12-18: original version, Daniel Quinlan
+ * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll
+ *             to use queue_empty, Nathan Laredo
+ * 1999-12-20: improved random point rejection, Nathan Laredo
+ * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed
+ *             queue code, added module options, other fixes, Daniel Quinlan
+ * 2002-03-15: Clean up for kernel merge <alan@redhat.com>
+ *             Fixed multi open race, fixed memory checks, fixed resource
+ *             allocation, fixed close/powerdown bug, switched to new init
+ * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch
+ * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik
+ * 2008-02-05: adapted from mk712.c for the TS-LCD, Michael Schmidt
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Daniel Quinlan <quinlan@pathname.com>, Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("TS-LCD");
+MODULE_LICENSE("GPL");
+
+static struct input_dev *tslcd_dev;
+static DEFINE_SPINLOCK(tslcd_lock);
+static struct timer_list timer;
+static int timer_started = 0;
+
+static int swapxy =
+#ifdef TOUCHSCREEN_TSLCD_SWAPXY
+			1;
+#else
+			0;
+#endif
+static int negx =
+#ifdef TOUCHSCREEN_TSLCD_NEGX
+			1;
+#else
+			0;
+#endif
+static int negy =
+#ifdef TOUCHSCREEN_TSLCD_NEGY
+			1;
+#else
+			0;
+#endif
+
+
+#define TSLCD_NOTOUCH 0xFFFF
+
+static volatile unsigned short *tX, *tY;
+static unsigned short calib_x0 = 87, calib_dx = 3867; // 3954-87;
+static unsigned short calib_y0 = 129, calib_dy = 3848; //3977-129;
+
+static inline unsigned short tslcd_readX(void) {
+  short x = swapxy ? *tY : *tX;
+
+  if (x & 1) {
+    x = ((~x) >> 4) & 0xfff;
+    x = (x-calib_x0) * 4096 / calib_dx;
+    if (x < 0) x = 0; else if (x > 4095) x = 4095;
+    if (negx) x = 4095 - x;
+    return x;
+  } else {
+    return TSLCD_NOTOUCH;
+  }
+}
+
+static inline unsigned short tslcd_readY(void) {
+  short y = swapxy ? *tX : *tY;
+
+  y = ((~y) >> 4) & 0xfff;
+  y = (y-calib_y0) * 4096 / calib_dy;
+  if (y < 0) y = 0; else if (y > 4095) y = 4095;
+  if (negy) y = 4095 - y;
+  return y;
+}
+
+#define DEBOUNCE_MS 0
+static void tslcd_sample(unsigned long data)
+{
+  static unsigned short last_x = TSLCD_NOTOUCH;
+  static unsigned short last_y = 0;
+  static unsigned short x,y;
+  static int bounce=DEBOUNCE_MS;
+
+  spin_lock(&tslcd_lock);
+  x = tslcd_readX();
+  y = tslcd_readY();
+  if (x == last_x && y == last_y) {
+    goto sample_done;
+  }
+  if (x == TSLCD_NOTOUCH) {
+    bounce = DEBOUNCE_MS;
+    //printk("sample none\n");
+    input_report_key(tslcd_dev, BTN_TOUCH, 0);
+  } else {
+    if (bounce > 0) {
+      bounce--;
+    } else {
+      //printk("x=%d,y=%d\n",x,y);
+      input_report_key(tslcd_dev, BTN_TOUCH, 1);
+      input_report_abs(tslcd_dev, ABS_X, x);
+      input_report_abs(tslcd_dev, ABS_Y, y);
+    }
+  }
+  last_x = x;
+  last_y = y;
+
+  input_sync(tslcd_dev);
+
+ sample_done:
+  timer.expires = jiffies + 1;
+  add_timer(&timer);
+  spin_unlock(&tslcd_lock);
+}
+
+static int tslcd_open(struct input_dev *dev)
+{
+  unsigned long flags;
+
+  //printk("tslcd_open\n");
+  spin_lock_irqsave(&tslcd_lock, flags);
+  if (!timer_started) {
+    timer_started++;
+    timer.function = tslcd_sample;
+    timer.data = 0;
+    timer.expires = jiffies + 1;
+    init_timer(&timer);
+    add_timer(&timer);
+  }
+  spin_unlock_irqrestore(&tslcd_lock, flags);
+
+  return 0;
+}
+
+static void tslcd_close(struct input_dev *dev)
+{
+  unsigned long flags;
+
+  //printk("tslcd_close\n");
+  spin_lock_irqsave(&tslcd_lock, flags);
+  if (!--timer_started) {
+    del_timer(&timer);
+  }
+  spin_unlock_irqrestore(&tslcd_lock, flags);
+
+}
+
+static void sspi_cmd(volatile unsigned short *gpio,unsigned int cmd) {
+  unsigned int i;
+
+  // pulse CS#
+  *gpio = (*gpio & 0xFFF0) | 0x2;
+  *gpio = (*gpio & 0xFFF0) | 0x0;
+
+  for (i = 0; i < 32; i++, cmd <<= 1) {
+    if (cmd & 0x80) {
+      *gpio = (*gpio & 0xFFF0) | 0x4;
+      *gpio = (*gpio & 0xFFF0) | 0xc;
+    } else {
+      *gpio = (*gpio & 0xFFF0) | 0x0;
+      *gpio = (*gpio & 0xFFF0) | 0x8;
+    }
+  }
+}
+
+#define CALIB1_OFFSET 7
+#define CALIB2_OFFSET 8
+static void load_calibration(volatile unsigned short *gpio,
+			     unsigned short *x,unsigned short *dx,
+			     unsigned short *y,unsigned short *dy) {
+  int i,j,ret,n[20],_x,_y,_dx,_dy;
+
+  sspi_cmd(gpio,0xac); // X_PROGRAM_EN
+  sspi_cmd(gpio,0x4e); // READ_TAG
+
+  for (j = 0; j < 20; j++) {
+    for (ret = 0x0, i = 0; i < 32; i++) {
+      *gpio = (*gpio & 0xFFF0) | 0x0;
+      *gpio = (*gpio & 0xFFF0) | 0x8;
+      ret = ret << 1 | (*gpio & 0x1);
+    }
+    n[j] = ret;
+  }
+
+  sspi_cmd(gpio,0x78); // PROGRAM_DIS
+
+  *gpio = (*gpio & 0xFFF0) | 0x2;
+  _x = n[CALIB1_OFFSET] >> 16;
+  _dx = n[CALIB1_OFFSET] & 0xFFFF;
+  _y = n[CALIB2_OFFSET] >> 16;
+  _dy = n[CALIB2_OFFSET] & 0xFFFF;
+  if ((_dx < 1 || _dx > 32768) || (_dy < 1 || _dy > 32768)) {
+    return; // use defaults
+  }
+  if ((_x < 1 || _x > 32768) || (_y < 1 || _y > 32768)) {
+    return; // use defaults
+  }
+  if (_dx == 0) _dx = 4096;
+  if (_dy == 0) _dy = 4096;
+
+  *x = _x;
+  *y = _y;
+  *dx = _dx;
+  *dy = _dy;
+}
+
+static int __init tslcd_init(void)
+{
+  int err;
+  volatile unsigned short *vreg;
+
+  vreg = ioremap(0x600ff000,4096);
+  if (vreg == 0) {
+    printk("tslcd: could not get touchscreen regs\n");
+    err = -ENOMEM;
+    goto fail1;
+  }
+  tX = vreg + 0x20;
+  tY = vreg + 0x21;
+  load_calibration(vreg+(0x86 / sizeof(unsigned short)),
+		   &calib_x0,&calib_dx,&calib_y0,&calib_dy);
+  printk("x calibration: offset=%d, width=%d\n",calib_x0,calib_dx);
+  printk("y calibration: offset=%d, width=%d\n",calib_y0,calib_dy);
+
+  tslcd_dev = input_allocate_device();
+  if (!tslcd_dev) {
+    printk(KERN_ERR "tslcd: not enough memory\n");
+    err = -ENOMEM;
+    goto fail1;
+  }
+
+  tslcd_dev->name = "TS-LCD";
+  tslcd_dev->phys = "tslcd/input0";
+  //tslcd_dev->id.bustype = BUS_ISA;
+  //tslcd_dev->id.vendor  = 0x0005;
+  //tslcd_dev->id.product = 0x0001;
+  //tslcd_dev->id.version = 0x0100;
+
+  tslcd_dev->open    = tslcd_open;
+  tslcd_dev->close   = tslcd_close;
+
+  tslcd_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+  tslcd_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+  input_set_abs_params(tslcd_dev, ABS_X, 0, 4096, 0, 0);
+  input_set_abs_params(tslcd_dev, ABS_Y, 0, 4096, 0, 0);
+
+  err = input_register_device(tslcd_dev);
+  if (err)
+    goto fail2;
+
+  return 0;
+
+ fail1:
+  input_free_device(tslcd_dev);
+ fail2:
+  return err;
+}
+
+static void __exit tslcd_exit(void)
+{
+  input_unregister_device(tslcd_dev);
+}
+
+module_init(tslcd_init);
+module_exit(tslcd_exit);
+module_param(swapxy,bool, 0644);
+module_param(negx,bool, 0644);
+module_param(negy,bool, 0644);
+MODULE_PARM_DESC(swapxy, "Swap X/Y axes");
+MODULE_PARM_DESC(negx, "Reverse X axis");
+MODULE_PARM_DESC(negy, "Reverse Y axis");
-- 
1.5.4.3

