summaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authorFrank Li <Frank.Li@freescale.com>2011-03-28 16:27:55 +0800
committerJason Liu <r64343@freescale.com>2012-01-09 19:53:50 +0800
commit3ede6e1cd32e3f545e0ad14c02cbae7bfb342a00 (patch)
treec144b157692f826219a5dc911d260f5fa3f15756 /drivers/input
parent8deeaf9eb6d9d09b6e67c0dd4181a22a854a75b0 (diff)
ENGR00141217-7 touch: p1003 touch screen driver for 2.6.38
add p1003 Signed-off-by: Frank Li <Frank.Li@freescale.com>
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/touchscreen/Kconfig13
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/p1003_ts.c361
3 files changed, 375 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index cabd9e54863f..5e42c8d047bb 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -714,6 +714,19 @@ config TOUCHSCREEN_STMPE
To compile this driver as a module, choose M here: the
module will be called stmpe-ts.
+config TOUCHSCREEN_P1003
+ tristate "HannStar P1003 touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a P1003 touchscreen
+ controller.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called p1003-ts.
+
+
config TOUCHSCREEN_TPS6507X
tristate "TPS6507x based touchscreens"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 282d6f76ae26..8956464a193a 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -59,4 +59,5 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
+obj-$(CONFIG_TOUCHSCREEN_P1003) += p1003_ts.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
diff --git a/drivers/input/touchscreen/p1003_ts.c b/drivers/input/touchscreen/p1003_ts.c
new file mode 100644
index 000000000000..2a2f52498e98
--- /dev/null
+++ b/drivers/input/touchscreen/p1003_ts.c
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ *
+ * 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 <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#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>
+
+struct point_state {
+ int x1;
+ int y1;
+ int x2;
+ int y2;
+ int state;
+};
+
+struct p1003_priv {
+ struct i2c_client *client;
+ 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;
+};
+
+#define POINTER_DATA_LEN 9
+#define PANEL_INFO_LEN 6
+#define READ_TOUCH_INFO_REPORT 0x10
+#define READ_PANEL_INFO 0x20
+#define MASS_PRODUCTION_CALIBRATION 0xcc
+
+static int calibration_env(struct i2c_client *client);
+
+static ssize_t p1003_cal_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Do touch panel hardware"
+ "calibration by write \"calibration\"\n");
+}
+
+static ssize_t p1003_cal_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct p1003_priv *priv = dev_get_drvdata(dev);
+ struct i2c_client *client = priv->client;
+ const char *str = "calibration";
+
+ if (strncmp(buf, str, strlen(str)) == 0) {
+ calibration_env(client);
+ return count;
+ } else
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(calibrate, 0664, p1003_cal_show, p1003_cal_store);
+
+static struct attribute *p1003_attributs[] = {
+ &dev_attr_calibrate.attr,
+ NULL
+};
+
+static const struct attribute_group p1003_attr_group = {
+ .attrs = p1003_attributs,
+};
+
+/* Mass Production Calibration function.
+ *
+ * The touch panel is calibrated with the system environmnet
+ *
+ * Notes:
+ * - Only suggested to be used for mass production purpose.
+ * - When use this command, it very important to avoid any touch
+ object surrounding the whole system
+ * - need some time to execute, it takes about 5 seconds to be
+ finished.
+ */
+static int calibration_env(struct i2c_client *client)
+{
+ int ret;
+ dev_info(&client->dev, "calibration started\n");
+ ret = i2c_smbus_write_byte_data(client, 0xCC, 0);
+ if (ret < 0)
+ return ret;
+ mdelay(5000);
+ dev_info(&client->dev, "calibration finished\n");
+ return 0;
+}
+
+static int p1003_i2c_read_packet(struct i2c_client *client, char *value)
+{
+ return i2c_smbus_read_i2c_block_data(client, READ_TOUCH_INFO_REPORT,
+ POINTER_DATA_LEN, (u8 *) value);
+}
+
+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 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)) {
+ if (p1003_i2c_read_packet(client, data) < 0) {
+ dev_err(&client->dev, "read i2c packet failed\n");
+ continue;
+ }
+
+ if (data[0] == 0x01) {
+ /* single point */
+ x1 = (data[2] << 8) | data[1];
+ y1 = (data[4] << 8) | data[3];
+
+ /* If it's same point, free some CPU */
+ if (old_state->state == data[0] &&
+ old_state->x1 == x1 && old_state->y1 == y1) {
+ msleep(1);
+ continue;
+ }
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, x1);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, y1);
+ input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, 1);
+ input_mt_sync(input);
+ input_sync(input);
+ old_state->x1 = x1;
+ old_state->y1 = y1;
+ old_state->state = 0x01;
+ } else if (data[0] == 0x03 || data[0] == 0x02) {
+ /* two point */
+ x1 = (data[2] << 8) | data[1];
+ y1 = (data[4] << 8) | data[3];
+ x2 = (data[6] << 8) | data[5];
+ y2 = (data[8] << 8) | data[7];
+
+ /* If they are same points, free some CPU */
+ if (old_state->state == data[0] &&
+ old_state->x1 == x1 && old_state->y1 == y1 &&
+ old_state->x2 == x2 && old_state->y2 == y2) {
+ msleep(1);
+ continue;
+ }
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, x1);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, y1);
+ input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, 1);
+ input_mt_sync(input);
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, x2);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, y2);
+ input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, 1);
+ input_mt_sync(input);
+ input_sync(input);
+ old_state->state = data[0];
+ old_state->x1 = x1;
+ old_state->y1 = y1;
+ old_state->x2 = x2;
+ old_state->y2 = y2;
+ }
+ };
+
+ /* the irq is high now, means figure is leave the panel, send
+ * release to user space. */
+ input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, 0);
+ input_mt_sync(input);
+ input_sync(input);
+ old_state->state = data[0];
+}
+
+static irqreturn_t p1003_irq(int irq, void *handle)
+{
+ struct p1003_priv *p1003 = handle;
+ queue_work(p1003->workqueue, &p1003->work);
+ return IRQ_HANDLED;
+}
+
+static int read_max_range(struct i2c_client *client, int *xmax, int *ymax)
+{
+ char buf[PANEL_INFO_LEN];
+ int x, y, ret;
+
+ ret = i2c_smbus_read_i2c_block_data(client, READ_PANEL_INFO,
+ PANEL_INFO_LEN, buf);
+ if (ret < 0)
+ return ret;
+
+ x = buf[0];
+ x |= buf[1] << 8;
+
+ y = buf[2];
+ y |= buf[3] << 8;
+
+ *xmax = x;
+ *ymax = y;
+ dev_info(&client->dev, "max range: x:%d y:%d \n", x, y);
+ return 0;
+}
+
+static int __devinit p1003_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret, xmax, ymax;
+ struct p1003_priv *p1003;
+ struct input_dev *input_dev;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ dev_err(&client->dev, "I2C don't support enough function");
+ return -EIO;
+ }
+
+ p1003 = kzalloc(sizeof(struct p1003_priv), GFP_KERNEL);
+ if (!p1003)
+ return -ENOMEM;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ ret = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ 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);
+
+ if (p1003->workqueue == NULL) {
+ dev_err(&client->dev, "couldn't create workqueue\n");
+ ret = -ENOMEM;
+ goto err_free_dev;
+ }
+
+ snprintf(p1003->phys, sizeof(p1003->phys),
+ "%s/input0", dev_name(&client->dev));
+
+ if (read_max_range(client, &xmax, &ymax) < 0) {
+ dev_err(&client->dev, "couldn't read panel infomation.\n");
+ ret = -EIO;
+ goto err_free_wq;
+ }
+
+ input_dev->name = "HannStar P1003 Touchscreen";
+ input_dev->phys = p1003->phys;
+ input_dev->id.bustype = BUS_I2C;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ 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);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 1, 0, 0);
+
+ ret = input_register_device(input_dev);
+ if (ret)
+ goto err_free_wq;
+
+ i2c_set_clientdata(client, p1003);
+
+ ret = sysfs_create_group(&client->dev.kobj, &p1003_attr_group);
+ if (ret)
+ goto err_free_wq;
+
+ /* set irq type to edge falling */
+ set_irq_type(p1003->irq, IRQF_TRIGGER_FALLING);
+ ret = request_irq(p1003->irq, p1003_irq, 0,
+ client->dev.driver->name, p1003);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to register irq %d!\n",
+ p1003->irq);
+ goto err_unreg_dev;
+ }
+
+ if (!gpio_get_value(p1003->gpio))
+ queue_work(p1003->workqueue, &p1003->work);
+
+ return 0;
+err_unreg_dev:
+ input_unregister_device(input_dev);
+err_free_wq:
+ destroy_workqueue(p1003->workqueue);
+err_free_dev:
+ input_free_device(input_dev);
+err_free_mem:
+ kfree(p1003);
+ return ret;
+}
+
+static int __devexit p1003_remove(struct i2c_client *client)
+{
+ struct p1003_priv *p1003 = i2c_get_clientdata(client);
+ free_irq(p1003->irq, p1003);
+ cancel_work_sync(&p1003->work);
+ destroy_workqueue(p1003->workqueue);
+ input_unregister_device(p1003->input);
+ input_free_device(p1003->input);
+ kfree(p1003);
+
+ return 0;
+}
+
+static const struct i2c_device_id p1003_idtable[] = {
+ {"p1003_ts", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, p1003_idtable);
+
+static struct i2c_driver p1003_driver = {
+
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "p1003",
+ },
+ .id_table = p1003_idtable,
+ .probe = p1003_probe,
+ .remove = __devexit_p(p1003_remove),
+};
+
+static int __init p1003_init(void)
+{
+ return i2c_add_driver(&p1003_driver);
+}
+
+static void __exit p1003_exit(void)
+{
+ i2c_del_driver(&p1003_driver);
+}
+
+module_init(p1003_init);
+module_exit(p1003_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("HannStar P1003 multitouch driver");
+MODULE_LICENSE("GPL");