From b67b4b117746aef686e527c3205792db0f2c9e16 Mon Sep 17 00:00:00 2001 From: Dominic Curran Date: Mon, 27 Oct 2008 22:30:53 -0400 Subject: Input: gpio-keys - add flag to allow auto repeat This patch adds a flag to gpio-key driver to turn on the input subsystems auto repeat feature if needed. Signed-off-by: Dominic Curran Signed-off-by: Dmitry Torokhov --- include/linux/gpio_keys.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index ec6ecd74781d..1289fa7623ca 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -15,6 +15,7 @@ struct gpio_keys_button { struct gpio_keys_platform_data { struct gpio_keys_button *buttons; int nbuttons; + unsigned int rep:1; /* enable input subsystem auto repeat */ }; #endif -- cgit v1.2.3 From 3eb1aa43ef5cb871ba3fb2f08633675eca374d2e Mon Sep 17 00:00:00 2001 From: Jaya Kumar Date: Wed, 19 Nov 2008 16:58:50 -0500 Subject: Input: add support for Wacom W8001 penabled serial touchscreen The Wacom W8001 sensor is a sensor device (uses electromagnetic resonance) and it is interfaced via its serial microcontroller to the host. Signed-off-by: Jaya Kumar Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 13 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/wacom_w8001.c | 325 ++++++++++++++++++++++++++++++++ include/linux/serio.h | 1 + 4 files changed, 340 insertions(+) create mode 100644 drivers/input/touchscreen/wacom_w8001.c (limited to 'include') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 3d1ab8fa9acc..20eb52ed176d 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -95,6 +95,19 @@ config TOUCHSCREEN_ELO To compile this driver as a module, choose M here: the module will be called elo. +config TOUCHSCREEN_WACOM_W8001 + tristate "Wacom W8001 penabled serial touchscreen" + select SERIO + help + Say Y here if you have an Wacom W8001 penabled serial touchscreen + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called wacom_w8001. + + config TOUCHSCREEN_MTOUCH tristate "MicroTouch serial touchscreens" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 15cf29079489..3dc84d3846c1 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o +obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c new file mode 100644 index 000000000000..2f33a0167644 --- /dev/null +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -0,0 +1,325 @@ +/* + * Wacom W8001 penabled serial touchscreen driver + * + * Copyright (c) 2008 Jaya Kumar + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + * Layout based on Elo serial touchscreen driver by Vojtech Pavlik + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Wacom W8001 serial touchscreen driver" + +MODULE_AUTHOR("Jaya Kumar "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define W8001_MAX_LENGTH 11 +#define W8001_PACKET_LEN 11 +#define W8001_LEAD_MASK 0x80 +#define W8001_LEAD_BYTE 0x80 +#define W8001_TAB_MASK 0x40 +#define W8001_TAB_BYTE 0x40 + +#define W8001_QUERY_PACKET 0x20 + +struct w8001_coord { + u8 rdy; + u8 tsw; + u8 f1; + u8 f2; + u16 x; + u16 y; + u16 pen_pressure; + u8 tilt_x; + u8 tilt_y; +}; + +/* + * Per-touchscreen data. + */ + +struct w8001 { + struct input_dev *dev; + struct serio *serio; + struct mutex cmd_mutex; + struct completion cmd_done; + int id; + int idx; + unsigned char expected_packet; + unsigned char data[W8001_MAX_LENGTH]; + unsigned char response[W8001_PACKET_LEN]; + char phys[32]; +}; + +static int parse_data(u8 *data, struct w8001_coord *coord) +{ + coord->rdy = data[0] & 0x20; + coord->tsw = data[0] & 0x01; + coord->f1 = data[0] & 0x02; + coord->f2 = data[0] & 0x04; + + coord->x = (data[1] & 0x7F) << 9; + coord->x |= (data[2] & 0x7F) << 2; + coord->x |= (data[6] & 0x60) >> 5; + + coord->y = (data[3] & 0x7F) << 9; + coord->y |= (data[4] & 0x7F) << 2; + coord->y |= (data[6] & 0x18) >> 3; + + coord->pen_pressure = data[5] & 0x7F; + coord->pen_pressure |= (data[6] & 0x07) << 7 ; + + coord->tilt_x = data[7] & 0x7F; + coord->tilt_y = data[8] & 0x7F; + + return 0; +} + +static void w8001_process_data(struct w8001 *w8001, unsigned char data) +{ + struct input_dev *dev = w8001->dev; + u8 tmp; + struct w8001_coord coord; + + w8001->data[w8001->idx] = data; + switch (w8001->idx++) { + case 0: + if ((data & W8001_LEAD_MASK) != W8001_LEAD_BYTE) { + pr_debug("w8001: unsynchronized data: 0x%02x\n", data); + w8001->idx = 0; + } + break; + case 8: + tmp = w8001->data[0] & W8001_TAB_MASK; + if (unlikely(tmp == W8001_TAB_BYTE)) + break; + w8001->idx = 0; + memset(&coord, 0, sizeof(coord)); + parse_data(w8001->data, &coord); + input_report_abs(dev, ABS_X, coord.x); + input_report_abs(dev, ABS_Y, coord.y); + input_report_abs(dev, ABS_PRESSURE, coord.pen_pressure); + input_report_key(dev, BTN_TOUCH, coord.tsw); + input_sync(dev); + break; + case 10: + w8001->idx = 0; + memcpy(w8001->response, &w8001->data, W8001_PACKET_LEN); + w8001->expected_packet = W8001_QUERY_PACKET; + complete(&w8001->cmd_done); + break; + } +} + + +static irqreturn_t w8001_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct w8001 *w8001 = serio_get_drvdata(serio); + + w8001_process_data(w8001, data); + + return IRQ_HANDLED; +} + +static int w8001_async_command(struct w8001 *w8001, unsigned char *packet, + int len) +{ + int rc = -1; + int i; + + mutex_lock(&w8001->cmd_mutex); + + for (i = 0; i < len; i++) { + if (serio_write(w8001->serio, packet[i])) + goto out; + } + rc = 0; + +out: + mutex_unlock(&w8001->cmd_mutex); + return rc; +} + +static int w8001_command(struct w8001 *w8001, unsigned char *packet, int len) +{ + int rc = -1; + int i; + + mutex_lock(&w8001->cmd_mutex); + + serio_pause_rx(w8001->serio); + init_completion(&w8001->cmd_done); + serio_continue_rx(w8001->serio); + + for (i = 0; i < len; i++) { + if (serio_write(w8001->serio, packet[i])) + goto out; + } + + wait_for_completion_timeout(&w8001->cmd_done, HZ); + + if (w8001->expected_packet == W8001_QUERY_PACKET) { + /* We are back in reporting mode, the query was ACKed */ + memcpy(packet, w8001->response, W8001_PACKET_LEN); + rc = 0; + } + +out: + mutex_unlock(&w8001->cmd_mutex); + return rc; +} + +static int w8001_setup(struct w8001 *w8001) +{ + struct w8001_coord coord; + struct input_dev *dev = w8001->dev; + unsigned char start[1] = { '1' }; + unsigned char query[11] = { '*' }; + + if (w8001_command(w8001, query, 1)) + return -1; + + memset(&coord, 0, sizeof(coord)); + parse_data(query, &coord); + + input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); + input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); + input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); + input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); + + if (w8001_async_command(w8001, start, 1)) + return -1; + + return 0; +} + +/* + * w8001_disconnect() is the opposite of w8001_connect() + */ + +static void w8001_disconnect(struct serio *serio) +{ + struct w8001 *w8001 = serio_get_drvdata(serio); + + input_get_device(w8001->dev); + input_unregister_device(w8001->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(w8001->dev); + kfree(w8001); +} + +/* + * w8001_connect() is the routine that is called when someone adds a + * new serio device that supports the w8001 protocol and registers it as + * an input device. + */ + +static int w8001_connect(struct serio *serio, struct serio_driver *drv) +{ + struct w8001 *w8001; + struct input_dev *input_dev; + int err; + + w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!w8001 || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + w8001->serio = serio; + w8001->id = serio->id.id; + w8001->dev = input_dev; + mutex_init(&w8001->cmd_mutex); + init_completion(&w8001->cmd_done); + snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); + + input_dev->name = "Wacom W8001 Penabled Serial TouchScreen"; + input_dev->phys = w8001->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_W8001; + input_dev->id.product = w8001->id; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + serio_set_drvdata(serio, w8001); + err = serio_open(serio, drv); + if (err) + goto fail2; + + if (w8001_setup(w8001)) + goto fail3; + + err = input_register_device(w8001->dev); + if (err) + goto fail3; + + return 0; + +fail3: + serio_close(serio); +fail2: + serio_set_drvdata(serio, NULL); +fail1: + input_free_device(input_dev); + kfree(w8001); + return err; +} + +static struct serio_device_id w8001_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_W8001, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, w8001_serio_ids); + +static struct serio_driver w8001_drv = { + .driver = { + .name = "w8001", + }, + .description = DRIVER_DESC, + .id_table = w8001_serio_ids, + .interrupt = w8001_interrupt, + .connect = w8001_connect, + .disconnect = w8001_disconnect, +}; + +static int __init w8001_init(void) +{ + return serio_register_driver(&w8001_drv); +} + +static void __exit w8001_exit(void) +{ + serio_unregister_driver(&w8001_drv); +} + +module_init(w8001_init); +module_exit(w8001_exit); diff --git a/include/linux/serio.h b/include/linux/serio.h index 25641d9e0ea8..1bcb357a01a1 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -213,5 +213,6 @@ static inline void serio_unpin_driver(struct serio *serio) #define SERIO_ZHENHUA 0x36 #define SERIO_INEXIO 0x37 #define SERIO_TOUCHIT213 0x37 +#define SERIO_W8001 0x39 #endif -- cgit v1.2.3 From a2d781fc8d9b16113dd9440107d73c0f21d7cbef Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 19 Nov 2008 17:02:24 -0500 Subject: Input: libps2 - handle 0xfc responses from devices Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 20 ++++++++++++++++---- include/linux/libps2.h | 2 ++ 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 2b304c22c200..67248c31e19a 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -262,9 +262,17 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) break; case PS2_RET_NAK: - ps2dev->nak = 1; + ps2dev->flags |= PS2_FLAG_NAK; + ps2dev->nak = PS2_RET_NAK; break; + case PS2_RET_ERR: + if (ps2dev->flags & PS2_FLAG_NAK) { + ps2dev->flags &= ~PS2_FLAG_NAK; + ps2dev->nak = PS2_RET_ERR; + break; + } + /* * Workaround for mice which don't ACK the Get ID command. * These are valid mouse IDs that we recognize. @@ -282,8 +290,11 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) } - if (!ps2dev->nak && ps2dev->cmdcnt) - ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1; + if (!ps2dev->nak) { + ps2dev->flags &= ~PS2_FLAG_NAK; + if (ps2dev->cmdcnt) + ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1; + } ps2dev->flags &= ~PS2_FLAG_ACK; wake_up(&ps2dev->wait); @@ -329,6 +340,7 @@ void ps2_cmd_aborted(struct ps2dev *ps2dev) if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD)) wake_up(&ps2dev->wait); - ps2dev->flags = 0; + /* reset all flags except last nack */ + ps2dev->flags &= PS2_FLAG_NAK; } EXPORT_SYMBOL(ps2_cmd_aborted); diff --git a/include/linux/libps2.h b/include/linux/libps2.h index afc413369101..b94534b7e266 100644 --- a/include/linux/libps2.h +++ b/include/linux/libps2.h @@ -18,11 +18,13 @@ #define PS2_RET_ID 0x00 #define PS2_RET_ACK 0xfa #define PS2_RET_NAK 0xfe +#define PS2_RET_ERR 0xfc #define PS2_FLAG_ACK 1 /* Waiting for ACK/NAK */ #define PS2_FLAG_CMD 2 /* Waiting for command to finish */ #define PS2_FLAG_CMD1 4 /* Waiting for the first byte of command response */ #define PS2_FLAG_WAITID 8 /* Command execiting is GET ID */ +#define PS2_FLAG_NAK 16 /* Last transmission was NAKed */ struct ps2dev { struct serio *serio; -- cgit v1.2.3 From 50b6f1f4a430608f7345f66ecd68a129bff11649 Mon Sep 17 00:00:00 2001 From: Kwangwoo Lee Date: Sat, 20 Dec 2008 04:26:01 -0500 Subject: Input: add tsc2007 based touchscreen driver This drive has been tested on ARM9 based SoC - MV86XX. Signed-off-by: Kwangwoo Lee Acked-by: Jean Delvare Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 11 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/tsc2007.c | 381 ++++++++++++++++++++++++++++++++++++ include/linux/i2c/tsc2007.h | 17 ++ 4 files changed, 410 insertions(+) create mode 100644 drivers/input/touchscreen/tsc2007.c create mode 100644 include/linux/i2c/tsc2007.h (limited to 'include') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 20eb52ed176d..83747dc9ba05 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -389,4 +389,15 @@ config TOUCHSCREEN_TOUCHIT213 To compile this driver as a module, choose M here: the module will be called touchit213. +config TOUCHSCREEN_TSC2007 + tristate "TSC2007 based touchscreens" + depends on I2C + help + Say Y here if you have a TSC2007 based touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tsc2007. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 3dc84d3846c1..127f87cc2e2e 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o +obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c new file mode 100644 index 000000000000..b75dc2990574 --- /dev/null +++ b/drivers/input/touchscreen/tsc2007.c @@ -0,0 +1,381 @@ +/* + * drivers/input/touchscreen/tsc2007.c + * + * Copyright (c) 2008 MtekVision Co., Ltd. + * Kwangwoo Lee + * + * Using code from: + * - ads7846.c + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define TS_POLL_DELAY (10 * 1000) /* ns delay before the first sample */ +#define TS_POLL_PERIOD (5 * 1000) /* ns delay between samples */ + +#define TSC2007_MEASURE_TEMP0 (0x0 << 4) +#define TSC2007_MEASURE_AUX (0x2 << 4) +#define TSC2007_MEASURE_TEMP1 (0x4 << 4) +#define TSC2007_ACTIVATE_XN (0x8 << 4) +#define TSC2007_ACTIVATE_YN (0x9 << 4) +#define TSC2007_ACTIVATE_YP_XN (0xa << 4) +#define TSC2007_SETUP (0xb << 4) +#define TSC2007_MEASURE_X (0xc << 4) +#define TSC2007_MEASURE_Y (0xd << 4) +#define TSC2007_MEASURE_Z1 (0xe << 4) +#define TSC2007_MEASURE_Z2 (0xf << 4) + +#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2) +#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2) +#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2) +#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2) + +#define TSC2007_12BIT (0x0 << 1) +#define TSC2007_8BIT (0x1 << 1) + +#define MAX_12BIT ((1 << 12) - 1) + +#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0) + +#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y) +#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1) +#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2) +#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X) +#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN) + +struct ts_event { + u16 x; + u16 y; + u16 z1, z2; +}; + +struct tsc2007 { + struct input_dev *input; + char phys[32]; + struct hrtimer timer; + struct ts_event tc; + + struct i2c_client *client; + + spinlock_t lock; + + u16 model; + u16 x_plate_ohms; + + unsigned pendown; + int irq; + + int (*get_pendown_state)(void); + void (*clear_penirq)(void); +}; + +static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd) +{ + s32 data; + u16 val; + + data = i2c_smbus_read_word_data(tsc->client, cmd); + if (data < 0) { + dev_err(&tsc->client->dev, "i2c io error: %d\n", data); + return data; + } + + /* The protocol and raw data format from i2c interface: + * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P + * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit]. + */ + val = swab16(data) >> 4; + + dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val); + + return val; +} + +static void tsc2007_send_event(void *tsc) +{ + struct tsc2007 *ts = tsc; + u32 rt; + u16 x, y, z1, z2; + + x = ts->tc.x; + y = ts->tc.y; + z1 = ts->tc.z1; + z2 = ts->tc.z2; + + /* range filtering */ + if (x == MAX_12BIT) + x = 0; + + if (likely(x && z1)) { + /* compute touch pressure resistance using equation #1 */ + rt = z2; + rt -= z1; + rt *= x; + rt *= ts->x_plate_ohms; + rt /= z1; + rt = (rt + 2047) >> 12; + } else + rt = 0; + + /* Sample found inconsistent by debouncing or pressure is beyond + * the maximum. Don't report it to user space, repeat at least + * once more the measurement + */ + if (rt > MAX_12BIT) { + dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); + + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), + HRTIMER_MODE_REL); + return; + } + + /* NOTE: We can't rely on the pressure to determine the pen down + * state, even this controller has a pressure sensor. The pressure + * value can fluctuate for quite a while after lifting the pen and + * in some cases may not even settle at the expected value. + * + * The only safe way to check for the pen up condition is in the + * timer by reading the pen signal state (it's a GPIO _and_ IRQ). + */ + if (rt) { + struct input_dev *input = ts->input; + + if (!ts->pendown) { + dev_dbg(&ts->client->dev, "DOWN\n"); + + input_report_key(input, BTN_TOUCH, 1); + ts->pendown = 1; + } + + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, rt); + + input_sync(input); + + dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", + x, y, rt); + } + + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), + HRTIMER_MODE_REL); +} + +static int tsc2007_read_values(struct tsc2007 *tsc) +{ + /* y- still on; turn on only y+ (and ADC) */ + tsc->tc.y = tsc2007_xfer(tsc, READ_Y); + + /* turn y- off, x+ on, then leave in lowpower */ + tsc->tc.x = tsc2007_xfer(tsc, READ_X); + + /* turn y+ off, x- on; we'll use formula #1 */ + tsc->tc.z1 = tsc2007_xfer(tsc, READ_Z1); + tsc->tc.z2 = tsc2007_xfer(tsc, READ_Z2); + + /* power down */ + tsc2007_xfer(tsc, PWRDOWN); + + return 0; +} + +static enum hrtimer_restart tsc2007_timer(struct hrtimer *handle) +{ + struct tsc2007 *ts = container_of(handle, struct tsc2007, timer); + + spin_lock_irq(&ts->lock); + + if (unlikely(!ts->get_pendown_state() && ts->pendown)) { + struct input_dev *input = ts->input; + + dev_dbg(&ts->client->dev, "UP\n"); + + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_sync(input); + + ts->pendown = 0; + enable_irq(ts->irq); + } else { + /* pen is still down, continue with the measurement */ + dev_dbg(&ts->client->dev, "pen is still down\n"); + + tsc2007_read_values(ts); + tsc2007_send_event(ts); + } + + spin_unlock_irq(&ts->lock); + + return HRTIMER_NORESTART; +} + +static irqreturn_t tsc2007_irq(int irq, void *handle) +{ + struct tsc2007 *ts = handle; + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + + if (likely(ts->get_pendown_state())) { + disable_irq(ts->irq); + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY), + HRTIMER_MODE_REL); + } + + if (ts->clear_penirq) + ts->clear_penirq(); + + spin_unlock_irqrestore(&ts->lock, flags); + + return IRQ_HANDLED; +} + +static int tsc2007_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tsc2007 *ts; + struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data; + struct input_dev *input_dev; + int err; + + if (!pdata) { + dev_err(&client->dev, "platform data is required!\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ts->client = client; + i2c_set_clientdata(client, ts); + + ts->input = input_dev; + + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = tsc2007_timer; + + spin_lock_init(&ts->lock); + + ts->model = pdata->model; + ts->x_plate_ohms = pdata->x_plate_ohms; + ts->get_pendown_state = pdata->get_pendown_state; + ts->clear_penirq = pdata->clear_penirq; + + pdata->init_platform_hw(); + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", client->dev.bus_id); + + input_dev->name = "TSC2007 Touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_I2C; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + + tsc2007_read_values(ts); + + ts->irq = client->irq; + + err = request_irq(ts->irq, tsc2007_irq, 0, + client->dev.driver->name, ts); + if (err < 0) { + dev_err(&client->dev, "irq %d busy?\n", ts->irq); + goto err_free_mem; + } + + err = input_register_device(input_dev); + if (err) + goto err_free_irq; + + dev_info(&client->dev, "registered with irq (%d)\n", ts->irq); + + return 0; + + err_free_irq: + free_irq(ts->irq, ts); + hrtimer_cancel(&ts->timer); + err_free_mem: + input_free_device(input_dev); + kfree(ts); + return err; +} + +static int tsc2007_remove(struct i2c_client *client) +{ + struct tsc2007 *ts = i2c_get_clientdata(client); + struct tsc2007_platform_data *pdata; + + pdata = client->dev.platform_data; + pdata->exit_platform_hw(); + + free_irq(ts->irq, ts); + hrtimer_cancel(&ts->timer); + input_unregister_device(ts->input); + kfree(ts); + + return 0; +} + +static struct i2c_device_id tsc2007_idtable[] = { + { "tsc2007", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tsc2007_idtable); + +static struct i2c_driver tsc2007_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tsc2007" + }, + .id_table = tsc2007_idtable, + .probe = tsc2007_probe, + .remove = tsc2007_remove, +}; + +static int __init tsc2007_init(void) +{ + return i2c_add_driver(&tsc2007_driver); +} + +static void __exit tsc2007_exit(void) +{ + i2c_del_driver(&tsc2007_driver); +} + +module_init(tsc2007_init); +module_exit(tsc2007_exit); + +MODULE_AUTHOR("Kwangwoo Lee "); +MODULE_DESCRIPTION("TSC2007 TouchScreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/i2c/tsc2007.h b/include/linux/i2c/tsc2007.h new file mode 100644 index 000000000000..c6361fbb7bf9 --- /dev/null +++ b/include/linux/i2c/tsc2007.h @@ -0,0 +1,17 @@ +#ifndef __LINUX_I2C_TSC2007_H +#define __LINUX_I2C_TSC2007_H + +/* linux/i2c/tsc2007.h */ + +struct tsc2007_platform_data { + u16 model; /* 2007. */ + u16 x_plate_ohms; + + int (*get_pendown_state)(void); + void (*clear_penirq)(void); /* If needed, clear 2nd level + interrupt source */ + int (*init_platform_hw)(void); + void (*exit_platform_hw)(void); +}; + +#endif -- cgit v1.2.3 From 34a4c5eb421dab6fe8381aa12c990f9d6f645b17 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 29 Dec 2008 04:00:23 -0800 Subject: Input: map_to_7segment.h - convert to __inline__ for userspace Use __inline__ rather than inline for map_to_seg7() since it is exported to userspace. Signed-off-by: Mike Frysinger Signed-off-by: Dmitry Torokhov --- include/linux/map_to_7segment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/map_to_7segment.h b/include/linux/map_to_7segment.h index 7df8432c4402..12d62a54d470 100644 --- a/include/linux/map_to_7segment.h +++ b/include/linux/map_to_7segment.h @@ -75,7 +75,7 @@ struct seg7_conversion_map { unsigned char table[128]; }; -static inline int map_to_seg7(struct seg7_conversion_map *map, int c) +static __inline__ int map_to_seg7(struct seg7_conversion_map *map, int c) { return c >= 0 && c < sizeof(map->table) ? map->table[c] : -EINVAL; } -- cgit v1.2.3