diff options
author | Robert Collins <rcollins@nvidia.com> | 2010-11-09 11:26:33 -0800 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2010-12-13 10:36:03 -0800 |
commit | 388cd26cf804d134becad1cb930338c5b230f968 (patch) | |
tree | 16120f142c62fc30f2c863fb47ad82e25efeee9c /drivers | |
parent | 57a79ed56ae70ecbec766f911d6da79169954c0c (diff) |
[ARM/tegra] Touchscreen : Added Panjit/Cypress driver
(Cherry-Picked Panjit driver from cc8231d0ddef32343db199b6a668d572c079afc4)
Reviewed-on: http://git-master/r/10443
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Tested-by: Yu-Huan Hsu <yhsu@nvidia.com>
(cherry picked from commit 208bbaaf873e202531e3d8045c9a5f534ba4906e)
Change-Id: Ie08bc42669fad152931be095a8a549852dd9be12
Reviewed-on: http://git-master/r/11716
Tested-by: Robert R Collins <rcollins@nvidia.com>
Reviewed-by: Robert R Collins <rcollins@nvidia.com>
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 13 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/panjit_i2c.c | 336 |
3 files changed, 350 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 24c51a52abe5..3353be314c63 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -321,6 +321,19 @@ config TOUCHSCREEN_ATMEL_TSADCC To compile this driver as a module, choose M here: the module will be called atmel_tsadcc. +config TOUCHSCREEN_PANJIT_I2C + tristate "PANJIT I2C touchscreen driver" + depends on I2C + default n + help + Say Y here to enable PANJIT I2C capacitive touchscreen support, + covering devices such as the MGG1010AI06 and EGG1010AI06 + + If unsure, say N + + To compile this driver as a module, choose M here: the module will + be called panjit_i2c. + config TOUCHSCREEN_UCB1400 tristate "Philips UCB1400 touchscreen" depends on AC97_BUS diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 126635c3f51f..5ab62f178da7 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o +obj-$(CONFIG_TOUCHSCREEN_PANJIT_I2C) += panjit_i2c.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI) += synaptics_i2c_rmi.o obj-$(CONFIG_TOUCHSCREEN_TEGRA_ODM) += tegra_odm.o diff --git a/drivers/input/touchscreen/panjit_i2c.c b/drivers/input/touchscreen/panjit_i2c.c new file mode 100644 index 000000000000..2e9d6e17f495 --- /dev/null +++ b/drivers/input/touchscreen/panjit_i2c.c @@ -0,0 +1,336 @@ +/* + * drivers/input/touchscreen/panjit_i2c.c + * + * Touchscreen class input driver for Panjit touch panel using I2C bus + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/input.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/i2c/panjit_ts.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/slab.h> + +#define CSR 0x00 +#define CSR_SCAN_EN (1 << 3) +#define CSR_SLEEP_EN (1 << 7) +#define C_FLAG 0x01 +#define X1_H 0x03 + +#define DRIVER_NAME "panjit_touch" + +struct pj_data { + struct input_dev *input_dev; + struct i2c_client *client; + int gpio_reset; +}; + +struct pj_event { + __be16 x0; + __be16 y0; + __be16 x1; + __be16 y1; + __u8 fingers; + __u8 gesture; +}; + +union pj_buff { + struct pj_event data; + unsigned char buff[sizeof(struct pj_data)]; +}; + +static void pj_reset(struct pj_data *touch) +{ + if (touch->gpio_reset < 0) + return; + + gpio_set_value(touch->gpio_reset, 1); + msleep(50); + gpio_set_value(touch->gpio_reset, 0); + msleep(50); +} + +static irqreturn_t pj_irq(int irq, void *dev_id) +{ + struct pj_data *touch = dev_id; + struct i2c_client *client = touch->client; + union pj_buff event; + unsigned short x, y; + int offs; + int ret; + + ret = i2c_smbus_read_i2c_block_data(client, X1_H, + sizeof(event.buff), event.buff); + if (WARN_ON(ret < 0)) { + dev_err(&client->dev, "error %d reading event data\n", ret); + return IRQ_NONE; + } + ret = i2c_smbus_write_byte_data(client, C_FLAG, 0); + if (WARN_ON(ret < 0)) { + dev_err(&client->dev, "error %d clearing interrupt\n", ret); + return IRQ_NONE; + } + + input_report_key(touch->input_dev, BTN_TOUCH, + (event.data.fingers == 1 || event.data.fingers == 2)); + input_report_key(touch->input_dev, BTN_2, (event.data.fingers == 2)); + + if (!event.data.fingers || (event.data.fingers > 2)) + goto out; + + offs = (event.data.fingers == 2) ? ABS_HAT0X : ABS_X; + + x = __be16_to_cpu(event.data.x0); + y = __be16_to_cpu(event.data.y0); + + dev_dbg(&client->dev, "f[0] x=%u, y=%u\n", x, y); + input_report_abs(touch->input_dev, offs, x); + input_report_abs(touch->input_dev, offs + 1, y); + + if (event.data.fingers == 1) + goto out; + + x = __be16_to_cpu(event.data.x1); + y = __be16_to_cpu(event.data.y1); + dev_dbg(&client->dev, "f[1] x=%u, y=%u\n", x, y); + input_report_abs(touch->input_dev, ABS_HAT1X, x); + input_report_abs(touch->input_dev, ABS_HAT1Y, y); + + out: + input_sync(touch->input_dev); + return IRQ_HANDLED; +} + +static int pj_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct panjit_i2c_ts_platform_data *pdata = client->dev.platform_data; + struct pj_data *touch = NULL; + struct input_dev *input_dev = NULL; + int ret = 0; + + touch = kzalloc(sizeof(struct pj_data), GFP_KERNEL); + if (!touch) { + dev_err(&client->dev, "%s: no memory\n", __func__); + return -ENOMEM; + } + + touch->gpio_reset = -EINVAL; + + if (pdata) { + ret = gpio_request(pdata->gpio_reset, "panjit_reset"); + if (!ret) { + ret = gpio_direction_output(pdata->gpio_reset, 1); + if (ret < 0) + gpio_free(pdata->gpio_reset); + } + + if (!ret) + touch->gpio_reset = pdata->gpio_reset; + else + dev_warn(&client->dev, "unable to configure GPIO\n"); + } + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&client->dev, "%s: no memory\n", __func__); + kfree(touch); + return -ENOMEM; + } + + touch->client = client; + i2c_set_clientdata(client, touch); + + pj_reset(touch); + + /* clear interrupt */ + ret = i2c_smbus_write_byte_data(touch->client, C_FLAG, 0); + if (ret < 0) { + dev_err(&client->dev, "%s: clear interrupt failed\n", __func__); + goto fail_i2c_or_register; + } + + /* enable scanning */ + ret = i2c_smbus_write_byte_data(touch->client, CSR, CSR_SCAN_EN); + if (ret < 0) { + dev_err(&client->dev, "%s: enable interrupt failed\n", + __func__); + goto fail_i2c_or_register; + } + + touch->input_dev = input_dev; + touch->input_dev->name = DRIVER_NAME; + + set_bit(EV_SYN, touch->input_dev->evbit); + set_bit(EV_KEY, touch->input_dev->evbit); + set_bit(EV_ABS, touch->input_dev->evbit); + set_bit(BTN_TOUCH, touch->input_dev->keybit); + set_bit(BTN_2, touch->input_dev->keybit); + + /* expose multi-touch capabilities */ + set_bit(ABS_MT_POSITION_X, touch->input_dev->keybit); + set_bit(ABS_MT_POSITION_Y, touch->input_dev->keybit); + set_bit(ABS_X, touch->input_dev->keybit); + set_bit(ABS_Y, touch->input_dev->keybit); + + /* all coordinates are reported in 0..4095 */ + input_set_abs_params(touch->input_dev, ABS_X, 0, 4095, 0, 0); + input_set_abs_params(touch->input_dev, ABS_Y, 0, 4095, 0, 0); + input_set_abs_params(touch->input_dev, ABS_HAT0X, 0, 4095, 0, 0); + input_set_abs_params(touch->input_dev, ABS_HAT0Y, 0, 4095, 0, 0); + input_set_abs_params(touch->input_dev, ABS_HAT1X, 0, 4095, 0, 0); + input_set_abs_params(touch->input_dev, ABS_HAT1Y, 0, 4095, 0, 0); + + input_set_abs_params(touch->input_dev, ABS_MT_POSITION_X, 0, 4095, 0, + 0); + input_set_abs_params(touch->input_dev, ABS_MT_POSITION_Y, 0, 4095, 0, + 0); + + ret = input_register_device(touch->input_dev); + if (ret) { + dev_err(&client->dev, "%s: input_register_device failed\n", + __func__); + goto fail_i2c_or_register; + } + + /* get the irq */ + ret = request_threaded_irq(touch->client->irq, NULL, pj_irq, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + DRIVER_NAME, touch); + if (ret) { + dev_err(&client->dev, "%s: request_irq(%d) failed\n", + __func__, touch->client->irq); + goto fail_irq; + } + + dev_info(&client->dev, "%s: initialized\n", __func__); + return 0; + + fail_irq: + input_unregister_device(touch->input_dev); + + fail_i2c_or_register: + if (touch->gpio_reset >= 0) + gpio_free(touch->gpio_reset); + + input_free_device(input_dev); + kfree(touch); + return ret; +} + +static int pj_suspend(struct i2c_client *client, pm_message_t state) +{ + struct pj_data *touch = i2c_get_clientdata(client); + int ret; + + if (WARN_ON(!touch)) + return -EINVAL; + + disable_irq(client->irq); + + /* disable scanning and enable deep sleep */ + ret = i2c_smbus_write_byte_data(client, CSR, CSR_SLEEP_EN); + if (ret < 0) { + dev_err(&client->dev, "%s: sleep enable fail\n", __func__); + return ret; + } + + return 0; +} + +static int pj_resume(struct i2c_client *client) +{ + struct pj_data *touch = i2c_get_clientdata(client); + int ret = 0; + + if (WARN_ON(!touch)) + return -EINVAL; + + pj_reset(touch); + + /* enable scanning and disable deep sleep */ + ret = i2c_smbus_write_byte_data(client, C_FLAG, 0); + if (ret >= 0) + ret = i2c_smbus_write_byte_data(client, CSR, CSR_SCAN_EN); + if (ret < 0) { + dev_err(&client->dev, "%s: scan enable fail\n", __func__); + return ret; + } + + enable_irq(client->irq); + + return 0; +} + +static int pj_remove(struct i2c_client *client) +{ + struct pj_data *touch = i2c_get_clientdata(client); + + if (!touch) + return -EINVAL; + + free_irq(touch->client->irq, touch); + if (touch->gpio_reset >= 0) + gpio_free(touch->gpio_reset); + input_unregister_device(touch->input_dev); + input_free_device(touch->input_dev); + kfree(touch); + return 0; +} + +static const struct i2c_device_id panjit_ts_id[] = { + {DRIVER_NAME, 0}, + {} +}; + +static struct i2c_driver panjit_driver = { + .probe = pj_probe, + .remove = pj_remove, + .suspend = pj_suspend, + .resume = pj_resume, + .id_table = panjit_ts_id, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __devinit panjit_init(void) +{ + int e; + + e = i2c_add_driver(&panjit_driver); + if (e != 0) { + pr_err("%s: failed to register with I2C bus with " + "error: 0x%x\n", __func__, e); + } + return e; +} + +static void __exit panjit_exit(void) +{ + i2c_del_driver(&panjit_driver); +} + +module_init(panjit_init); +module_exit(panjit_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Panjit I2C touch driver"); |