]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
Add various OMAP input drivers
authorTony Lindgren <tony@atomide.com>
Mon, 9 May 2005 21:28:04 +0000 (14:28 -0700)
committerTony Lindgren <tony@atomide.com>
Mon, 9 May 2005 21:28:04 +0000 (14:28 -0700)
Adds various OMAP input drivers.

Signed-off-by: Tony Lindgren <tony@atomide.com>
13 files changed:
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/innovator_ps2.c [new file with mode: 0644]
drivers/input/keyboard/omap-keypad.c [new file with mode: 0644]
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/omap/Makefile [new file with mode: 0644]
drivers/input/touchscreen/omap/ads7846.h [new file with mode: 0644]
drivers/input/touchscreen/omap/omap_ts.c [new file with mode: 0644]
drivers/input/touchscreen/omap/omap_ts.h [new file with mode: 0644]
drivers/input/touchscreen/omap/ts_hx.c [new file with mode: 0644]
drivers/input/touchscreen/omap/ts_inn1510.c [new file with mode: 0644]
drivers/input/touchscreen/omap/ts_osk.c [new file with mode: 0644]

index e55dee3907756425059612c4fe049ecc177d58b3..2c8413337de33632a5ff6993b966d757b0f18b87 100644 (file)
@@ -183,3 +183,23 @@ config KEYBOARD_HIL
          to your machine, so normally you should say Y here.
 
 endif
+
+config KEYBOARD_OMAP
+       tristate "TI OMAP keypad support"
+       depends on (ARCH_OMAP1510 || ARCH_OMAP16XX) && INPUT && INPUT_KEYBOARD
+       help
+         Say Y here if you want to use the OMAP keypad.
+
+         To compile this driver as a module, choose M here: the
+         module will be called omap-keypad.
+
+config OMAP_PS2
+       tristate "TI OMAP Innovator 1510 PS/2 keyboard & mouse support"
+       depends on ARCH_OMAP1510 && MACH_OMAP_INNOVATOR && INPUT && INPUT_KEYBOARD
+       help
+         Say Y here if you want to use the OMAP Innovator 1510 PS/2
+         keyboard and mouse.
+
+         To compile this driver as a module, choose M here: the
+         module will be called innovator_ps2.
+         
index b02eeceea3c32a0b683e703f2d8aa8591b205151..a2f928a763faeac51cb5c52e655d0fa858fc7797 100644 (file)
@@ -16,4 +16,5 @@ obj-$(CONFIG_KEYBOARD_98KBD)          += 98kbd.o
 obj-$(CONFIG_KEYBOARD_CORGI)           += corgikbd.o
 obj-$(CONFIG_KEYBOARD_HIL)             += hil_kbd.o
 obj-$(CONFIG_KEYBOARD_HIL_OLD)         += hilkbd.o
