diff options
author | Frank Li <Frank.Li@freescale.com> | 2011-07-20 18:41:50 +0800 |
---|---|---|
committer | Jason Liu <r64343@freescale.com> | 2012-01-09 20:21:34 +0800 |
commit | 34eb292ef7fb5c92a8901391161bd3a119bbf202 (patch) | |
tree | 0ad5160d22fcb988975ab4145f10f648423df2d3 /drivers/input | |
parent | 847b5d6c878b852b254038d842724b911fe9a06e (diff) |
ENGR00139238-2 Touch: add p1003 and egalax
Add p1003 and egalax touch driver
Signed-off-by: Frank Li <Frank.Li@freescale.com>
Diffstat (limited to 'drivers/input')
-rwxr-xr-x | drivers/input/touchscreen/Kconfig | 10 | ||||
-rwxr-xr-x | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/egalax_ts.c | 357 | ||||
-rw-r--r-- | drivers/input/touchscreen/p1003_ts.c | 21 |
4 files changed, 382 insertions, 7 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 7cd31c031096..c698e138e86c 100755 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -177,6 +177,16 @@ config TOUCHSCREEN_EETI To compile this driver as a module, choose M here: the module will be called eeti_ts. +config TOUCHSCREEN_EGALAX + tristate "EETI eGalax multi-touchscreen panel support" + depends on I2C + help + Say Y here to enable support for I2C connected EETI + eGalax multiple touch panels. + + To compile this driver as a module, choose M here: the + module will be called egalax_ts. + config TOUCHSCREEN_FUJITSU tristate "Fujitsu serial touchscreen" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 4a4dfe2fe4f2..63c23d061916 100755 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -64,3 +64,4 @@ obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_P1003) += p1003_ts.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o +obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c new file mode 100644 index 000000000000..58273b9e3567 --- /dev/null +++ b/drivers/input/touchscreen/egalax_ts.c @@ -0,0 +1,357 @@ +/* + * Driver for EETI eGalax Multiple Touch Controller + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * + * based on max11801_ts.c + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* EETI eGalax serial touch screen controller is a I2C based multiple + * touch screen controller, it can supports 5 pointer multiple + * touch. */ + +/* TODO: + - auto idle mode support + - early suspend support for android +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/bitops.h> + +#define REPORT_MODE_SINGLE 0x1 +#define REPORT_MODE_VENDOR 0x3 +#define REPORT_MODE_MTTOUCH 0x4 + +#define MAX_SUPPORT_POINTS 5 + +#define EVENT_MODE 0 +#define EVENT_STATUS 1 +#define EVENT_VALID_OFFSET 7 +#define EVENT_VAILD_MASK (0x1 << EVENT_VALID_OFFSET) +#define EVENT_ID_OFFSET 2 +#define EVENT_ID_MASK (0xf << EVENT_ID_OFFSET) +#define EVENT_IN_RANGE (0x1 << 1) +#define EVENT_DOWN_UP (0X1 << 0) + +#define MAX_I2C_DATA_LEN 10 + +struct egalax_pointer { + bool valid; + bool status; + u16 x; + u16 y; +}; + +struct egalax_ts { + struct i2c_client *client; + struct input_dev *input_dev; + struct egalax_pointer events[MAX_SUPPORT_POINTS]; +}; + +static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id) +{ + struct egalax_ts *data = dev_id; + struct input_dev *input_dev = data->input_dev; + struct i2c_client *client = data->client; + struct egalax_pointer *events = data->events; + u8 buf[MAX_I2C_DATA_LEN]; + int i, id, ret, x, y; + bool down, valid; + u8 state; + +retry: + ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN); + if (ret == -EAGAIN) + goto retry; + + if (ret < 0) + return IRQ_HANDLED; + + dev_dbg(&client->dev, "recv ret:%d", ret); + for (i = 0; i < MAX_I2C_DATA_LEN; i++) + printk(KERN_DEBUG " %x ", buf[i]); + + if (buf[0] != REPORT_MODE_VENDOR + && buf[0] != REPORT_MODE_SINGLE + && buf[0] != REPORT_MODE_MTTOUCH) { + /* invalid point */ + return IRQ_HANDLED; + } + + if (buf[0] == REPORT_MODE_VENDOR) { + dev_dbg(&client->dev, "vendor message, ignored\n"); + return IRQ_HANDLED; + } + + state = buf[1]; + x = (buf[3] << 8) | buf[2]; + y = (buf[5] << 8) | buf[4]; + + /* Currently, the panel Freescale using on SMD board _NOT_ + * support single pointer mode. All event are going to + * multiple pointer mode. Add single pointer mode according + * to EETI eGalax I2C programming manual. + */ + if (buf[0] == REPORT_MODE_SINGLE) { + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_key(input_dev, BTN_TOUCH, !!state); + input_sync(input_dev); + return IRQ_HANDLED; + } + + /* deal with multiple touch */ + valid = state & EVENT_VAILD_MASK; + id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET; + down = state & EVENT_DOWN_UP; + + if (!valid || id > MAX_SUPPORT_POINTS) { + dev_dbg(&client->dev, "point invalid\n"); + return IRQ_HANDLED; + } + + if (down) { + /* should also report old pointers */ + events[id].valid = valid; + events[id].status = down; + events[id].x = x; + events[id].y = y; + +#ifdef FORCE_SINGLE_POINTER_SUPPORT + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_PRESSURE, 1); +#else + for (i = 0; i < MAX_SUPPORT_POINTS; i++) { + if (!events[i].valid) + continue; + dev_dbg(&client->dev, "report id:%d valid:%d x:%d y:%d", + i, valid, x, y); + + input_report_abs(input_dev, + ABS_MT_TRACKING_ID, i); + input_report_abs(input_dev, + ABS_MT_TOUCH_MAJOR, 1); + input_report_abs(input_dev, + ABS_MT_POSITION_X, events[i].x); + input_report_abs(input_dev, + ABS_MT_POSITION_Y, events[i].y); + input_mt_sync(input_dev); + } +#endif + } else { + dev_dbg(&client->dev, "release id:%d\n", id); + events[id].valid = 0; + events[id].status = 0; +#ifdef FORCE_SINGLE_POINTER_SUPPORT + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); +#else + input_report_abs(input_dev, ABS_MT_TRACKING_ID, id); + input_event(input_dev, EV_ABS, ABS_MT_TOUCH_MAJOR, 0); + input_mt_sync(input_dev); +#endif + } + + input_sync(input_dev); + return IRQ_HANDLED; +} + +static int egalax_wake_up_device(struct i2c_client *client) +{ + int gpio = irq_to_gpio(client->irq); + int ret; + + ret = gpio_request(gpio, "egalax_irq"); + if (ret < 0) { + dev_err(&client->dev, "request gpio failed:%d\n", ret); + return ret; + } + /* wake up controller via an falling edge on IRQ. */ + gpio_direction_output(gpio, 0); + gpio_set_value(gpio, 1); + /* controller should be waken up, return irq. */ + gpio_direction_input(gpio); + gpio_free(gpio); + return 0; +} + +static int egalax_7200_firmware_version(struct i2c_client *client) +{ + static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 }; + int ret; + ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN); + if (ret < 0) + return ret; + return 0; +} + +static int __devinit egalax_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct egalax_ts *data; + struct input_dev *input_dev; + int ret; + + data = kzalloc(sizeof(struct egalax_ts), GFP_KERNEL); + if (!data) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto err_free_data; + } + + data->client = client; + data->input_dev = input_dev; + egalax_wake_up_device(client); + ret = egalax_7200_firmware_version(client); + if (ret < 0) { + dev_err(&client->dev, + "egalax_ts: failed to read firmware version\n"); + ret = -EIO; + goto err_free_dev; + } + + input_dev->name = "eGalax Touch Screen"; + input_dev->phys = "I2C", + input_dev->id.bustype = BUS_I2C; + input_dev->id.vendor = 0x0EEF; + input_dev->id.product = 0x0020; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &client->dev; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(ABS_X, input_dev->absbit); + __set_bit(ABS_Y, input_dev->absbit); + __set_bit(ABS_PRESSURE, input_dev->absbit); + input_set_abs_params(input_dev, ABS_X, 0, 32767, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 32767, 0, 0); + +#ifndef FORCE_SINGLE_POINTER_SUPPORT + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 32767, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 32767, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, + MAX_SUPPORT_POINTS, 0, 0); +#endif + input_set_drvdata(input_dev, data); + + ret = request_threaded_irq(client->irq, NULL, egalax_ts_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "egalax_ts", data); + if (ret < 0) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_dev; + } + + ret = input_register_device(data->input_dev); + if (ret < 0) + goto err_free_irq; + i2c_set_clientdata(client, data); + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_dev: + input_free_device(input_dev); +err_free_data: + kfree(data); + + return ret; +} + +static __devexit int egalax_ts_remove(struct i2c_client *client) +{ + struct egalax_ts *data = i2c_get_clientdata(client); + + free_irq(client->irq, data); + input_unregister_device(data->input_dev); + input_free_device(data->input_dev); + kfree(data); + + return 0; +} + +static const struct i2c_device_id egalax_ts_id[] = { + {"egalax_ts", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, egalax_ts_id); + +#ifdef CONFIG_PM_SLEEP +static int egalax_ts_suspend(struct device *dev) +{ + int ret; + u8 suspend_cmd[MAX_I2C_DATA_LEN] = {0x3, 0x6, 0xa, 0x3, 0x36, + 0x3f, 0x2, 0, 0, 0}; + struct i2c_client *client = to_i2c_client(dev); + ret = i2c_master_send(client, suspend_cmd, + MAX_I2C_DATA_LEN); + return ret > 0 ? 0 : ret; +} + +static int egalax_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + return egalax_wake_up_device(client); +} +#endif + +static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume); +static struct i2c_driver egalax_ts_driver = { + .driver = { + .name = "egalax_ts", + .pm = &egalax_ts_pm_ops, + }, + .id_table = egalax_ts_id, + .probe = egalax_ts_probe, + .remove = __devexit_p(egalax_ts_remove), +}; + +static int __init egalax_ts_init(void) +{ + return i2c_add_driver(&egalax_ts_driver); +} + +static void __exit egalax_ts_exit(void) +{ + i2c_del_driver(&egalax_ts_driver); +} + +module_init(egalax_ts_init); +module_exit(egalax_ts_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/p1003_ts.c b/drivers/input/touchscreen/p1003_ts.c index 653f7f523133..39e9c6573727 100644 --- a/drivers/input/touchscreen/p1003_ts.c +++ b/drivers/input/touchscreen/p1003_ts.c @@ -24,7 +24,6 @@ #include <linux/i2c.h> #include <linux/input.h> #include <linux/irq.h> -#include <linux/gpio.h> #include <linux/platform_device.h> #include <linux/fsl_devices.h> @@ -41,7 +40,6 @@ struct p1003_priv { struct input_dev *input; char phys[32]; unsigned int irq; - unsigned int gpio; struct work_struct work; struct point_state old_state; struct workqueue_struct *workqueue; @@ -121,13 +119,14 @@ static void p1003_work(struct work_struct *work) { struct p1003_priv *p1003 = container_of(work, struct p1003_priv, work); struct i2c_client *client = p1003->client; + struct p1003_ts_platform_data *pdata = client->dev.platform_data; struct input_dev *input = p1003->input; struct point_state *old_state = &p1003->old_state; char data[POINTER_DATA_LEN]; int x1, x2, y1, y2; /* the sample can only be read when intr pin low */ - while (!gpio_get_value(p1003->gpio)) { + while (!pdata->hw_status()) { if (p1003_i2c_read_packet(client, data) < 0) { dev_err(&client->dev, "read i2c packet failed\n"); continue; @@ -151,6 +150,7 @@ static void p1003_work(struct work_struct *work) input_event(input, EV_ABS, ABS_X, x1); input_event(input, EV_ABS, ABS_Y, y1); input_event(input, EV_KEY, BTN_TOUCH, 1); + input_report_abs(input, ABS_PRESSURE, 1); input_sync(input); old_state->x1 = x1; old_state->y1 = y1; @@ -191,6 +191,7 @@ static void p1003_work(struct work_struct *work) input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, 0); input_mt_sync(input); input_event(input, EV_KEY, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); input_sync(input); old_state->state = data[0]; } @@ -230,6 +231,7 @@ static int __devinit p1003_probe(struct i2c_client *client, int ret, xmax, ymax; struct p1003_priv *p1003; struct input_dev *input_dev; + struct p1003_ts_platform_data *pdata = client->dev.platform_data; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { @@ -237,6 +239,11 @@ static int __devinit p1003_probe(struct i2c_client *client, return -EIO; } + if (!pdata || !pdata->hw_status) { + dev_err(&client->dev, "No hw status function!\n"); + return -EIO; + } + p1003 = kzalloc(sizeof(struct p1003_priv), GFP_KERNEL); if (!p1003) return -ENOMEM; @@ -250,7 +257,6 @@ static int __devinit p1003_probe(struct i2c_client *client, p1003->client = client; p1003->irq = client->irq; p1003->input = input_dev; - p1003->gpio = irq_to_gpio(client->irq); p1003->workqueue = create_singlethread_workqueue("p1003"); INIT_WORK(&p1003->work, p1003_work); @@ -278,6 +284,7 @@ static int __devinit p1003_probe(struct i2c_client *client, __set_bit(BTN_TOUCH, input_dev->keybit); __set_bit(ABS_X, input_dev->absbit); __set_bit(ABS_Y, input_dev->absbit); + __set_bit(ABS_PRESSURE, input_dev->absbit); input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, xmax, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ymax, 0, 0); @@ -303,7 +310,7 @@ static int __devinit p1003_probe(struct i2c_client *client, goto err_unreg_dev; } - if (!gpio_get_value(p1003->gpio)) + if (!pdata->hw_status()) queue_work(p1003->workqueue, &p1003->work); return 0; @@ -332,7 +339,7 @@ static int __devexit p1003_remove(struct i2c_client *client) } static const struct i2c_device_id p1003_idtable[] = { - {"p1003_ts", 0}, + {"p1003_fwv33", 0}, {} }; @@ -342,7 +349,7 @@ static struct i2c_driver p1003_driver = { .driver = { .owner = THIS_MODULE, - .name = "p1003", + .name = "p1003_fwv33", }, .id_table = p1003_idtable, .probe = p1003_probe, |