From 6425efa7e9971c4ba09dfcf7e96b810cb3c7148f Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 9 May 2005 14:28:04 -0700 Subject: [PATCH] Add various OMAP input drivers Adds various OMAP input drivers. Signed-off-by: Tony Lindgren --- drivers/input/keyboard/Kconfig | 20 + drivers/input/keyboard/Makefile | 3 +- drivers/input/keyboard/innovator_ps2.c | 1303 +++++++++++++++++++ drivers/input/keyboard/omap-keypad.c | 312 +++++ drivers/input/touchscreen/Kconfig | 14 + drivers/input/touchscreen/Makefile | 3 +- drivers/input/touchscreen/omap/Makefile | 12 + drivers/input/touchscreen/omap/ads7846.h | 53 + drivers/input/touchscreen/omap/omap_ts.c | 266 ++++ drivers/input/touchscreen/omap/omap_ts.h | 58 + drivers/input/touchscreen/omap/ts_hx.c | 184 +++ drivers/input/touchscreen/omap/ts_inn1510.c | 218 ++++ drivers/input/touchscreen/omap/ts_osk.c | 149 +++ 13 files changed, 2593 insertions(+), 2 deletions(-) create mode 100644 drivers/input/keyboard/innovator_ps2.c create mode 100644 drivers/input/keyboard/omap-keypad.c create mode 100644 drivers/input/touchscreen/omap/Makefile create mode 100644 drivers/input/touchscreen/omap/ads7846.h create mode 100644 drivers/input/touchscreen/omap/omap_ts.c create mode 100644 drivers/input/touchscreen/omap/omap_ts.h create mode 100644 drivers/input/touchscreen/omap/ts_hx.c create mode 100644 drivers/input/touchscreen/omap/ts_inn1510.c create mode 100644 drivers/input/touchscreen/omap/ts_osk.c diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index e55dee39077..2c8413337de 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -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. + diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index b02eeceea3c..a2f928a763f 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -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 index 00000000000..de07ce99a8b --- /dev/null +++ b/drivers/input/keyboard/innovator_ps2.c @@ -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. + * or + * + * + * 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 + * 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 + * + * 20040812: Thiago Radicchi + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 "); +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 index 00000000000..d9bc34f45ae --- /dev/null +++ b/drivers/input/keyboard/omap-keypad.c @@ -0,0 +1,312 @@ +/* + * linux/drivers/char/omap-keypad.c + * + * OMAP Keypad Driver + * + * Copyright (C) 2003 Nokia Corporation + * Written by Timo Teräs + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 7e991274ea4..9329982b14a 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -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. + + + diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 6842869c9a2..a904d43d68e 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -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 index 00000000000..6d39b66753c --- /dev/null +++ b/drivers/input/touchscreen/omap/Makefile @@ -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 index 00000000000..ae4347a8f68 --- /dev/null +++ b/drivers/input/touchscreen/omap/ads7846.h @@ -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< + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//#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 index 00000000000..718b0c7eede --- /dev/null +++ b/drivers/input/touchscreen/omap/omap_ts.h @@ -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 index 00000000000..813aef0b521 --- /dev/null +++ b/drivers/input/touchscreen/omap/ts_hx.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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 index 00000000000..b85f0032771 --- /dev/null +++ b/drivers/input/touchscreen/omap/ts_inn1510.c @@ -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 +#include +#include +#include +#include + +#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 index 00000000000..d05afc63557 --- /dev/null +++ b/drivers/input/touchscreen/omap/ts_osk.c @@ -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 +#include +#include +#include +#include + +#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 -- 2.41.1