-
+obj-$(CONFIG_KEYBOARD_OMAP)             += omap-keypad.o
+obj-$(CONFIG_OMAP_PS2)                 += innovator_ps2.o
diff --git a/drivers/input/keyboard/innovator_ps2.c b/drivers/input/keyboard/innovator_ps2.c
new file mode 100644 (file)
index 0000000..de07ce9
--- /dev/null
@@ -0,0 +1,1303 @@
+/*
+ * drivers/char/innovator_ps2.c
+ *
+ * Basic PS/2 keyboard/mouse driver for the Juno® USAR HID controller
+ * present on the TI Innovator/OMAP1510 Break-out-board.
+ *
+ *
+ * Author: MontaVista Software, Inc.
+ *         <gdavis@mvista.com> or <source@mvista.com>
+ *
+ *
+ * 2003 (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.
+ *
+ *
+ * REFERENCES:
+ *
+ * 1.  Technical Reference Manual
+ *     Juno® 01
+ *     Multi-function ICs family
+ *     UR8HC007-001 HID & Power management controller
+ *     Document Number: DOC8-007-001-TR-075
+ *     Date: February 2002
+ *     Copyright ©1998-2002 Semtech Corporation
+ *     http://www.semtech.com/pdf/doc8-007-001-tr.pdf
+ *
+ * 2.  Juno® 01 UR8HC007-001 Data Sheet
+ *     Extremely Low-power Input Device and Power Management IC
+ *     Copyright ©1998-2002 Semtech Corporation
+ *     DOC8-007-001-DS-112
+ *     http://www.semtech.com/pdf/doc8-007-001-ds.pdf
+ *
+ *
+ * HISTORY:
+ *
+ * 20030626: George G. Davis <gdavis@mvista.com>
+ *      Initially based on the following RidgeRun DSPlinux Version 1.6 files:
+ *             linux-2.4.15-rmk1-dsplinux/arch/arm/dsplinux/hid/omap1510_hid.c
+ *             linux-2.4.15-rmk1-dsplinux/arch/arm/dsplinux/hid/omap1510_hid.h
+ *             linux-2.4.15-rmk1-dsplinux/arch/arm/dsplinux/hid/omap1510_ps2.c
+ *             linux-2.4.15-rmk1-dsplinux/arch/arm/dsplinux/hid/omap1510_spi.c
+ *     All original files above are
+ *             Copyright (C) 2001 RidgeRun, Inc.
+ *             Author: Alex McMains <aam@ridgerun.com>
+ *
+ * 20040812: Thiago Radicchi <trr@dcc.ufmg.br>
+ *      Cleanup of old code from 2.4 driver and some debug code.
+ *      Minor changes in interrupt handling code.
+ *
+ * NOTES:
+ *
+ * 1. This driver does not provide support for setting keyboard/mouse
+ *    configuration parameters. Both devices are managed directly by
+ *    the Juno UR8HC007-001 on behalf of the host. This minimises the
+ *    amount of host processing required to manage HID events and state
+ *    changes, e.g. both keyboard and mouse devices are hot pluggable
+ *    with no host intervention required. However, we cannot customise
+ *    keyboard/mouse settings in this case. So we live with the defaults
+ *    as setup by the Juno UR8HC007-001 whatever they may be.
+ * 2. Keyboard auto repeat does not work. See 1 above. : )
+ *
+ *
+ * TODO:
+ *
+ * 1. Complete DPM/LDM stubs and test.
+ * 2. Add SPI error handling support, i.e. resend, etc.,.
+ * 3. Determine why innovator_hid_interrupt() is called for every
+ *    invocation of Innovator FPGA IRQ demux. It appears that the
+ *    missed Innovator ethernet workaround may be to blame. However,
+ *    it does not adversely affect operation of this driver since we
+ *    check for assertion of ATN prior to servicing the interrupt. If
+ *    ATN is negated, we bug out right away.
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/arch/fpga.h>
+
+#undef INNOVATOR_KEYB_DEBUG
+#ifdef INNOVATOR_KEYB_DEBUG
+#define        dbg(format, arg...) printk(KERN_DEBUG "%s:%d: " format , \
+                                  __FUNCTION__ , __LINE__ , ## arg)
+#define        entry() printk(KERN_DEBUG "%s:%d: Entry\n" , __FUNCTION__ , __LINE__)
+#define        exit()  printk(KERN_DEBUG "%s:%d: Exit\n" , __FUNCTION__ , __LINE__)
+#define dump_packet(p, n)                                      \
+       {                                                       \
+               int i;                                          \
+               printk(KERN_DEBUG "%s:%d: %08x:" ,              \
+                      __FUNCTION__ , __LINE__ , (int) p);      \
+               for (i = 0; i < n; i += 1) {                    \
+                       printk(" %02x", (int) p[i]);            \
+               }                                               \
+               printk("\n");                                   \
+       }
+#else
+#define        dbg(format, arg...) do {} while (0)
+#define        entry() do {} while (0)
+#define        exit()  do {} while (0)
+#define dump_packet(p, n) do {} while (0)
+#endif
+
+
+#define        PFX     "innovator_ps2"
+#define err(format, arg...)    printk(KERN_ERR PFX ": " format , ## arg)
+#define info(format, arg...)   printk(KERN_INFO PFX ": " format , ## arg)
+#define warn(format, arg...)   printk(KERN_WARNING PFX ": " format , ## arg)
+
+
+/****************************************************************************/
+
+/*
+ * Synchronous communications timing parameters (Reference [1] pg 7-7)
+ */
+
+#define tMSA   5000    /* -/5ms        _SS to _ATN (master transfer) */
+#define tMAC   100     /* 100us/5ms    _ATN to first clock pulse (master
+                                       transfer) */
+#define tMIB   150     /* 150us/5ms    Beginning of byte transfer to beginning
+                                       of next byte transfer */
+#define tSIB   150     /* 150us/5ms    Beginning of byte transfer to beginning
+                                       of next byte transfer */
+#define tMSP   100     /* -/100us      Last clock pulse of packet to _SS
+                                       de-assertion */
+#define tMNSA  100     /* -/100us      _SS de-assertion to _ATN de-assertion */
+#define tMNEXT 120     /* 120uS/-      _ATN release to _SS re-assertion
+                                       (master transfer) */
+#define        tSAS    5000    /* -/5ms        _ATN to _SS (slave transfer) */
+#define tSSC   100     /* 100us/5ms    _SS to first clock pulse (slave
+                                       transfer) */
+#define tSNA   100     /* -/100us      Last clock pulse of packet to _ATN
+                                       de-assertion */
+#define tSNAS  100     /* -/100us      _ATN release to _SS de-assertion */
+#define tSNEXT 120     /* 120us/-      _SS release to _ATN re-assertion
+                                       (slave transfer) */
+#define tSCK   4       /* 4us/-        Clock period */
+#define tSLOW  2       /* 2us/-        Clock LOW period */
+#define tHOLD  200     /* 200ns/-      Master data hold time */
+#define tSETUP 100     /* 100ns/-      Master data setup Time */
+#define tSSETUP        500     /* -/500ns      Slave data setup time from clock
+                                       falling edge */
+
+
+/*
+ * Protocol Headers (Reference [1], pg. 5-1):
+ */
+
+
+/* Protocols used in commands issued by the host: */
+#define SIMPLE                 0x80    /* Simple commands
+                                        * Common for both host and controller
+                                        * protocol headers.
+                                        */
+#define WRITE_REGISTER_BIT     0x81    /* Write register bit */
+#define READ_REGISTER_BIT      0x82    /* Read register bit */
+#define WRITE_REGISTER         0x83    /* Write register */
+#define READ_REGISTER          0x84    /* Read register */
+#define WRITE_BLOCK            0x85    /* Write block */
+#define READ_BLOCK             0x86    /* Read block */
+
+
+/* Protocols used in responses, reports and alerts issued by the controller: */
+#define REPORT_REGISTER_BIT    0x81    /* Report register bit & event alerts */
+#define REPORT_REGISTER                0x83    /* Report register */
+#define REPORT_BLOCK           0x85    /* Report block */
+#define POINTING_REPORT                0x87    /* Pointing device data report */
+#define KEYBOARD_REPORT                0x88    /* Keyboard device data report */
+
+
+/* Simple Commands (Reference [1], pg 5-3): */
+#define INITIALIZE             0x00    /* Forces the recipient to enter the
+                                        * known default power-on state.
+                                        */
+#define INITIALIZATION_COMPLETE        0x01    /* Issued as a hand-shake response only
+                                        * to the "Initialize" command.
+                                        */
+#define RESEND_REQUEST         0x05    /* Issued upon error in the reception
+                                        * of a package. The recipient resends
+                                        * the last transmitted packet.
+                                        */
+
+/* Register offsets (Reference [1], pg 6-1 thru 6-9): */
+
+#define REG_PM_COMM            0
+#define REG_PM_STATUS          1
+#define REG_PAGENO             255
+
+/* Power management bits ((Reference [1], pg 6-10): */
+
+#define SUS_STATE              0x2     /* in REG_PM_COMM */
+
+/* Miscellaneous constants: */
+
+#define X_MSB_SHIFT    (8-4)
+#define X_MSB_MASK     (3<<4)
+#define Y_MSB_SHIFT    (8-6)
+#define Y_MSB_MASK     (3<<6)
+
+
+#define JUNO_BLOCK_SIZE     32
+#define JUNO_BUFFER_SIZE    256
+
+
+/*
+ * Errors:
+ */
+
+#define E_BAD_HEADER   1
+#define E_BAD_LRC      2
+#define E_ZERO_BYTES   3
+#define E_BAD_VALUE    4
+#define E_BAD_MODE     5
+#define E_REPORT_MODE  6
+#define E_BAD_ACK      7
+#define E_BAD_DEVICE_ID        8
+#define E_PKT_SZ       9
+
+
+/*
+ * Host/Controller Command/Response Formats:
+ */
+
+typedef struct _simple_t {
+       u8 header;
+       u8 cmd_code;
+       u8 LRC;
+} __attribute__ ((packed)) simple_t;
+
+typedef struct _write_bit_t {
+       u8 header;
+       u8 offset;
+       u8 value_bit;
+       u8 LRC;
+} __attribute__ ((packed)) write_bit_t;
+
+typedef struct _read_bit_t {
+       u8 header;
+       u8 offset;
+       u8 bit;
+       u8 LRC;
+} __attribute__ ((packed)) read_bit_t;
+
+typedef struct _write_reg_t {
+       u8 header;
+       u8 offset;
+       u8 value;
+       u8 LRC;
+} __attribute__ ((packed)) write_reg_t;
+
+typedef struct _read_reg_t {
+       u8 header;
+       u8 offset;
+       u8 LRC;
+} __attribute__ ((packed)) read_reg_t;
+
+typedef struct _write_block_t {
+       u8 header;
+       u8 offset;
+       u8 length;
+       u8 block[JUNO_BLOCK_SIZE + 1]; /* Hack: LRC is last element of block[] */
+} __attribute__ ((packed)) write_block_t;
+
+typedef struct _read_block_t {
+       u8 header;
+       u8 offset;
+       u8 length;
+       u8 LRC;
+} __attribute__ ((packed)) read_block_t;
+
+typedef struct _report_bit_t {
+       u8 header;
+       u8 offset;
+       u8 value_bit;
+       u8 LRC;
+} __attribute__ ((packed)) report_bit_t;
+
+typedef struct _report_reg_t {
+       u8 header;
+       u8 offset;
+       u8 value;
+       u8 LRC;
+} __attribute__ ((packed)) report_reg_t;
+
+typedef struct _report_block_t {
+       u8 header;
+       u8 offset;
+       u8 length;
+       u8 block[32];
+       u8 LRC;
+} __attribute__ ((packed)) report_block_t;
+
+typedef struct _mse_report_t {
+       u8 header;
+       u8 buttons;
+       u8 Xdisplacement;
+       u8 Ydisplacement;
+       u8 Zdisplacement;
+       u8 LRC;
+} __attribute__ ((packed)) mse_report_t;
+
+typedef struct _kdb_report_t {
+       u8 header;
+       u8 keynum;              /* up > 0x80, down < 0x7E, all keys up 0x00 */
+       u8 LRC;
+} __attribute__ ((packed)) kdb_report_t;
+
+
+static u8 buffer[JUNO_BUFFER_SIZE];
+static u8 block[JUNO_BLOCK_SIZE];
+
+static void do_hid_tasklet(unsigned long);
+DECLARE_TASKLET(hid_tasklet, do_hid_tasklet, 0);
+static struct innovator_hid_dev *hid;
+static spinlock_t innovator_fpga_hid_lock = SPIN_LOCK_UNLOCKED;
+static atomic_t innovator_fpga_hid_busy = ATOMIC_INIT(0);
+
+struct innovator_hid_dev {
+       struct input_dev mouse, keyboard;
+       int open;
+       int irq_enabled;
+};
+
+/****************************************************************************/
+
+/*
+ * Low-level TI Innovator/OMAP1510 FPGA HID SPI interface helper functions:
+ */
+
+static u8
+innovator_fpga_hid_rd(void)
+{
+       u8 val = inb(INNOVATOR_FPGA_HID_SPI);
+       return val;
+}
+
+static void
+innovator_fpga_hid_wr(u8 val)
+{
+       outb(val, INNOVATOR_FPGA_HID_SPI);
+}
+
+static void
+innovator_fpga_hid_frob(u8 mask, u8 val)
+{
+       unsigned long flags;
+       local_irq_save(flags);
+       innovator_fpga_hid_wr((innovator_fpga_hid_rd() & ~mask) | val);
+       local_irq_restore(flags);
+}
+
+static void
+innovator_fpga_hid_set_bits(u8 x)
+{
+       innovator_fpga_hid_frob(x, x);
+}
+
+static void
+innovator_fpga_hid_clear_bits(u8 x)
+{
+       innovator_fpga_hid_frob(x, 0);
+}
+
+static void
+SS(int value)
+{
+       innovator_fpga_hid_frob(OMAP1510_FPGA_HID_nSS, value ? OMAP1510_FPGA_HID_nSS : 0);
+}
+
+static void
+SCLK(int value)
+{
+       innovator_fpga_hid_frob(OMAP1510_FPGA_HID_SCLK, value ? OMAP1510_FPGA_HID_SCLK : 0);
+}
+
+static void
+MOSI(int value)
+{
+       innovator_fpga_hid_frob(OMAP1510_FPGA_HID_MOSI, value ? OMAP1510_FPGA_HID_MOSI : 0);
+}
+
+static u8
+MISO(void)
+{
+       return ((innovator_fpga_hid_rd() & OMAP1510_FPGA_HID_MISO) ? 1 : 0);
+}
+
+static u8 
+ATN(void)
+{
+       return ((innovator_fpga_hid_rd() & OMAP1510_FPGA_HID_ATN) ? 1 : 0);
+}
+
+static int
+wait_for_ATN(int assert, int timeout)
+{
+       do {
+               if (ATN() == assert)
+                       return 0;
+               udelay(1);
+       } while (timeout -= 1);
+       return -1;
+}
+
+static u8
+innovator_fpga_hid_xfer_byte(u8 xbyte)
+{
+       int i;
+       u8 rbyte;
+
+       for (rbyte = 0, i = 7; i >= 0; i -= 1) {
+               SCLK(0);
+               MOSI((xbyte >> i) & 1);
+               udelay(tSLOW);
+               SCLK(1);
+               rbyte = (rbyte << 1) | MISO();
+               udelay(tSLOW);
+       }
+
+       return rbyte;
+}
+
+static void
+innovator_fpga_hid_reset(void)
+{
+       innovator_fpga_hid_wr(OMAP1510_FPGA_HID_SCLK | OMAP1510_FPGA_HID_MOSI);
+       mdelay(1);
+       innovator_fpga_hid_set_bits(OMAP1510_FPGA_HID_RESETn);
+}
+
+
+/*****************************************************************************
+
+  Refer to Reference [1], Chapter 7 / Low-level communications, Serial
+  Peripheral Interface (SPI) implementation Host (master) packet
+  transmission timing, pg. 7-3, for timing and implementation details
+  for spi_xmt().
+
+ *****************************************************************************/
+
+int
+spi_xmt(u8 * p, u8 n)
+{
+       unsigned long flags;
+
+       dump_packet(p, n);
+       local_irq_save(flags);
+       disable_irq(OMAP1510_INT_FPGA_ATN);
+
+       if (ATN()) {
+               /* Oops, we have a collision. */
+               enable_irq(OMAP1510_INT_FPGA_ATN);
+               local_irq_restore(flags);
+               dbg("Protocol error: ATN is asserted\n");
+               return -EAGAIN;
+       }
+
+       SS(1);
+
+       if (wait_for_ATN(1, tMSA) < 0) {
+               SS(0);
+               enable_irq(OMAP1510_INT_FPGA_ATN);
+               local_irq_restore(flags);
+               dbg("timeout waiting for ATN assertion\n");
+               return -EREMOTEIO;
+       }
+
+       udelay(tMAC);
+
+       while (n--) {
+               innovator_fpga_hid_xfer_byte(*p++);
+               if (n) {
+                       udelay(tMIB - 8 * tSCK);
+               }
+       }
+
+       MOSI(1);        /* Set MOSI to idle high. */
+
+       /* NOTE: The data sheet does not specify a minimum delay
+        * here. But innovator_fpga_hid_xfer_byte() gives us a half-clock
+        * delay (tSLOW) after the last bit is sent. So I'm happy with
+        * that.
+        */
+
+       SS(0);
+
+       if (wait_for_ATN(0, tMNSA) < 0) {
+               enable_irq(OMAP1510_INT_FPGA_ATN);
+               local_irq_restore(flags);
+               dbg("timeout waiting for ATN negation\n");
+               return -EREMOTEIO;
+       }
+
+       udelay(tMNEXT);
+       enable_irq(OMAP1510_INT_FPGA_ATN);
+       local_irq_restore(flags);
+       return 0;
+}
+
+
+/*****************************************************************************
+
+  Refer to Reference [1],  Chapter 7 / Low-level communications, Serial
+  Peripheral Interface (SPI) implementation, Slave packet transmission
+  timing, pg. 7-5, for timing and implementation details for spi_rcv().
+
+ *****************************************************************************/
+
+int
+spi_rcv(u8 * p, int len)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       if (len > 256) {
+               /* Limit packet size to something reasonable */
+               return -1;
+       }
+
+       local_irq_save(flags);
+
+       if (wait_for_ATN(1, tMSA) < 0) {
+               local_irq_restore(flags);
+               dbg("Protocol error: ATN is not asserted\n");
+               return -EREMOTEIO;
+       }
+
+       SS(1);
+
+       udelay(tSSC);
+
+       while (ATN()) {
+               if (ret >= len) {
+                       err("over run error\n");
+                       ret = -1;
+                       break;
+               }
+               p[ret++] = innovator_fpga_hid_xfer_byte(0xff);
+               udelay(tSNA);   /* Wait long enough to detect negation of ATN
+                                * after last clock pulse of packet.
+                                *
+                                * NOTE: Normally, we need a minimum delay of
+                                *       tSIB between the start of one byte
+                                *       and the start of the next. However,
+                                *       we also need to wait long enough
+                                *       for the USAR to negate ATN before
+                                *       starting the next byte. So we use
+                                *       max(tSIB - 8 * tSCK, tSNA) here to
+                                *       satisfy both constraints.
+                                */
+       }
+
+       SS(0);  /* NOTE: The data sheet does not specify a minimum delay
+                * here. But innovator_fpga_hid_xfer_byte() gives us a
+                * half-clock delay (tSLOW) after the last bit is sent. So
+                * I'm happy with that (rather than no delay at all : ).
+                */
+
+
+       udelay(tSNEXT); /* This isn't quite right. Assertion of ATN after
+                        * negation of SS is an USAR timing constraint.
+                        * What we need here is a spec for the minimum
+                        * delay from SS negation to SS assertion. But
+                        * for now, just use this brain dead delay.
+                        */
+
+       local_irq_restore(flags);
+
+       if (ret > 0) {
+               dump_packet(p, ret);
+       }
+
+       return ret;
+}
+
+
+/*****************************************************************************
+  Calculate Host/Controller Command/Response Longitudinal Redundancy Check (LRC)
+
+  The algorithm implemented in calculate_LRC() below is taken directly from
+  the reference [1], Chapter 7 / Low-level communications, LRC (Longitudinal
+  Redundancy Check), pg 5-10.
+
+ *****************************************************************************/
+
+static u8
+calculate_LRC(u8 * p, int n)
+{
+       u8 LRC;
+       int i;
+
+       /*
+        * Init the LRC using the first two message bytes.
+        */
+       LRC = p[0] ^ p[1];
+
+       /*
+        * Update the LRC using the remainder of the p.
+        */
+       for (i = 2; i < n; i++)
+               LRC ^= p[i];
+
+       /*
+        * If the MSB is set then clear the MSB and change the next
+        * most significant bit
+        */
+       if (LRC & 0x80)
+               LRC ^= 0xC0;
+
+       return LRC;
+}
+
+
+/*
+ * Controller response helper functions:
+ */
+
+static inline int
+report_mouse(mse_report_t * p, int n)
+{
+       if (p->header != POINTING_REPORT)
+               return -E_BAD_HEADER;
+
+       if (n != sizeof(mse_report_t))
+               return -E_PKT_SZ;
+
+       return (p->LRC != calculate_LRC((u8 *) p, sizeof(mse_report_t) - 1)) ?
+               -E_BAD_LRC : POINTING_REPORT;
+}
+
+static inline int
+report_keyboard(kdb_report_t * p, int n)
+{
+       if (p->header != KEYBOARD_REPORT)
+               return -E_BAD_HEADER;
+
+       if (n != sizeof(kdb_report_t))
+               return -E_PKT_SZ;
+
+       return (p->LRC != calculate_LRC((u8 *) p, sizeof(kdb_report_t) - 1)) ?
+               -E_BAD_LRC : KEYBOARD_REPORT;
+}
+
+
+/*
+ * Miscellaneous helper functions:
+ */
+
+static inline int
+report_type(u8 * type)
+{
+       /* check the header to find out what kind of report it is */
+       if ((*type) == KEYBOARD_REPORT)
+               return KEYBOARD_REPORT;
+       else if ((*type) == POINTING_REPORT)
+               return POINTING_REPORT;
+       else
+               return -E_BAD_HEADER;
+}
+
+static inline int
+report_async(void * p, int n)
+{
+       int ret;
+
+       if ((ret = spi_rcv((u8 *) p, n)) < 0)
+               return ret;
+
+       if (report_type((u8 *) p) == POINTING_REPORT)
+               ret = report_mouse((mse_report_t *) p, ret);
+       else if (report_type((u8 *) p) == KEYBOARD_REPORT)
+               ret = report_keyboard((kdb_report_t *) p, ret);
+
+       return ret;
+}
+
+static int
+verify_init(u8 * p)
+{
+       return (((simple_t *)p)->cmd_code == 0x01) ? 0 : -1;
+}
+
+
+/*
+ * Host command helper functions:
+ */
+
+#if    0
+/* REVISIT/TODO: Wrapper for command/response with resend handing. */
+static int
+spi_xfer(u8 * optr, u8 osz, u8 * iptr, u8 isz)
+{
+       static u8 buf[256];
+       int ret;
+       int xretries = 3;
+
+       do {
+               if (optr != NULL && osz) {
+                       do {
+                               ret = spi_xmt((u8 *) optr, osz);
+                       } while (ret < 0);
+               }
+
+               ret = spi_rcv((u8 *) buf, 256);
+
+               if (ret == -EREMOTEIO) {
+                       if (iptr == NULL) {
+                               break;
+                       }
+               }
+       } while (xretries--);
+
+       return ret;
+}
+#endif
+
+/* REVISIT: Enable these when/if additional Juno features are required. */
+static inline int
+simple(u8 cmd)
+{
+       static simple_t p;
+       int ret;
+
+       p.header = SIMPLE;
+       p.cmd_code = cmd;
+       p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1);
+
+       if ((ret = spi_xmt((u8 *) & p, sizeof(p))) < 0)
+               return ret;
+
+       if ((ret = spi_rcv((u8 *) & p, sizeof(p))) < 0)
+               return ret;
+
+       if (ret == 0)
+               return -E_ZERO_BYTES;
+
+       if (ret != sizeof(p))
+               return -E_PKT_SZ;
+
+       if (p.header != SIMPLE)
+               return -E_BAD_HEADER;
+
+       if (p.LRC != calculate_LRC((u8 *) & p, sizeof(p) - 1))
+               return -E_BAD_LRC;
+
+       /* REVISIT: Need to check or return response code here? */
+}
+
+static inline int
+write_bit(u8 offset, u8 bit, u8 value)
+{
+       static write_bit_t p;
+
+       p.header = WRITE_REGISTER_BIT;
+       p.offset = offset;
+       p.value_bit = (bit << 1) | (value & 1);
+       p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1);
+
+       return spi_xmt((u8 *) & p, sizeof(p));
+}
+
+static inline int
+read_bit(u8 offset, u8 bit, u8 * data)
+{
+       static read_bit_t p;
+       static report_bit_t q;
+       int ret;
+
+       p.header = READ_REGISTER_BIT;
+       p.offset = offset;
+       p.bit = bit;
+       p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1);
+
+       if ((ret = spi_xmt((u8 *) & p, sizeof(p))) < 0)
+               return ret;
+
+       if ((ret = spi_rcv((u8 *) & q, sizeof(q))) < 0)
+               return ret;
+
+       if (ret == 0)
+               return -E_ZERO_BYTES;
+
+       if (ret != sizeof(q))
+               return -E_PKT_SZ;
+
+       if (q.header != REPORT_REGISTER_BIT)
+               return -E_BAD_HEADER;
+
+       if (q.LRC != calculate_LRC((u8 *) & q, sizeof(q) - 1))
+               return -E_BAD_LRC;
+
+       *data = q.value_bit;
+
+       return 0;
+}
+
+static inline int
+write_reg(u8 offset, u8 value)
+{
+       static write_reg_t p;
+
+       p.header = WRITE_REGISTER;
+       p.offset = offset;
+       p.value = value;
+       p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1);
+
+       return spi_xmt((u8 *) & p, sizeof(p));
+}
+
+static inline int
+read_reg(u8 offset, u8 * data)
+{
+       static read_reg_t p;
+       static report_reg_t q;
+       int ret;
+
+       p.header = READ_REGISTER;
+       p.offset = offset;
+       p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1);
+
+       if ((ret = spi_xmt((u8 *) & p, sizeof(p))) < 0)
+               return ret;
+
+       if ((ret = spi_rcv((u8 *) & q, sizeof(q))) < 0)
+               return ret;
+
+       if (ret == 0)
+               return -E_ZERO_BYTES;
+
+       if (ret != sizeof(q))
+               return -E_PKT_SZ;
+
+       if (q.header != REPORT_REGISTER)
+               return -E_BAD_HEADER;
+
+       if (q.LRC != calculate_LRC((u8 *) & q, sizeof(q) - 1))
+               return -E_BAD_LRC;
+
+       *data = q.value;
+
+       return 0;
+}
+
+static inline int
+write_block(u8 offset, u8 length, u8 * block)
+{
+       static write_block_t p;
+
+       p.header = WRITE_BLOCK;
+       p.offset = offset;
+       p.length = length;
+       memcpy(&p.block, block, length);
+       p.block[length] = calculate_LRC((u8 *) & p, 3 + length);
+
+       return spi_xmt((u8 *) & p, 4 + length);
+}
+
+static inline int
+read_block(u8 offset, u8 length, u8 * buf)
+{
+       static read_block_t p;
+       static report_block_t q;
+       int ret;
+
+       p.header = READ_BLOCK;
+       p.offset = offset;
+       p.length = length;
+       p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1);
+
+       if ((ret = spi_xmt((u8 *) & p, sizeof(p))) < 0)
+               return ret;
+
+       if ((ret = spi_rcv((u8 *) & q, sizeof(q))) < 0)
+               return ret;
+
+       if (ret == 0)
+               return -E_ZERO_BYTES;
+
+       if (ret != sizeof(4 + q.length))
+               return -E_PKT_SZ;
+
+       if (q.header != REPORT_BLOCK)
+               return -E_BAD_HEADER;
+
+       if (q.block[q.length] != calculate_LRC((u8 *) & q, 3 + q.length))
+               return -E_BAD_LRC;
+
+       if (length != q.length)
+               return -E_PKT_SZ;
+
+       memcpy(buf, &q.block, length);
+
+       return 0;
+}
+
+#ifdef INNOVATOR_KEYB_DEBUG
+static void
+ctrl_dump_regs(void)
+{
+       int i;
+       int n;
+
+       for (i = 0; i < 256; i += 8) {
+               read_block(i, 16, buffer);
+               mdelay(1);
+       }
+}
+#endif
+
+/*****************************************************************************/
+
+static void
+process_pointing_report(struct innovator_hid_dev *hid, u8 * buffer)
+{
+       static int prev_x, prev_y, prev_btn;
+       int x, y, btn;
+
+       if (buffer[1] & (1 << 3)) {
+               /* relative pointing device report */
+               x = buffer[2];
+               y = buffer[3];
+
+               /* check the sign and convert from 2's complement if negative */
+               if (buffer[1] & (1<<4))
+                       x = ~(-x) - 255;
+
+               /* input driver wants -y */
+               if (buffer[1] & (1<<5))
+                       y = -(~(-y) - 255);
+               else
+                       y = -y;
+
+               input_report_key(&hid->mouse,
+                                BTN_LEFT, buffer[1] & (1<<0));
+               input_report_key(&hid->mouse,
+                                BTN_RIGHT, buffer[1] & (1<<1));
+               input_report_key(&hid->mouse,
+                                BTN_MIDDLE, buffer[1] & (1<<2));
+               input_report_rel(&hid->mouse, REL_X, x);
+               input_report_rel(&hid->mouse, REL_Y, y);
+       } else {
+               /* REVISIT: Does this work? */
+               /* absolute pointing device report */
+               x = buffer[2] + ((buffer[1] & X_MSB_MASK) << X_MSB_SHIFT);
+               y = buffer[3] + ((buffer[1] & Y_MSB_MASK) << Y_MSB_SHIFT);
+               btn = buffer[1] & (1<<0);
+
+               if ((prev_x == x) && (prev_y == y)
+                   && (prev_btn == btn))
+                       return;
+
+               input_report_key(&hid->mouse, BTN_LEFT, btn);
+               input_report_abs(&hid->mouse, ABS_X, x);
+               input_report_abs(&hid->mouse, ABS_Y, y);
+               prev_x = x;
+               prev_y = y;
+               prev_btn = btn;
+       }
+       input_sync(&hid->mouse);
+       dbg("HID X: %d Y: %d Functions: %x\n", x, y, buffer[1]);
+}
+
+/*
+ * Reference [1], Appendix A, Semtech standard PS/2 key number definitions,
+ * pgs. A-1 through A-3. The following table lists standard PS/2 key numbers
+ * used by the Juno® 01 keyboard manager.
+ *
+ * NOTES:
+ * 1. The following table indices are E0 codes which require special handling:
+ *     53..62, 77..78, 94, 96, 100, 102..104, 108..110
+ * 2. The following table indices are E1 codes which require special handling:
+ *     101
+ */
+
+static unsigned char usar2scancode[128] = {
+       0x00, 0x29, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+       0x18, 0x19, 0x1a, 0x1b, 0x2b, 0x1e, 0x1f, 0x20,
+       0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+       0x1c, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32,
+       0x33, 0x34, 0x35, 0x39, 0x01, 0x52, 0x53, 0x4b,
+       0x47, 0x4f, 0x48, 0x50, 0x49, 0x51, 0x4d, 0x37,
+       0x4e, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47,
+       0x48, 0x49, 0x52, 0x53, 0x4a, 0x1c, 0x35, 0x3b,
+       0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43,
+       0x44, 0x57, 0x58, 0x2a, 0x36, 0x38, 0x38, 0x1d,
+       0x1d, 0x3a, 0x45, 0x46, 0x2a, 0x1d, 0x5b, 0x5c,
+       0x5d, 0xff, 0x00, 0x00, 0x5e, 0x5f, 0x63, 0x70,
+       0x7b, 0x79, 0x7d, 0x73, 0x5b, 0x5c, 0x5d, 0x63,
+       0x65, 0x66, 0x68, 0x69, 0x6b, 0x56, 0x54, 0x00
+};
+
+/*
+ * The following are bit masks used to encode E0 scan codes which
+ * require special handling. However, scan codes 100 and 101 are
+ * excludable here since they each require unique multi-byte scan
+ * code translations and are therefore dealt with individually via
+ * handle_print_scr() and handle_pause() respectively below.
+ */
+
+static unsigned long int e0_codes1 = 0x030003ff; /* scan codes 53..84 */
+static unsigned long int e0_codes2 = 0x038e0a00; /* scan codes 85..116 */
+
+static void
+handle_print_scr(int up)
+{
+       if (up) {
+               input_report_key(&hid->keyboard, 0xe0, 1);
+               input_report_key(&hid->keyboard, 0xb7, 1);
+               input_report_key(&hid->keyboard, 0xe0, 1);
+               input_report_key(&hid->keyboard, 0xaa, 1);
+       } else {
+               input_report_key(&hid->keyboard, 0xe0, 0);
+               input_report_key(&hid->keyboard, 0x2a, 0);
+               input_report_key(&hid->keyboard, 0xe0, 0);
+               input_report_key(&hid->keyboard, 0x37, 0);
+       }
+}
+
+static void
+handle_pause(void)
+{
+       input_report_key(&hid->keyboard, 0xe1, 0);
+       input_report_key(&hid->keyboard, 0x1d, 0);
+       input_report_key(&hid->keyboard, 0x45, 0);
+       input_report_key(&hid->keyboard, 0xe1, 0);
+       input_report_key(&hid->keyboard, 0x9d, 0);
+       input_report_key(&hid->keyboard, 0xc5, 0);
+}
+
+static void
+process_keyboard_report(struct innovator_hid_dev *hid, u8 * buffer)
+{
+       unsigned char ch = buffer[1] & 0x7f;
+       int up = buffer[1] & 0x80 ? 1 : 0;
+       int is_e0 = 0;
+
+       if ((ch == 106) || (ch == 107))
+               return;         /* no code */
+
+       if (ch == 100) {
+               handle_print_scr(up);
+               return;
+       }
+
+       if (ch == 101) {
+               handle_pause();
+               return;
+       }
+
+       if ((ch >= 53) && (ch <= 84)) {
+               /* first block of e0 codes */
+               is_e0 = e0_codes1 & (1 << (ch - 53));
+       } else if ((ch >= 85) && (ch <= 116)) {
+               /* second block of e0 codes */
+               is_e0 = e0_codes2 & (1 << (ch - 85));
+       }
+
+       if (is_e0) {
+               input_report_key(&hid->keyboard, 0xe0, !up);
+       }
+       input_report_key(&hid->keyboard, usar2scancode[ch], !up);
+       input_sync(&hid->keyboard);
+}
+
+static irqreturn_t
+innovator_hid_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       if (ATN()) {
+               disable_irq(OMAP1510_INT_FPGA_ATN);
+               tasklet_schedule(&hid_tasklet);
+       }
+       return IRQ_HANDLED;
+}
+
+static void
+do_hid_tasklet(unsigned long unused)
+{
+       int ret;
+       if ((ret = report_async(buffer, 256)) == -1) {
+               dbg("Error: Bad Juno return value: %d\n", ret);
+       } else if (ret == KEYBOARD_REPORT) {
+               process_keyboard_report(hid, buffer);
+       } else if (ret == POINTING_REPORT) {
+               process_pointing_report(hid, buffer);
+       } else {
+               dbg("ERROR: bad report\n");
+       }
+       enable_irq(OMAP1510_INT_FPGA_ATN);
+}
+
+static int
+innovator_hid_open(struct input_dev *dev)
+{
+       if (hid->open++)
+               return 0;
+
+       if (request_irq(OMAP1510_INT_FPGA_ATN, (void *) innovator_hid_interrupt,
+                       SA_INTERRUPT, PFX, hid) < 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+static void
+innovator_hid_close(struct input_dev *dev)
+{
+       if (!--hid->open)
+               return;
+
+       if (hid == NULL)
+               return;
+
+       kfree(hid);
+}
+
+static int innovator_ps2_remove(struct device *dev)
+{
+       return 0;
+}
+
+static void innovator_ps2_device_release(struct device *dev)
+{
+       /* Nothing */
+}
+
+static int innovator_ps2_suspend(struct device *dev, u32 state, u32 level)
+{
+       u8 pmcomm;
+
+       
+       switch(level) {
+       case SUSPEND_DISABLE:
+
+               /*
+                * Set SUS_STATE in REG_PM_COMM (Page 0 R0).  This will cause
+                * PM_MOD bits of REG_PM_STATUS to show suspended state,
+                * but the SUS_STAT bit of REG_PM_STATUS will continue to
+                * reflect the state of the _HSUS pin.
+                */
+
+               if (write_reg(REG_PAGENO, 0) < 0)
+                       printk("ps2 suspend: write_reg REG_PAGENO error\n");
+
+               if (read_reg(REG_PM_COMM, &pmcomm) < 0)
+                       printk("ps2 suspend: read_reg REG_PM_COMM error\n");
+               
+               if (write_reg(REG_PM_COMM, pmcomm | SUS_STATE) < 0)
+                       printk("ps2 suspend: write_reg REG_PM_COMM error\n");
+               break;
+       }
+
+       return 0;
+}
+
+static int innovator_ps2_resume(struct device *dev, u32 level)
+{
+       u8 pmcomm;
+
+       switch(level) {
+       case RESUME_ENABLE:
+
+               /*
+                * Clear SUS_STATE from REG_PM_COMM (Page 0 R0).
+                */
+
+               if (write_reg(REG_PAGENO, 0) < 0)
+                       printk("ps2 resume: write_reg REG_PAGENO error\n");
+               
+               if (read_reg(REG_PM_COMM, &pmcomm) < 0)
+                       printk("ps2 resume: read_reg REG_PM_COMM error\n");
+
+               if (write_reg(REG_PM_COMM, pmcomm & ~SUS_STATE) < 0)
+                       printk("ps2 resume: write_reg REG_PM_COMM error\n");
+
+               break;
+       }
+
+       return 0;
+}
+
+static struct device_driver innovator_ps2_driver = {
+       .name           = "innovator_ps2",
+       .bus            = &platform_bus_type,
+       .remove         = innovator_ps2_remove,
+       .suspend        = innovator_ps2_suspend,
+       .resume         = innovator_ps2_resume,
+};
+
+static struct platform_device innovator_ps2_device = {
+       .name           = "ps2",
+       .id             = -1,
+       .dev = {
+               .driver         = &innovator_ps2_driver,
+               .release        = innovator_ps2_device_release,
+       },
+};
+
+static int __init
+innovator_kbd_init(void)
+{
+       int i;
+       info("Innovator PS/2 keyboard/mouse driver v1.0\n");
+
+       innovator_fpga_hid_reset();
+
+       if ((hid = kmalloc(sizeof(struct innovator_hid_dev),
+            GFP_KERNEL)) == NULL) {
+               warn("unable to allocate space for HID device\n");
+               return -ENOMEM;
+       }
+
+       /* setup the mouse */
+       memset(hid, 0, sizeof(struct innovator_hid_dev));
+       hid->mouse.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+       hid->mouse.keybit[LONG(BTN_MOUSE)] =
+           BIT(BTN_LEFT) | BIT(BTN_RIGHT) |
+           BIT(BTN_MIDDLE) | BIT(BTN_TOUCH);
+       hid->mouse.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+       hid->mouse.private = hid;
+       hid->mouse.open = innovator_hid_open;
+       hid->mouse.close = innovator_hid_close;
+       hid->mouse.name = "innovator_mouse";
+       hid->mouse.id.bustype = 0;
+       hid->mouse.id.vendor = 0;
+       hid->mouse.id.product = 0;
+       hid->mouse.id.version = 0;
+       hid->keyboard.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+        init_input_dev(&hid->keyboard);
+        hid->keyboard.keycodesize = sizeof(unsigned char);
+        hid->keyboard.keycodemax = ARRAY_SIZE(usar2scancode);
+       for(i = 0; i < 128; i++)
+               set_bit(usar2scancode[i], hid->keyboard.keybit); 
+       hid->keyboard.private = hid;
+       hid->keyboard.open = innovator_hid_open;
+       hid->keyboard.close = innovator_hid_close;
+       hid->keyboard.name = "innovator_keyboard";
+       hid->keyboard.id.bustype = 0;
+       hid->keyboard.id.vendor = 0;
+       hid->keyboard.id.product = 0;
+       hid->keyboard.id.version = 0;
+       input_register_device(&hid->mouse);
+       input_register_device(&hid->keyboard);
+       innovator_hid_open(&hid->mouse);
+       innovator_hid_open(&hid->keyboard);
+
+       if (driver_register(&innovator_ps2_driver) != 0)
+               printk(KERN_ERR "Driver register failed for innovator_ps2\n");
+
+       if (platform_device_register(&innovator_ps2_device) != 0) {
+               printk(KERN_ERR "Device register failed for ps2\n");
+               driver_unregister(&innovator_ps2_driver);
+       }
+
+#ifdef INNOVATOR_KEYB_DEBUG
+       ctrl_dump_regs();
+#endif
+       return 0;
+}
+
+static void __exit
+innovator_kbd_exit(void)
+{
+       input_unregister_device(&hid->mouse);
+       input_unregister_device(&hid->keyboard);
+       free_irq(OMAP1510_INT_FPGA_ATN, hid);
+       if (hid != NULL)
+               kfree(hid);
+       driver_unregister(&innovator_ps2_driver);
+       platform_device_unregister(&innovator_ps2_device);
+       return;
+}
+
+module_init(innovator_kbd_init);
+module_exit(innovator_kbd_exit);
+
+MODULE_AUTHOR("George G. Davis <gdavis@mvista.com>");
+MODULE_DESCRIPTION("Innovator PS/2 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
new file mode 100644 (file)
index 0000000..d9bc34f
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * linux/drivers/char/omap-keypad.c
+ *
+ * OMAP Keypad Driver
+ *
+ * Copyright (C) 2003 Nokia Corporation
+ * Written by Timo Teräs <ext-timo.teras@nokia.com>
+ *
+ * Added support for H2 & H3 Keypad
+ * Copyright (C) 2004 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/hardware.h>
+#include <asm/io.h>
+#include <asm/errno.h>
+#include <asm/mach-types.h>
+#include <asm/arch/mux.h>
+
+#undef NEW_BOARD_LEARNING_MODE
+
+static void omap_kp_tasklet(unsigned long);
+static void omap_kp_timer(unsigned long);
+
+static struct input_dev omap_kp_dev;
+static unsigned char keypad_state[8];
+
+static struct timer_list kp_timer;
+DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
+
+#define KEY(col, row, val) (((col) << 28) | ((row) << 24) | (val))
+
+static int h2_keymap[] = {
+       KEY(0, 0, KEY_LEFT),
+       KEY(0, 1, KEY_RIGHT),
+       KEY(0, 2, KEY_3),
+       KEY(0, 3, KEY_F10),
+       KEY(0, 4, KEY_F5),
+       KEY(0, 5, KEY_9),
+       KEY(1, 0, KEY_DOWN),
+       KEY(1, 1, KEY_UP),
+       KEY(1, 2, KEY_2),
+       KEY(1, 3, KEY_F9),
+       KEY(1, 4, KEY_F7),
+       KEY(1, 5, KEY_0),
+       KEY(2, 0, KEY_ENTER),
+       KEY(2, 1, KEY_6),
+       KEY(2, 2, KEY_1),
+       KEY(2, 3, KEY_F2),
+       KEY(2, 4, KEY_F6),
+       KEY(2, 5, KEY_HOME),
+       KEY(3, 0, KEY_8),
+       KEY(3, 1, KEY_5),
+       KEY(3, 2, KEY_F12),
+       KEY(3, 3, KEY_F3),
+       KEY(3, 4, KEY_F8),
+       KEY(3, 5, KEY_END),
+       KEY(4, 0, KEY_7),
+       KEY(4, 1, KEY_4),
+       KEY(4, 2, KEY_F11),
+       KEY(4, 3, KEY_F1),
+       KEY(4, 4, KEY_F4),
+       KEY(4, 5, KEY_ESC),
+       KEY(5, 0, KEY_F13),
+       KEY(5, 1, KEY_F14),
+       KEY(5, 2, KEY_F15),
+       KEY(5, 3, KEY_F16),
+       KEY(5, 4, KEY_SLEEP),
+       0
+};
+
+static int test_keymap[] = {
+       KEY(0, 0, KEY_F4),
+       KEY(1, 0, KEY_LEFT),
+       KEY(2, 0, KEY_F1),
+       KEY(0, 1, KEY_DOWN),
+       KEY(1, 1, KEY_ENTER),
+       KEY(2, 1, KEY_UP),
+       KEY(0, 2, KEY_F3),
+       KEY(1, 2, KEY_RIGHT),
+       KEY(2, 2, KEY_F2),
+       0
+};
+
+static int innovator_keymap[] = {
+       KEY(0, 0, KEY_F1),
+       KEY(0, 3, KEY_DOWN),
+       KEY(1, 1, KEY_F2),
+       KEY(1, 2, KEY_RIGHT),
+       KEY(2, 0, KEY_F3),
+       KEY(2, 1, KEY_F4),
+       KEY(2, 2, KEY_UP),
+       KEY(3, 2, KEY_ENTER),
+       KEY(3, 3, KEY_LEFT),
+       0
+};
+
+static int osk_keymap[] = {
+       KEY(0, 0, KEY_F1),
+       KEY(0, 3, KEY_UP),
+       KEY(1, 1, KEY_LEFTCTRL),
+       KEY(1, 2, KEY_LEFT),
+       KEY(2, 0, KEY_SPACE),
+       KEY(2, 1, KEY_ESC),
+       KEY(2, 2, KEY_DOWN),
+       KEY(3, 2, KEY_ENTER),
+       KEY(3, 3, KEY_RIGHT),
+       0 
+}; 
+
+static int *keymap;
+
+static irqreturn_t omap_kp_interrupt(int irq, void *dev_id,
+                                    struct pt_regs *regs)
+{
+       /* disable keyboard interrupt and schedule for handling */
+       omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+       tasklet_schedule(&kp_tasklet);
+
+       return IRQ_HANDLED;
+}
+
+static void omap_kp_timer(unsigned long data)
+{
+       tasklet_schedule(&kp_tasklet);
+}
+
+static void omap_kp_scan_keypad(unsigned char *state)
+{
+       int col = 0;
+
+       /* read the keypad status */
+       omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+       for (col = 0; col < 8; col++) {
+               omap_writew(~(1 << col) & 0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+
+               if (machine_is_omap_osk() || machine_is_omap_h2() || machine_is_omap_h3()) {
+                       udelay(9);
+               } else {
+                       udelay(2);
+               }
+
+               state[col] = ~omap_readw(OMAP_MPUIO_BASE + OMAP_MPUIO_KBR_LATCH) & 0xff;
+       }
+       omap_writew(0x00, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+       udelay(2);
+}
+
+static inline int omap_kp_find_key(int col, int row)
+{
+       int i, key;
+
+       key = KEY(col, row, 0);
+       for (i = 0; keymap[i] != 0; i++)
+               if ((keymap[i] & 0xff000000) == key)
+                       return keymap[i] & 0x00ffffff;
+       return -1;
+}
+
+static void omap_kp_tasklet(unsigned long data)
+{
+       unsigned char new_state[8], changed, key_down = 0;
+       int col, row;
+       int spurious = 0;
+
+       /* check for any changes */
+       omap_kp_scan_keypad(new_state);
+
+       /* check for changes and print those */
+       for (col = 0; col < 8; col++) {
+               changed = new_state[col] ^ keypad_state[col];
+               key_down |= new_state[col];
+               if (changed == 0)
+                       continue;
+
+               for (row = 0; row < 8; row++) {
+                       int key;
+                       if (!(changed & (1 << row)))
+                               continue;
+#ifdef NEW_BOARD_LEARNING_MODE
+                       printk(KERN_INFO "omap-keypad: key %d-%d %s\n", col, row, (new_state[col] & (1 << row)) ? "pressed" : "released");
+#else
+                       key = omap_kp_find_key(col, row);
+                       if (key < 0) {
+                               printk(KERN_WARNING "omap-keypad: Spurious key event %d-%d\n",
+                                      col, row);
+                               /* We scan again after a couple of seconds */
+                               spurious = 1;
+                               continue;
+                       }
+
+                       input_report_key(&omap_kp_dev, key,
+                                        new_state[col] & (1 << row));
+#endif
+               }
+       }
+       memcpy(keypad_state, new_state, sizeof(keypad_state));
+
+       if (key_down) {
+                int delay = HZ / 20;
+               /* some key is pressed - keep irq disabled and use timer
+                * to poll the keypad */
+               if (spurious)
+                       delay = 2 * HZ;
+               mod_timer(&kp_timer, jiffies + delay);
+       } else {
+               /* enable interrupts */
+               omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+       }
+}
+
+static int __init omap_kp_init(void)
+{
+       int i;
+
+       printk(KERN_INFO "OMAP Keypad Driver\n");
+
+       /* Disable the interrupt for the MPUIO keyboard */
+       omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+       if (machine_is_omap_h2() || machine_is_omap_h3()) {
+               keymap = h2_keymap;
+               set_bit(EV_REP, omap_kp_dev.evbit);
+       } else if (machine_is_omap_innovator()) {
+               keymap = innovator_keymap;
+       } else if (machine_is_omap_osk()) {
+               keymap = osk_keymap;
+       } else {
+               keymap = test_keymap;
+       }
+
+       init_timer(&kp_timer);
+       kp_timer.function = omap_kp_timer;
+
+       /* get the irq and init timer*/
+       tasklet_enable(&kp_tasklet);
+       if (request_irq(INT_KEYBOARD, omap_kp_interrupt, 0,
+                       "omap-keypad", 0) < 0)
+               return -EINVAL;
+
+       /* setup input device */
+       set_bit(EV_KEY, omap_kp_dev.evbit);
+       for (i = 0; keymap[i] != 0; i++)
+               set_bit(keymap[i] & 0x00ffffff, omap_kp_dev.keybit);
+       omap_kp_dev.name = "omap-keypad";
+       input_register_device(&omap_kp_dev);
+
+       if (machine_is_omap_h2() || machine_is_omap_h3()) {
+               omap_cfg_reg(F18_1610_KBC0);
+               omap_cfg_reg(D20_1610_KBC1);
+               omap_cfg_reg(D19_1610_KBC2);
+               omap_cfg_reg(E18_1610_KBC3);
+               omap_cfg_reg(C21_1610_KBC4);
+
+               omap_cfg_reg(G18_1610_KBR0);
+               omap_cfg_reg(F19_1610_KBR1);
+               omap_cfg_reg(H14_1610_KBR2);
+               omap_cfg_reg(E20_1610_KBR3);
+               omap_cfg_reg(E19_1610_KBR4);
+               omap_cfg_reg(N19_1610_KBR5);
+
+               omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING);
+       }
+
+       /* scan current status and enable interrupt */
+       omap_kp_scan_keypad(keypad_state);
+       omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+       return 0;
+}
+
+static void __exit omap_kp_exit(void)
+{
+       /* disable keypad interrupt handling */
+       tasklet_disable(&kp_tasklet);
+       omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+       free_irq(INT_KEYBOARD, 0);
+       del_timer_sync(&kp_timer);
+
+       /* unregister everything */
+       input_unregister_device(&omap_kp_dev);
+}
+
+module_init(omap_kp_init);
+module_exit(omap_kp_exit);
+
+MODULE_AUTHOR("Timo Teräs");
+MODULE_DESCRIPTION("OMAP Keypad Driver");
+MODULE_LICENSE("GPL");
index 7e991274ea400beb5d8eaaf9ab72a72755469082..9329982b14ae192eef83e2f2b1b926f943b69660 100644 (file)
@@ -96,3 +96,17 @@ config TOUCHSCREEN_HP600
          module will be called hp680_ts_input.
 
 endif
+config TOUCHSCREEN_OMAP
+       tristate "OMAP touchscreen input driver"
+       depends on INPUT && INPUT_TOUCHSCREEN
+       help
+         Say Y here if you have an OMAP based board with touchscreen
+         attached to it, e.g. OMAP Innovator, OSK, H2 or H3
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called omap_ts.
+
+
+
index 6842869c9a263d570b0ace2c53ace0e461cac9dc..a904d43d68ea49d4751f412001bb70b734653ba0 100644 (file)
@@ -1,5 +1,5 @@
 #
-# Makefile for the mouse drivers.
+# Makefile for the touchscreen input drivers.
 #
 
 # Each configuration option enables a list of files.
@@ -11,3 +11,4 @@ obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
 obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
 obj-$(CONFIG_TOUCHSCREEN_MK712)        += mk712.o
 obj-$(CONFIG_TOUCHSCREEN_HP600)        += hp680_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_OMAP) += omap/
diff --git a/drivers/input/touchscreen/omap/Makefile b/drivers/input/touchscreen/omap/Makefile
new file mode 100644 (file)
index 0000000..6d39b66
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# Makefile for the OMAP touchscreen input driver
+#
+
+obj-$(CONFIG_TOUCHSCREEN_OMAP) += omapts.o
+
+objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_H2) += ts_hx.o
+objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_H3) += ts_hx.o
+objs-$(CONFIG_ARCH_OMAP1510)$(CONFIG_MACH_OMAP_INNOVATOR) += ts_inn1510.o
+objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_OSK) += ts_osk.o
+
+omapts-objs := omap_ts.o $(objs-yy)
diff --git a/drivers/input/touchscreen/omap/ads7846.h b/drivers/input/touchscreen/omap/ads7846.h
new file mode 100644 (file)
index 0000000..ae4347a
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * ads7846.h - header file for ADS7846 touchscreen controller
+ * 
+ * Copyright 2002 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *             stevel@mvista.com or source@mvista.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ADS7846_H
+#define __ADS7846_H
+
+// ADS7846 Control Byte bit defines
+#define ADS7846_S         (1<<7)
+#define ADS7846_ADDR_BIT  4
+#define ADS7846_ADDR_MASK (0x7<<ADS7846_ADDR_BIT)
+#define   ADS7846_MEASURE_X  (0x5<<ADS7846_ADDR_BIT)
+#define   ADS7846_MEASURE_Y  (0x1<<ADS7846_ADDR_BIT)
+#define   ADS7846_MEASURE_Z1 (0x3<<ADS7846_ADDR_BIT)
+#define   ADS7846_MEASURE_Z2 (0x4<<ADS7846_ADDR_BIT)
+#define ADS7846_8BITS     (1<<3)
+#define ADS7846_12BITS    0
+#define ADS7846_SER       (1<<2)
+#define ADS7846_DFR       0
+#define ADS7846_PWR_BIT   0
+#define   ADS7846_PD      0
+#define   ADS7846_ADC_ON  (0x1<<ADS7846_PWR_BIT)
+#define   ADS7846_REF_ON  (0x2<<ADS7846_PWR_BIT)
+#define   ADS7846_REF_ADC_ON (0x3<<ADS7846_PWR_BIT)
+
+#define MEASURE_12BIT_X \
+  (ADS7846_S | ADS7846_MEASURE_X | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
+#define MEASURE_12BIT_Y \
+  (ADS7846_S | ADS7846_MEASURE_Y | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
+#define MEASURE_12BIT_Z1 \
+  (ADS7846_S | ADS7846_MEASURE_Z1 | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
+#define MEASURE_12BIT_Z2 \
+  (ADS7846_S | ADS7846_MEASURE_Z2 | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
+
+#endif /* __ADS7846_H */
diff --git a/drivers/input/touchscreen/omap/omap_ts.c b/drivers/input/touchscreen/omap/omap_ts.c
new file mode 100644 (file)
index 0000000..bb594df
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * input/touchscreen/omap/omap_ts.c
+ *
+ * touchscreen input device driver for various TI OMAP boards
+ * Copyright (c) 2002 MontaVista Software Inc.
+ * Copyright (c) 2004 Texas Instruments, Inc.
+ * Cleanup and modularization 2004 by Dirk Behme <dirk.behme@de.bosch.com>
+ *
+ * Assembled using driver code copyright the companies above.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * History:
+ * 12/12/2004    Srinath Modified and intergrated code for H2 and H3
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/suspend.h>
+#include <linux/device.h>
+
+#include <asm/mach-types.h>
+
+//#define DEBUG
+
+#include "omap_ts.h"
+
+#define OMAP_TS_NAME   "omap_ts"
+
+static struct ts_device *__initdata ts_devs[] = {
+#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3)
+       &hx_ts,
+#endif
+#ifdef CONFIG_MACH_OMAP_OSK
+       &osk_ts,
+#endif
+#if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP1510)
+       &innovator1510_ts,
+#endif
+};
+
+static struct omap_ts_t ts_omap;
+
+static int omap_ts_read(void)
+{
+       u16 data[4] = { 0, 0, 0, 0 };
+
+       ts_omap.dev->read(data);
+
+       input_report_abs(&(ts_omap.inputdevice), ABS_X, data[0]);
+       input_report_abs(&(ts_omap.inputdevice), ABS_Y, data[1]);
+       input_report_abs(&(ts_omap.inputdevice), ABS_PRESSURE, data[2]);
+       input_sync(&(ts_omap.inputdevice));
+
+       DEBUG_TS("omap_ts_read: read x=%d,y=%d,p=%d\n", data[0], data[1],
+                data[2]);
+
+       return 0;
+}
+
+static void omap_ts_timer(unsigned long data)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ts_omap.lock, flags);
+
+       if (!ts_omap.dev->penup()) {
+               if (!ts_omap.touched) {
+                       DEBUG_TS("omap_ts_timer: pen down\n");
+                       input_report_key(&(ts_omap.inputdevice), BTN_TOUCH, 1);
+               }
+               ts_omap.touched = 1;
+               omap_ts_read();
+               ts_omap.ts_timer.expires = jiffies + HZ / 100;
+               add_timer(&(ts_omap.ts_timer));
+       } else {
+               if (ts_omap.touched) {
+                       DEBUG_TS("omap_ts_timer: pen up\n");
+                       ts_omap.touched = 0;
+                       input_report_abs(&(ts_omap.inputdevice), ABS_X, 0);
+                       input_report_abs(&(ts_omap.inputdevice), ABS_Y, 0);
+                       input_report_abs(&(ts_omap.inputdevice), ABS_PRESSURE,
+                                        0);
+                       input_sync(&(ts_omap.inputdevice));
+                       input_report_key(&(ts_omap.inputdevice), BTN_TOUCH, 0);
+               }
+               if (!ts_omap.irq_enabled) {
+                       ts_omap.irq_enabled = 1;
+                       enable_irq(ts_omap.irq);
+               }
+       }
+
+       spin_unlock_irqrestore(&ts_omap.lock, flags);
+}
+
+static irqreturn_t omap_ts_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       spin_lock(&ts_omap.lock);
+
+       if (ts_omap.irq_enabled) {
+               ts_omap.irq_enabled = 0;
+               disable_irq(irq);
+       }
+       // restart acquire
+       ts_omap.ts_timer.expires = jiffies + HZ / 100;
+       add_timer(&(ts_omap.ts_timer));
+
+       spin_unlock(&ts_omap.lock);
+
+       return IRQ_HANDLED;
+}
+
+static int __init omap_ts_probe(struct device *dev)
+{
+       int i;
+       int status = -ENODEV;
+
+       memset(&ts_omap, 0, sizeof(ts_omap));
+       spin_lock_init(&ts_omap.lock);
+
+       for (i = 0; i < ARRAY_SIZE(ts_devs); i++) {
+               if (!ts_devs[i] || !ts_devs[i]->probe)
+                       continue;
+               status = ts_devs[i]->probe(&ts_omap);
+               if (status == 0) {
+                       ts_omap.dev = ts_devs[i];
+                       break;
+               }
+       }
+
+       if (status != 0)
+               return status;
+
+       // Init acquisition timer function
+       init_timer(&ts_omap.ts_timer);
+       ts_omap.ts_timer.function = omap_ts_timer;
+
+       /* request irq */
+       if (ts_omap.irq != -1) {
+               if (request_irq(ts_omap.irq, omap_ts_handler, 0,
+                               OMAP_TS_NAME, &ts_omap)) {
+                       printk(KERN_ERR
+         "omap_ts.c: Could not allocate touchscreen IRQ!\n");
+                       ts_omap.irq = -1;
+                       return -EINVAL;
+               }
+               ts_omap.irq_enabled = 1;
+       } else {
+               printk(KERN_ERR "omap_ts.c: No touchscreen IRQ assigned!\n");
+               return -EINVAL;
+       }
+
+       init_input_dev(&(ts_omap.inputdevice));
+       ts_omap.inputdevice.name = OMAP_TS_NAME;
+       ts_omap.inputdevice.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+       ts_omap.inputdevice.keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH);
+       ts_omap.inputdevice.absbit[0] =
+           BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+       input_register_device(&(ts_omap.inputdevice));
+
+       ts_omap.dev->enable();
+
+       printk("OMAP touchscreen driver initialized\n");
+
+       return 0;
+}
+
+static int __exit omap_ts_remove(struct device *dev)
+{
+       ts_omap.dev->disable();
+       input_unregister_device(&ts_omap.inputdevice);
+       if (ts_omap.irq != -1)
+               free_irq(ts_omap.irq, &ts_omap);
+
+       ts_omap.dev->remove();
+
+       return 0;
+}
+
+static int omap_ts_suspend(struct device *dev, pm_message_t mesg, u32 level)
+{
+       if (level != SUSPEND_POWER_DOWN) {
+               return 0;
+       }
+
+       ts_omap.dev->disable();
+       return 0;
+}
+
+static int omap_ts_resume(struct device *dev, u32 level)
+{
+       if (level != RESUME_POWER_ON) {
+               return 0;
+       }
+
+       ts_omap.dev->enable();
+       return 0;
+}
+
+static void omap_ts_device_release(struct device *dev)
+{
+       /* Nothing */
+}
+
+static struct device_driver omap_ts_driver = {
+       .name           = OMAP_TS_NAME,
+       .bus            = &platform_bus_type,
+       .probe          = omap_ts_probe,
+       .remove         = __exit_p(omap_ts_remove),
+       .suspend        = omap_ts_suspend,
+       .resume         = omap_ts_resume,
+};
+
+static struct platform_device omap_ts_device = {
+       .name           = OMAP_TS_NAME,
+       .id             = -1,
+       .dev = {
+               .release        = omap_ts_device_release,
+       },
+};
+
+static int __init omap_ts_init(void)
+{
+       int ret;
+
+       ret = platform_device_register(&omap_ts_device);
+       if (ret != 0)
+               return -ENODEV;
+
+       ret = driver_register(&omap_ts_driver);
+       if (ret != 0) {
+               platform_device_unregister(&omap_ts_device);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void __exit omap_ts_exit(void)
+{
+       driver_unregister(&omap_ts_driver);
+       platform_device_unregister(&omap_ts_device);
+}
+
+module_init(omap_ts_init);
+module_exit(omap_ts_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/omap/omap_ts.h b/drivers/input/touchscreen/omap/omap_ts.h
new file mode 100644 (file)
index 0000000..718b0c7
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * omap_ts.h - header file for OMAP touchscreen support
+ * 
+ * Copyright (c) 2002 MontaVista Software Inc.
+ * Copyright (c) 2004 Texas Instruments, Inc.
+ *
+ * Assembled using driver code copyright the companies above.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __OMAP_TS_H
+#define __OMAP_TS_H
+
+#ifdef DEBUG
+#define DEBUG_TS(fmt...)   printk(fmt)
+#else
+#define DEBUG_TS(fmt...)   do { } while (0)
+#endif
+
+struct omap_ts_t;
+
+struct ts_device {
+        int  (*probe)   (struct omap_ts_t *);
+        void (*read)    (u16 *);
+        void (*enable)  (void);
+        void (*disable) (void);
+        void (*remove)  (void);
+        int  (*penup)  (void);
+};
+
+struct omap_ts_t{
+       struct input_dev inputdevice;
+       struct timer_list ts_timer;      // Timer for triggering acquisitions
+       int touched;
+       int irq;
+       int irq_enabled;
+       struct ts_device *dev;
+       spinlock_t lock;
+};
+
+extern struct ts_device hx_ts;
+extern struct ts_device osk_ts;
+extern struct ts_device innovator1510_ts;
+
+#endif /* __OMAP_TS_H */
diff --git a/drivers/input/touchscreen/omap/ts_hx.c b/drivers/input/touchscreen/omap/ts_hx.c
new file mode 100644 (file)
index 0000000..813aef0
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * input/touchscreen/omap/ts_hx.c
+ * touchscreen support for OMAP H3 and H2  boards
+ *
+ * Copyright (c) 2002 MontaVista Software Inc.
+ * Copyright (c) 2004 Texas Instruments, Inc.
+ *
+ * Assembled using driver code copyright the companies above.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * History:
+ * 9/12/2004   Srinath Modified and integrated  H2 and H3 code
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/device.h>
+#include <asm/mach-types.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/hardware.h>
+#include <asm/hardware/tsc2101.h>
+
+#include "../drivers/ssi/omap-tsc2101.h"
+#include "omap_ts.h"
+
+#define        H2_GPIO_NUM             4
+#define        H3_GPIO_NUM             48
+
+#define OMAP_TSC2101_XRES                     500
+#define TOUCHSCREEN_DATA_REGISTERS_PAGE         0x0
+#define TOUCHSCREEN_CONTROL_REGISTERS_PAGE      0x1
+#define OMAP_TSC2101_READ_MAX             0x4
+#define TSC2101_GETSTATUS(ret)           (((ret) >> 11) & 0x1)
+#define TSC2101_MASKVAL                         0xFFF
+#define TSC2101_PRESSUREVAL(x)           ((x) << 12)
+
+static int hx_ts_penup(void);
+static int hx_ts_probe(struct omap_ts_t *ts);
+static void hx_ts_read(u16 * data);
+static void hx_ts_enable(void);
+static void hx_ts_disable(void);
+#ifdef MODULE
+static void hx_ts_remove(void);
+#endif
+
+struct ts_device hx_ts = {
+       .probe          = hx_ts_probe,
+       .read           = hx_ts_read,
+       .enable         = hx_ts_enable,
+       .disable        = hx_ts_disable,
+       .remove         = __exit_p(hx_ts_remove),
+       .penup          = hx_ts_penup,
+};
+
+static int hx_ts_penup(void)
+{
+       int ret = 0;
+       /* Read the status register */
+       ret = omap_tsc2101_read(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+                               TSC2101_TS_STATUS);
+       /* Check for availability of data in status register */
+       ret = TSC2101_GETSTATUS(ret);
+       return !ret;
+
+}
+
+static int __init hx_ts_probe(struct omap_ts_t *ts)
+{
+       unsigned        gpio;
+
+       if (machine_is_omap_h2()) {
+               gpio = H2_GPIO_NUM;
+               omap_cfg_reg(P20_1610_GPIO4);
+       } else if (machine_is_omap_h3()) {
+               gpio = H3_GPIO_NUM;
+               omap_cfg_reg(W19_1610_GPIO48);
+       } else
+               return -ENODEV;
+
+       ts->irq = OMAP_GPIO_IRQ(gpio);
+       if (omap_request_gpio(gpio) != 0) {
+               printk(KERN_ERR "hX_ts_init.c: Could not reserve GPIO!\n");
+               return -EINVAL;
+       };
+
+       omap_set_gpio_direction(gpio, 1);
+       omap_set_gpio_edge_ctrl(gpio, OMAP_GPIO_FALLING_EDGE);
+       return 0;
+}
+
+static void hx_ts_read(u16 * values)
+{
+       s32 t, p = 0;
+       int i;
+
+       /* Read X, Y, Z1 and Z2 */
+       omap_tsc2101_reads(TOUCHSCREEN_DATA_REGISTERS_PAGE, TSC2101_TS_X,
+                          values, OMAP_TSC2101_READ_MAX);
+
+       for (i = 0; i < OMAP_TSC2101_READ_MAX; i++)
+               values[i] &= TSC2101_MASKVAL;
+
+       /* Calculate Pressure */
+       if (values[TSC2101_TS_Z1] != 0) {
+               t = ((OMAP_TSC2101_XRES * values[TSC2101_TS_X]) *
+                    (values[TSC2101_TS_Z2] - values[TSC2101_TS_Z1]));
+               p = t / (u32) (TSC2101_PRESSUREVAL(values[TSC2101_TS_Z1]));
+               if (p < 0)
+                       p = 0;
+       }
+
+       values[TSC2101_TS_Z1] = p;
+}
+
+static void hx_ts_enable(void)
+{
+       int ret = omap_tsc2101_enable();
+       if (ret) {
+               printk(KERN_ERR "FAILED TO INITIALIZE TSC CODEC\n");
+               return;
+       }
+
+       /* PINTDAV is data available only */
+       omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+                          TSC2101_TS_STATUS, TSC2101_DATA_AVAILABLE);
+       /* disable buffer mode */
+       omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+                          TSC2101_TS_BUFFER_CTRL, TSC2101_BUFFERMODE_DISABLE);
+       /* use internal reference, 100 usec power-up delay,
+        *        * power down between conversions, 1.25V internal reference */
+       omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+                          TSC2101_TS_REF_CTRL, TSC2101_REF_POWERUP);
+       /* enable touch detection, 84usec precharge time, 32 usec sense time */
+       omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+                          TSC2101_TS_CONFIG_CTRL, TSC2101_ENABLE_TOUCHDETECT);
+       /* 3 msec conversion delays  */
+       omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+                          TSC2101_TS_PROG_DELAY, TSC2101_PRG_DELAY);
+       /*
+        * TSC2101-controlled conversions
+        * 12-bit samples
+        * continuous X,Y,Z1,Z2 scan mode
+        * average (mean) 4 samples per coordinate
+        * 1 MHz internal conversion clock
+        * 500 usec panel voltage stabilization delay
+        */
+       omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+                          TSC2101_TS_ADC_CTRL, TSC2101_ADC_CONTROL);
+
+       return;
+
+}
+
+static void hx_ts_disable(void)
+{
+       /* stop conversions and power down */
+       omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+                          TSC2101_TS_ADC_CTRL, TSC2101_ADC_POWERDOWN);
+       omap_tsc2101_disable();
+}
+
+#ifdef MODULE
+static void __exit hx_ts_remove(void)
+{
+       if (machine_is_omap_h2())
+               omap_free_gpio(H2_GPIO_NUM);
+       else if (machine_is_omap_h3())
+               omap_free_gpio(H3_GPIO_NUM);
+}
+#endif
diff --git a/drivers/input/touchscreen/omap/ts_inn1510.c b/drivers/input/touchscreen/omap/ts_inn1510.c
new file mode 100644 (file)
index 0000000..b85f003
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * ts_inn1510.c - touchscreen support for OMAP1510 Innovator board
+ * 
+ * Copyright 2002 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *             stevel@mvista.com or source@mvista.com
+ *
+ * The touchscreen hardware on the Innovator consists of an FPGA
+ * register which is bit-banged to generate SPI-like transactions
+ * to an ADS7846 touch screen controller.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/input.h>
+#include <linux/device.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/arch/fpga.h>
+
+#include "omap_ts.h"
+#include "ads7846.h"
+
+// The Touch Screen Register on Innovator FPGA
+#define FPGA_TS_BCLK     (1<<0)
+#define FPGA_TS_BDIN     (1<<1)
+#define FPGA_TS_BCS      (1<<2)
+#define FPGA_TS_BBUSY    (1<<3)
+#define FPGA_TS_BOUT     (1<<4)
+#define FPGA_TS_BPENUP   (1<<5)
+
+#define X_PLATE_OHMS 419
+#define Y_PLATE_OHMS 486
+
+static int inn1510_ts_penup(void);
+static int inn1510_ts_probe(struct omap_ts_t *ts);
+static void inn1510_ts_read(u16 * data);
+static void inn1510_ts_enable(void);
+static void inn1510_ts_disable(void);
+#ifdef MODULE
+static void inn1510_ts_remove(void);
+#endif
+
+struct ts_device innovator1510_ts = {
+        .probe   = inn1510_ts_probe,
+        .read    = inn1510_ts_read,
+        .enable  = inn1510_ts_enable,
+        .disable = inn1510_ts_disable,
+        .remove  = __exit_p(inn1510_ts_remove),
+       .penup   = inn1510_ts_penup,
+};
+
+static inline u8 fpga_ts_read(void)
+{
+       return fpga_read(OMAP1510_FPGA_TOUCHSCREEN);
+}
+
+static inline void fpga_ts_write(u8 val)
+{
+       fpga_write(val, OMAP1510_FPGA_TOUCHSCREEN);
+}
+
+static inline void fpga_ts_set_bits(u8 mask)
+{
+       fpga_ts_write(fpga_ts_read() | mask);
+}
+
+static inline void fpga_ts_clear_bits(u8 mask)
+{
+       fpga_ts_write(fpga_ts_read() & ~mask);
+}
+
+static inline void CS_H(void)
+{
+       // EPLD inverts active low signals.
+       fpga_ts_clear_bits(FPGA_TS_BCS);
+}
+
+static inline void CS_L(void)
+{
+       fpga_ts_set_bits(FPGA_TS_BCS);
+}
+
+static inline void SCLK_L(void)
+{
+       fpga_ts_clear_bits(FPGA_TS_BCLK);
+}
+
+static inline void SCLK_H(void)
+{
+       fpga_ts_set_bits(FPGA_TS_BCLK);
+}
+
+static inline void SDI_L(void)
+{
+       fpga_ts_clear_bits(FPGA_TS_BDIN);
+}
+
+static inline void SDI_H(void)
+{
+       fpga_ts_set_bits(FPGA_TS_BDIN);
+}
+
+static inline int BUSY(void)
+{
+       return (((fpga_ts_read() & FPGA_TS_BBUSY) == 0) ? 1 : 0) ;
+}
+
+static inline u8 DOUT(void)
+{       
+       return ((fpga_ts_read() & FPGA_TS_BOUT) ? 1 : 0) ;
+}
+
+static u16 ads7846_do(u8 cmd)
+{  
+       int i;
+       u16 val=0;
+
+       SCLK_L() ;
+       SDI_L();
+       CS_L() ;        // enable the chip select
+
+       // send the command to the ADS7846
+       for (i=0; i<8; i++ ) {
+               if (cmd & 0x80)
+                       SDI_H();
+               else
+                       SDI_L();   // prepare the data on line sdi OR din
+
+               SCLK_H() ;      // clk in the data
+               cmd <<= 1 ;
+               SCLK_L() ;
+       }
+
+       SDI_L();
+       while (BUSY())
+               ;
+
+       // now read returned data
+       for (i=0 ; i<16 ; i++ ) {
+               SCLK_L() ;
+               
+               if (i < 12) {
+                       val <<= 1 ;
+                       val |= DOUT();
+               }
+               SCLK_H() ;
+       }
+
+       SCLK_L() ;
+       CS_H() ;   // disable the chip select
+
+       return val;
+}
+
+static int inn1510_ts_penup(void)
+{
+       return ((fpga_ts_read() & FPGA_TS_BPENUP) ? 0 : 1) ;
+}
+
+static int __init inn1510_ts_probe(struct omap_ts_t *ts)
+{
+       if (!cpu_is_omap15xx() || !machine_is_omap_innovator())
+               return -ENODEV;
+
+       ts->irq = OMAP1510_INT_FPGA_TS;
+       
+       return 0;
+}
+
+static void inn1510_ts_read(u16 *data)
+{
+       unsigned int Rt = 0;
+
+       data[0] = ads7846_do(MEASURE_12BIT_X);
+       data[1] = ads7846_do(MEASURE_12BIT_Y); 
+       data[2] = ads7846_do(MEASURE_12BIT_Z1); 
+       data[3] = ads7846_do(MEASURE_12BIT_Z2); 
+
+       // Calculate touch pressure resistance
+       if (data[2]) {
+               Rt = (X_PLATE_OHMS * (u32)data[0] *
+                    ((u32)data[3] - (u32)data[2])) / (u32)data[2];
+
+               Rt = (Rt + 2048) >> 12; // round up to nearest ohm
+       }
+
+       data[2] = Rt;
+}
+
+static void inn1510_ts_enable(void)
+{
+
+}
+
+static void inn1510_ts_disable(void)
+{
+
+}
+
+#ifdef MODULE
+static void __exit inn1510_ts_remove(void)
+{
+       /* Nothing to do here */
+}
+#endif
diff --git a/drivers/input/touchscreen/omap/ts_osk.c b/drivers/input/touchscreen/omap/ts_osk.c
new file mode 100644 (file)
index 0000000..d05afc6
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * ts_osk.c - touchscreen support for OMAP OSK board
+ * 
+ * Copyright 2002 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *             stevel@mvista.com or source@mvista.com
+ *
+ * The touchscreen hardware on the OSK uses OMAP5912 uWire interface,
+ * GPIO4 (/PENIRQ) and GPIO6 (BUSY) to connect to an ADS7846 
+ * touch screen controller. GPIO6 doesn't seem to be necessary here.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/input.h>
+#include <linux/device.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/gpio.h>
+#include <asm/mach-types.h>
+
+#include "../drivers/ssi/omap-uwire.h"
+
+#include "omap_ts.h"
+#include "ads7846.h"
+
+// /PENIRQ on GPIO4 on OSK
+#define PEN_IRQ                 OMAP_GPIO_IRQ(4)
+
+// ADS7846 is on OSK uWire CS 0
+#define ADS7846_UWIRE_CS       0
+#define UWIRE_LEAVE_CS         1
+
+#define X_PLATE_OHMS 419
+#define Y_PLATE_OHMS 486
+
+static int osk_ts_penup(void);
+static int osk_ts_probe(struct omap_ts_t *ts);
+static void osk_ts_read(u16 * data);
+static void osk_ts_enable(void);
+static void osk_ts_disable(void);
+#ifdef MODULE
+static void osk_ts_remove(void);
+#endif
+
+struct ts_device osk_ts = {
+        .probe   = osk_ts_probe,
+        .read    = osk_ts_read,
+        .enable  = osk_ts_enable,
+        .disable = osk_ts_disable,
+        .remove  = __exit_p(osk_ts_remove),
+       .penup   = osk_ts_penup,
+};
+
+static u16 ads7846_do(u8 cmd)
+{
+       u16 val = 0;
+       
+       // send the command to the ADS7846, leave CS active after this
+       omap_uwire_data_transfer(ADS7846_UWIRE_CS, cmd, 8, 0, NULL, UWIRE_LEAVE_CS);
+
+       // now read returned data
+        omap_uwire_data_transfer(ADS7846_UWIRE_CS, 0, 0, 16, &val, !UWIRE_LEAVE_CS);
+       
+       return val;
+}
+
+static int osk_ts_penup(void)
+{
+       return (omap_get_gpio_datain(4));
+}
+
+static int  __init osk_ts_probe(struct omap_ts_t *ts)
+{
+       if (!machine_is_omap_osk())
+               return -ENODEV;
+
+        /* Configure GPIO4 (pin M17 ZDY) as /PENIRQ interrupt input */
+        omap_cfg_reg(P20_1610_GPIO4);
+       omap_request_gpio(4);
+       omap_set_gpio_direction(4, 1);
+       omap_set_gpio_edge_ctrl(4, OMAP_GPIO_FALLING_EDGE);
+
+       ts->irq = PEN_IRQ;
+
+       /* Configure uWire interface. ADS7846 is on CS0 */
+       omap_uwire_configure_mode(ADS7846_UWIRE_CS, UWIRE_READ_RISING_EDGE |
+                                                   UWIRE_WRITE_RISING_EDGE |
+                                                   UWIRE_CS_ACTIVE_LOW |
+                                                   UWIRE_FREQ_DIV_2);
+       
+       return 0;
+}
+
+static void osk_ts_read(u16 *data)
+{
+       unsigned int Rt = 0;
+
+       data[0] = ads7846_do(MEASURE_12BIT_X);
+       data[1] = ads7846_do(MEASURE_12BIT_Y); 
+       data[2] = ads7846_do(MEASURE_12BIT_Z1); 
+       data[3] = ads7846_do(MEASURE_12BIT_Z2); 
+
+       // Calculate touch pressure resistance
+       if (data[2]) {
+               Rt = (X_PLATE_OHMS * (u32)data[0] *
+                    ((u32)data[3] - (u32)data[2])) / (u32)data[2];
+
+               Rt = (Rt + 2048) >> 12; // round up to nearest ohm
+       }
+
+       /* 
+        * Raw OSK touchscreen data values are between ~4000 and
+        * ~60000. This seems to be to large for calibration 
+        * systems (e.g. tslib). Make the values smaller.
+        */
+       data[0] = data[0] >> 4;
+       data[1] = data[1] >> 4;
+
+       data[2] = Rt;
+}
+
+static void osk_ts_enable(void)
+{
+
+}
+
+static void osk_ts_disable(void)
+{
+
+}
+
+#ifdef MODULE
+static void __exit osk_ts_remove(void)
+{
+       omap_free_gpio(4);
+}
+#endif