This patch implements touchscreen support for the TSC2003 controller. There is no support for the temperature sensor yet. Signed-off-by: Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx> --- drivers/input/touchscreen/Kconfig | 8 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/tsc2003.c | 416 +++++++++++++++++++++++++++++++++++ include/linux/i2c/tsc2003.h | 28 +++ 4 files changed, 453 insertions(+), 0 deletions(-) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b01fd61..87b980c 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -455,6 +455,14 @@ config TOUCHSCREEN_TOUCHIT213 To compile this driver as a module, choose M here: the module will be called touchit213. +config TOUCHSCREEN_TSC2003 + tristate "Texas Instruments TSC2003 Touchscreen Controller" + depends on I2C + ---help--- + If you say yes here you get support for the Texas Instruments + TSC2003 Touchscreen Controller with on-chip temperature + measurement. + config TOUCHSCREEN_TSC2007 tristate "TSC2007 based touchscreens" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 6700f7b..e965422 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -27,6 +27,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_TSC2003) += tsc2003.o obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o diff --git a/drivers/input/touchscreen/tsc2003.c b/drivers/input/touchscreen/tsc2003.c new file mode 100644 index 0000000..5acaa0d --- /dev/null +++ b/drivers/input/touchscreen/tsc2003.c @@ -0,0 +1,416 @@ +/* + * linux/drivers/input/touchscreen/tsc2003.c + * + * Copyright (C) 2007-2008 Avionic Design Development GmbH + * Copyright (C) 2008-2009 Avionic Design GmbH + * + * 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. + * + * Written by Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx> + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/i2c/tsc2003.h> + +#define DRIVER_NAME "tsc2003" +#define DRIVER_VERSION "1" + +/* basic commands */ +#define CMD_MEASURE_TEMPERATURE_0 0x00 +#define CMD_MEASURE_BATTERY_1 0x10 +#define CMD_MEASURE_INPUT_1 0x20 +#define CMD_MEASURE_TEMPERATURE_1 0x40 +#define CMD_MEASURE_BATTERY_2 0x50 +#define CMD_MEASURE_INPUT_2 0x60 +#define CMD_ACTIVATE_X 0x80 +#define CMD_ACTIVATE_Y 0x90 +#define CMD_ACTIVATE_XY 0xA0 +#define CMD_MEASURE_X 0xC0 +#define CMD_MEASURE_Y 0xD0 +#define CMD_MEASURE_Z1 0xE0 +#define CMD_MEASURE_Z2 0xF0 + +/* powerdown modes */ +#define PDM_POWERDOWN 0x00 +#define PDM_IR_OFF_ADC_ON 0x04 +#define PDM_IR_ON_ADC_OFF 0x08 +#define PDM_IR_ON_ADC_ON 0x0A +#define PDM_TOUCH_IRQ_OFF 0x04 + +/* data width modes */ +#define MODE_12BIT 0x00 +#define MODE_8BIT 0x02 + +/* periodic polling delay and period */ +#define TS_POLL_DELAY (1 * 1000000) +#define TS_POLL_PERIOD (5 * 1000000) + +/** + * struct ts_event - touchscreen event structure + * @pendown: state of the pen + * @x: X-coordinate of the event + * @y: Y-coordinate of the event + * @z: pressure of the event + */ +struct ts_event { + short pendown; + short x; + short y; + short z; +}; + +/** + * struct tsc2003 - touchscreen controller context + * @client: I2C client + * @input: touchscreen input device + * @lock: lock for resource protection + * @timer: timer for periodical polling + * @work: workqueue structure + * @pendown: current pen state + * @event: current touchscreen event + * @pdata: platform-specific information + */ +struct tsc2003 { + struct i2c_client *client; + struct input_dev *input; + spinlock_t lock; + struct hrtimer timer; + struct work_struct work; + + struct ts_event event; + unsigned pendown:1; + + struct tsc2003_platform_data *pdata; +}; + +/** + * tsc2003_get_pendown_state() - obtain the current pen state + * @ts: touchscreen controller context + */ +static int tsc2003_get_pendown_state(struct tsc2003 *ts) +{ + int state = 0; + + if (ts && ts->pdata && ts->pdata->get_irq_level) + state = !ts->pdata->get_irq_level(); + + return state; +} + +/** + * tsc2003_read() - send a command and read the response + * @client: I2C client + * @command: command to send + */ +static int tsc2003_read(struct i2c_client *client, u8 command) +{ + u8 value[2] = { 0, 0 }; + int size = 2; + int status; + + command &= ~MODE_8BIT; + + status = i2c_master_send(client, &command, 1); + if (status < 0) + return status; + + if (command & MODE_8BIT) + size = 1; + + status = i2c_master_recv(client, value, size); + if (status < 0) + return status; + + if (command & MODE_8BIT) + return value[0]; + + return (value[0] << 4) | (value[1] >> 4); +} + +/** + * tsc2003_read_x() - read touch screen X-coordinate + * @client: I2C client + * @pdm: powerdown mode + * @samples: number of samples to average over + */ +static int tsc2003_read_x(struct i2c_client *client, u8 pdm, int samples) +{ + return tsc2003_read(client, CMD_MEASURE_X | pdm); +} + +/** + * tsc2003_read_y() - read touch screen Y-coordinate + * @client: I2C client + * @pdm: powerdown mode + * @samples: number of samples to average over + */ +static int tsc2003_read_y(struct i2c_client *client, u8 pdm, int samples) +{ + return tsc2003_read(client, CMD_MEASURE_Y | pdm); +} + +/** + * tsc2003_read_z() - read touch screen pressure + * @client: I2C client + * @pdm: powerdown mode + * @samples: number of samples to average over + */ +static int tsc2003_read_z(struct i2c_client *client, u8 pdm, int samples) +{ + int p1 = tsc2003_read(client, CMD_MEASURE_Z1 | pdm); + int p2 = tsc2003_read(client, CMD_MEASURE_Z2 | pdm); + + p2 = 0; + + return p1; +} + +/** + * tsc2003_timer() - timer callback function + * @timer: timer that caused this function call + */ +static enum hrtimer_restart tsc2003_timer(struct hrtimer *timer) +{ + struct tsc2003 *ts = container_of(timer, struct tsc2003, timer); + unsigned long flags = 0; + int state = 0; + + spin_lock_irqsave(&ts->lock, flags); + + state = tsc2003_get_pendown_state(ts); + if (state) { + /* reset if this is the first pen down event */ + if (!ts->pendown) { + ts->event.pendown = 0; + ts->pendown = 1; + } + + schedule_work(&ts->work); + } else { + /* enable IRQ after the pen was lifted */ + if (ts->pendown) + ts->pendown = 0; + + input_report_key(ts->input, BTN_TOUCH, 0); + input_report_abs(ts->input, ABS_PRESSURE, 0); + input_sync(ts->input); + + enable_irq(ts->client->irq); + } + + spin_unlock_irqrestore(&ts->lock, flags); + return HRTIMER_NORESTART; +} + +/** + * tsc2003_work() - work queue handler (initiated by the interrupt handler) + * @work: work queue to handle + */ +static void tsc2003_work(struct work_struct *work) +{ + struct tsc2003 *ts = container_of(work, struct tsc2003, work); + + /* report only the first pen down event */ + if (!ts->event.pendown) { + input_report_key(ts->input, BTN_TOUCH, 1); + ts->event.pendown = 1; + } + + /* read X- and Y-coordinates and the pressure */ + ts->event.x = tsc2003_read_x(ts->client, PDM_POWERDOWN, 1); + ts->event.y = tsc2003_read_y(ts->client, PDM_POWERDOWN, 1); + ts->event.z = tsc2003_read_z(ts->client, PDM_POWERDOWN, 1); + + /* report X- and Y-coordinates and the pressure */ + input_report_abs(ts->input, ABS_X, ts->event.x); + input_report_abs(ts->input, ABS_Y, ts->event.y); + input_report_abs(ts->input, ABS_PRESSURE, ts->event.z); + input_sync(ts->input); + + /* restart the timer */ + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), + HRTIMER_MODE_REL); +} + +/** + * tsc2003_interrupt() - interrupt handler for touch events + * @irq: interrupt to handle + * @dev_id: device-specific information + */ +static irqreturn_t tsc2003_interrupt(int irq, void *dev_id) +{ + struct i2c_client *client = (struct i2c_client *)dev_id; + struct tsc2003 *ts = i2c_get_clientdata(client); + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + + /* if the pen is down, disable IRQ and start timer chain */ + if (tsc2003_get_pendown_state(ts)) { + disable_irq_nosync(client->irq); + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY), + HRTIMER_MODE_REL); + } + + spin_unlock_irqrestore(&ts->lock, flags); + return IRQ_HANDLED; +} + +/** + * tsc2003_probe() - initialize the I2C client + * @client: client to initialize + * @id: I2C device ID + */ +static int tsc2003_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tsc2003 *ts; + int err = 0; + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (!ts) { + err = -ENOMEM; + goto fail; + } + + ts->client = client; + + ts->input = input_allocate_device(); + if (!ts->input) { + err = -ENOMEM; + goto fail; + } + + /* setup the input device */ + ts->input->name = "Texas Instruments TSC2003 I2C Touchscreen"; + ts->input->phys = DRIVER_NAME "/input0"; + ts->input->id.bustype = BUS_I2C; + ts->input->dev.parent = &client->dev; + + ts->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + ts->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(ts->input, ABS_X, 0, 0x3ff, 0, 0); + input_set_abs_params(ts->input, ABS_Y, 0, 0x3ff, 0, 0); + input_set_abs_params(ts->input, ABS_PRESSURE, 0, 0x3ff, 0, 0); + + /* setup platform-specific hooks */ + ts->pdata = client->dev.platform_data; + if (!ts->pdata || !ts->pdata->init_irq || !ts->pdata->get_irq_level) { + dev_err(&client->dev, "no platform-specific callbacks " + "provided\n"); + err = -ENXIO; + goto fail; + } + + if (ts->pdata->init_irq) { + err = ts->pdata->init_irq(); + if (err < 0) { + dev_err(&client->dev, "failed to initialize IRQ#%d: " + "%d\n", client->irq, err); + goto fail; + } + } + + spin_lock_init(&ts->lock); + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = tsc2003_timer; + INIT_WORK(&ts->work, tsc2003_work); + ts->pendown = 0; + + err = request_irq(client->irq, tsc2003_interrupt, IRQF_SHARED, + "TSC2003 Touch Screen", client); + if (err) { + dev_err(&client->dev, "failed to request IRQ#%d: %d\n", + client->irq, err); + goto fail; + } + + i2c_set_clientdata(client, ts); + + err = input_register_device(ts->input); + if (err) { + dev_err(&client->dev, "failed to register input device: %d\n", + err); + goto fail_irq; + } + + /* dummy read; necessary to enable the pen IRQ */ + (void)tsc2003_read_z(client, PDM_POWERDOWN, 1); + err = 0; + goto out; + +fail_irq: + free_irq(client->irq, client); + +fail: + if (ts) { + input_free_device(ts->input); + kfree(ts); + } + + i2c_set_clientdata(client, NULL); +out: + return err; +} + +/** + * tsc2003_remove() - cleanup the I2C client + * @client: client to clean up + */ +static int tsc2003_remove(struct i2c_client *client) +{ + struct tsc2003 *priv = i2c_get_clientdata(client); + + free_irq(client->irq, client); + i2c_set_clientdata(client, NULL); + input_unregister_device(priv->input); + kfree(priv); + + return 0; +} + +static const struct i2c_device_id tsc2003_ids[] = { + { DRIVER_NAME, 0 }, + { } +}; + +/* TSC2003 I2C driver */ +static struct i2c_driver tsc2003_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = tsc2003_probe, + .remove = __devexit_p(tsc2003_remove), + .id_table = tsc2003_ids, +}; + +/** + * tsc2003_init() - module initialization + */ +static int __init tsc2003_init(void) +{ + return i2c_add_driver(&tsc2003_driver); +} + +/** + * tsc2003_exit() - module cleanup + */ +static void __exit tsc2003_exit(void) +{ + i2c_del_driver(&tsc2003_driver); +} + +module_init(tsc2003_init); +module_exit(tsc2003_exit); + +MODULE_AUTHOR("Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx>"); +MODULE_DESCRIPTION("Texas Instruments TSC2003 I2C Touch Screen driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRIVER_VERSION); + diff --git a/include/linux/i2c/tsc2003.h b/include/linux/i2c/tsc2003.h new file mode 100644 index 0000000..45e6100 --- /dev/null +++ b/include/linux/i2c/tsc2003.h @@ -0,0 +1,28 @@ +/** + * linux/include/i2c/tsc2003.h + * + * Copyright (C) 2007-2008 Avionic Design Development GmbH + * Copyright (C) 2008-2009 Avionic Design GmbH + * + * 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. + * + * Written by Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx> + */ + +#ifndef LINUX_I2C_TSC2003_H +#define LINUX_I2C_TSC2003_H + +/** + * struct tsc2003_platform_data - platform-specific TSC2003 data + * @init_irq: initialize interrupt + * @get_irq_level: obtain current interrupt level + */ +struct tsc2003_platform_data { + int (*init_irq)(void); + int (*get_irq_level)(void); +}; + +#endif /* !LINUX_I2C_TSC2003_H */ + -- tg: (fb160d7..) adx/i2c/tsc2003 (depends on: adx/master) -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html