summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/input/touchscreen/Kconfig13
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/ft5x06_ts.c654
-rw-r--r--include/linux/input/ft5x06_ts.h31
4 files changed, 699 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 93ad9df1f294..529edb16565a 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1153,4 +1153,17 @@ config TOUCHSCREEN_GEN_VKEYS
To compile this driver as a module, choose M here: the
module will be called gen_vkeys.
+config TOUCHSCREEN_FT5X06
+ tristate "FocalTech touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a ft5X06 touchscreen.
+ Ft5x06 controllers are multi touch controllers which can
+ report 5 touches at a time.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ft5x06_ts.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index aaf7f587ed19..e04e787cea6e 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o
+obj-$(CONFIG_TOUCHSCREEN_FT5X06) += ft5x06_ts.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
obj-$(CONFIG_TOUCHSCREEN_GEN_VKEYS) += gen_vkeys.o
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
new file mode 100644
index 000000000000..c9905a4a87df
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -0,0 +1,654 @@
+/*
+ *
+ * FocalTech ft5x06 TouchScreen driver.
+ *
+ * Copyright (c) 2010 Focal tech Ltd.
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/input/ft5x06_ts.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+/* Early-suspend level */
+#define FT5X06_SUSPEND_LEVEL 1
+#endif
+
+#define CFG_MAX_TOUCH_POINTS 5
+
+#define FT_STARTUP_DLY 150
+#define FT_RESET_DLY 20
+
+#define FT_PRESS 0x7F
+#define FT_MAX_ID 0x0F
+#define FT_TOUCH_STEP 6
+#define FT_TOUCH_X_H_POS 3
+#define FT_TOUCH_X_L_POS 4
+#define FT_TOUCH_Y_H_POS 5
+#define FT_TOUCH_Y_L_POS 6
+#define FT_TOUCH_EVENT_POS 3
+#define FT_TOUCH_ID_POS 5
+
+#define POINT_READ_BUF (3 + FT_TOUCH_STEP * CFG_MAX_TOUCH_POINTS)
+
+/*register address*/
+#define FT5X06_REG_PMODE 0xA5
+#define FT5X06_REG_FW_VER 0xA6
+#define FT5X06_REG_POINT_RATE 0x88
+#define FT5X06_REG_THGROUP 0x80
+
+/* power register bits*/
+#define FT5X06_PMODE_ACTIVE 0x00
+#define FT5X06_PMODE_MONITOR 0x01
+#define FT5X06_PMODE_STANDBY 0x02
+#define FT5X06_PMODE_HIBERNATE 0x03
+
+#define FT5X06_VTG_MIN_UV 2600000
+#define FT5X06_VTG_MAX_UV 3300000
+#define FT5X06_I2C_VTG_MIN_UV 1800000
+#define FT5X06_I2C_VTG_MAX_UV 1800000
+
+struct ts_event {
+ u16 x[CFG_MAX_TOUCH_POINTS]; /*x coordinate */
+ u16 y[CFG_MAX_TOUCH_POINTS]; /*y coordinate */
+ /* touch event: 0 -- down; 1-- contact; 2 -- contact */
+ u8 touch_event[CFG_MAX_TOUCH_POINTS];
+ u8 finger_id[CFG_MAX_TOUCH_POINTS]; /*touch ID */
+ u16 pressure;
+ u8 touch_point;
+};
+
+struct ft5x06_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct ts_event event;
+ const struct ft5x06_ts_platform_data *pdata;
+ struct regulator *vdd;
+ struct regulator *vcc_i2c;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+};
+
+static int ft5x06_i2c_read(struct i2c_client *client, char *writebuf,
+ int writelen, char *readbuf, int readlen)
+{
+ int ret;
+
+ if (writelen > 0) {
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = writelen,
+ .buf = writebuf,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = readlen,
+ .buf = readbuf,
+ },
+ };
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret < 0)
+ dev_err(&client->dev, "%s: i2c read error.\n",
+ __func__);
+ } else {
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = readlen,
+ .buf = readbuf,
+ },
+ };
+ ret = i2c_transfer(client->adapter, msgs, 1);
+ if (ret < 0)
+ dev_err(&client->dev, "%s:i2c read error.\n", __func__);
+ }
+ return ret;
+}
+
+static int ft5x06_i2c_write(struct i2c_client *client, char *writebuf,
+ int writelen)
+{
+ int ret;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = writelen,
+ .buf = writebuf,
+ },
+ };
+ ret = i2c_transfer(client->adapter, msgs, 1);
+ if (ret < 0)
+ dev_err(&client->dev, "%s: i2c write error.\n", __func__);
+
+ return ret;
+}
+
+static void ft5x06_report_value(struct ft5x06_ts_data *data)
+{
+ struct ts_event *event = &data->event;
+ int i;
+ int fingerdown = 0;
+
+ for (i = 0; i < event->touch_point; i++) {
+ if (event->touch_event[i] == 0 || event->touch_event[i] == 2) {
+ event->pressure = FT_PRESS;
+ fingerdown++;
+ } else {
+ event->pressure = 0;
+ }
+
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X,
+ event->x[i]);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
+ event->y[i]);
+ input_report_abs(data->input_dev, ABS_MT_PRESSURE,
+ event->pressure);
+ input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
+ event->finger_id[i]);
+ input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
+ event->pressure);
+ input_mt_sync(data->input_dev);
+ }
+
+ input_report_key(data->input_dev, BTN_TOUCH, !!fingerdown);
+ input_sync(data->input_dev);
+}
+
+static int ft5x06_handle_touchdata(struct ft5x06_ts_data *data)
+{
+ struct ts_event *event = &data->event;
+ int ret, i;
+ u8 buf[POINT_READ_BUF] = { 0 };
+ u8 pointid = FT_MAX_ID;
+
+ ret = ft5x06_i2c_read(data->client, buf, 1, buf, POINT_READ_BUF);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "%s read touchdata failed.\n",
+ __func__);
+ return ret;
+ }
+ memset(event, 0, sizeof(struct ts_event));
+
+ event->touch_point = 0;
+ for (i = 0; i < CFG_MAX_TOUCH_POINTS; i++) {
+ pointid = (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4;
+ if (pointid >= FT_MAX_ID)
+ break;
+ else
+ event->touch_point++;
+ event->x[i] =
+ (s16) (buf[FT_TOUCH_X_H_POS + FT_TOUCH_STEP * i] & 0x0F) <<
+ 8 | (s16) buf[FT_TOUCH_X_L_POS + FT_TOUCH_STEP * i];
+ event->y[i] =
+ (s16) (buf[FT_TOUCH_Y_H_POS + FT_TOUCH_STEP * i] & 0x0F) <<
+ 8 | (s16) buf[FT_TOUCH_Y_L_POS + FT_TOUCH_STEP * i];
+ event->touch_event[i] =
+ buf[FT_TOUCH_EVENT_POS + FT_TOUCH_STEP * i] >> 6;
+ event->finger_id[i] =
+ (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4;
+ }
+
+ ft5x06_report_value(data);
+
+ return 0;
+}
+
+static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
+{
+ struct ft5x06_ts_data *data = dev_id;
+ int rc;
+
+ rc = ft5x06_handle_touchdata(data);
+ if (rc)
+ pr_err("%s: handling touchdata failed\n", __func__);
+
+ return IRQ_HANDLED;
+}
+
+static int ft5x06_power_on(struct ft5x06_ts_data *data, bool on)
+{
+ int rc;
+
+ if (!on)
+ goto power_off;
+
+ rc = regulator_enable(data->vdd);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator vdd enable failed rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = regulator_enable(data->vcc_i2c);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator vcc_i2c enable failed rc=%d\n", rc);
+ regulator_disable(data->vdd);
+ }
+
+ return rc;
+
+power_off:
+ rc = regulator_disable(data->vdd);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator vdd disable failed rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = regulator_disable(data->vcc_i2c);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator vcc_i2c disable failed rc=%d\n", rc);
+ regulator_enable(data->vdd);
+ }
+
+ return rc;
+}
+
+static int ft5x06_power_init(struct ft5x06_ts_data *data, bool on)
+{
+ int rc;
+
+ if (!on)
+ goto pwr_deinit;
+
+ data->vdd = regulator_get(&data->client->dev, "vdd");
+ if (IS_ERR(data->vdd)) {
+ rc = PTR_ERR(data->vdd);
+ dev_err(&data->client->dev,
+ "Regulator get failed vdd rc=%d\n", rc);
+ return rc;
+ }
+
+ if (regulator_count_voltages(data->vdd) > 0) {
+ rc = regulator_set_voltage(data->vdd, FT5X06_VTG_MIN_UV,
+ FT5X06_VTG_MAX_UV);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator set_vtg failed vdd rc=%d\n", rc);
+ goto reg_vdd_put;
+ }
+ }
+
+ data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c");
+ if (IS_ERR(data->vcc_i2c)) {
+ rc = PTR_ERR(data->vcc_i2c);
+ dev_err(&data->client->dev,
+ "Regulator get failed vcc_i2c rc=%d\n", rc);
+ goto reg_vdd_set_vtg;
+ }
+
+ if (regulator_count_voltages(data->vcc_i2c) > 0) {
+ rc = regulator_set_voltage(data->vcc_i2c, FT5X06_I2C_VTG_MIN_UV,
+ FT5X06_I2C_VTG_MAX_UV);
+ if (rc) {
+ dev_err(&data->client->dev,
+ "Regulator set_vtg failed vcc_i2c rc=%d\n", rc);
+ goto reg_vcc_i2c_put;
+ }
+ }
+
+ return 0;
+
+reg_vcc_i2c_put:
+ regulator_put(data->vcc_i2c);
+reg_vdd_set_vtg:
+ if (regulator_count_voltages(data->vdd) > 0)
+ regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV);
+reg_vdd_put:
+ regulator_put(data->vdd);
+ return rc;
+
+pwr_deinit:
+ if (regulator_count_voltages(data->vdd) > 0)
+ regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV);
+
+ regulator_put(data->vdd);
+
+ if (regulator_count_voltages(data->vcc_i2c) > 0)
+ regulator_set_voltage(data->vcc_i2c, 0, FT5X06_I2C_VTG_MAX_UV);
+
+ regulator_put(data->vcc_i2c);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ft5x06_ts_suspend(struct device *dev)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ char txbuf[2];
+
+ disable_irq(data->client->irq);
+
+ if (gpio_is_valid(data->pdata->reset_gpio)) {
+ txbuf[0] = FT5X06_REG_PMODE;
+ txbuf[1] = FT5X06_PMODE_HIBERNATE;
+ ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf));
+ }
+
+ return 0;
+}
+
+static int ft5x06_ts_resume(struct device *dev)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+ if (gpio_is_valid(data->pdata->reset_gpio)) {
+ gpio_set_value_cansleep(data->pdata->reset_gpio, 0);
+ msleep(FT_RESET_DLY);
+ gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
+ }
+ enable_irq(data->client->irq);
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ft5x06_ts_early_suspend(struct early_suspend *handler)
+{
+ struct ft5x06_ts_data *data = container_of(handler,
+ struct ft5x06_ts_data,
+ early_suspend);
+
+ ft5x06_ts_suspend(&data->client->dev);
+}
+
+static void ft5x06_ts_late_resume(struct early_suspend *handler)
+{
+ struct ft5x06_ts_data *data = container_of(handler,
+ struct ft5x06_ts_data,
+ early_suspend);
+
+ ft5x06_ts_resume(&data->client->dev);
+}
+#endif
+
+static const struct dev_pm_ops ft5x06_ts_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = ft5x06_ts_suspend,
+ .resume = ft5x06_ts_resume,
+#endif
+};
+#endif
+
+static int ft5x06_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct ft5x06_ts_platform_data *pdata = client->dev.platform_data;
+ struct ft5x06_ts_data *data;
+ struct input_dev *input_dev;
+ u8 reg_value;
+ u8 reg_addr;
+ int err;
+
+ if (!pdata) {
+ dev_err(&client->dev, "Invalid pdata\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "I2C not supported\n");
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(struct ft5x06_ts_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&client->dev, "Not enough memory\n");
+ return -ENOMEM;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ err = -ENOMEM;
+ dev_err(&client->dev, "failed to allocate input device\n");
+ goto free_mem;
+ }
+
+ data->input_dev = input_dev;
+ data->client = client;
+ data->pdata = pdata;
+
+ input_dev->name = "ft5x06_ts";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ input_set_drvdata(input_dev, data);
+ i2c_set_clientdata(client, data);
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+ pdata->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+ pdata->y_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,
+ CFG_MAX_TOUCH_POINTS, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, FT_PRESS, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, FT_PRESS, 0, 0);
+
+ err = input_register_device(input_dev);
+ if (err) {
+ dev_err(&client->dev, "Input device registration failed\n");
+ goto free_inputdev;
+ }
+
+ if (pdata->power_init) {
+ err = pdata->power_init(true);
+ if (err) {
+ dev_err(&client->dev, "power init failed");
+ goto unreg_inputdev;
+ }
+ } else {
+ err = ft5x06_power_init(data, true);
+ if (err) {
+ dev_err(&client->dev, "power init failed");
+ goto unreg_inputdev;
+ }
+ }
+
+ if (pdata->power_on) {
+ err = pdata->power_on(true);
+ if (err) {
+ dev_err(&client->dev, "power on failed");
+ goto pwr_deinit;
+ }
+ } else {
+ err = ft5x06_power_on(data, true);
+ if (err) {
+ dev_err(&client->dev, "power on failed");
+ goto pwr_deinit;
+ }
+ }
+
+ if (gpio_is_valid(pdata->irq_gpio)) {
+ err = gpio_request(pdata->irq_gpio, "ft5x06_irq_gpio");
+ if (err) {
+ dev_err(&client->dev, "irq gpio request failed");
+ goto pwr_off;
+ }
+ err = gpio_direction_input(pdata->irq_gpio);
+ if (err) {
+ dev_err(&client->dev,
+ "set_direction for irq gpio failed\n");
+ goto free_irq_gpio;
+ }
+ }
+
+ if (gpio_is_valid(pdata->reset_gpio)) {
+ err = gpio_request(pdata->reset_gpio, "ft5x06_reset_gpio");
+ if (err) {
+ dev_err(&client->dev, "reset gpio request failed");
+ goto free_irq_gpio;
+ }
+
+ err = gpio_direction_output(pdata->reset_gpio, 0);
+ if (err) {
+ dev_err(&client->dev,
+ "set_direction for reset gpio failed\n");
+ goto free_reset_gpio;
+ }
+ msleep(FT_RESET_DLY);
+ gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
+ }
+
+ /* make sure CTP already finish startup process */
+ msleep(FT_STARTUP_DLY);
+
+ /*get some register information */
+ reg_addr = FT5X06_REG_FW_VER;
+ err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
+ if (err)
+ dev_err(&client->dev, "version read failed");
+
+ dev_info(&client->dev, "[FTS] Firmware version = 0x%x\n", reg_value);
+
+ reg_addr = FT5X06_REG_POINT_RATE;
+ ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
+ if (err)
+ dev_err(&client->dev, "report rate read failed");
+ dev_info(&client->dev, "[FTS] report rate is %dHz.\n", reg_value * 10);
+
+ reg_addr = FT5X06_REG_THGROUP;
+ err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
+ if (err)
+ dev_err(&client->dev, "threshold read failed");
+ dev_dbg(&client->dev, "[FTS] touch threshold is %d.\n", reg_value * 4);
+
+ err = request_threaded_irq(client->irq, NULL,
+ ft5x06_ts_interrupt, pdata->irqflags,
+ client->dev.driver->name, data);
+ if (err) {
+ dev_err(&client->dev, "request irq failed\n");
+ goto free_reset_gpio;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+ FT5X06_SUSPEND_LEVEL;
+ data->early_suspend.suspend = ft5x06_ts_early_suspend;
+ data->early_suspend.resume = ft5x06_ts_late_resume;
+ register_early_suspend(&data->early_suspend);
+#endif
+
+ return 0;
+
+free_reset_gpio:
+ if (gpio_is_valid(pdata->reset_gpio))
+ gpio_free(pdata->reset_gpio);
+free_irq_gpio:
+ if (gpio_is_valid(pdata->irq_gpio))
+ gpio_free(pdata->reset_gpio);
+pwr_off:
+ if (pdata->power_on)
+ pdata->power_on(false);
+ else
+ ft5x06_power_on(data, false);
+pwr_deinit:
+ if (pdata->power_init)
+ pdata->power_init(false);
+ else
+ ft5x06_power_init(data, false);
+unreg_inputdev:
+ input_unregister_device(input_dev);
+ input_dev = NULL;
+free_inputdev:
+ input_free_device(input_dev);
+free_mem:
+ kfree(data);
+ return err;
+}
+
+static int __devexit ft5x06_ts_remove(struct i2c_client *client)
+{
+ struct ft5x06_ts_data *data = i2c_get_clientdata(client);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&data->early_suspend);
+#endif
+ free_irq(client->irq, data);
+
+ if (gpio_is_valid(data->pdata->reset_gpio))
+ gpio_free(data->pdata->reset_gpio);
+
+ if (gpio_is_valid(data->pdata->irq_gpio))
+ gpio_free(data->pdata->reset_gpio);
+
+ if (data->pdata->power_on)
+ data->pdata->power_on(false);
+ else
+ ft5x06_power_on(data, false);
+
+ if (data->pdata->power_init)
+ data->pdata->power_init(false);
+ else
+ ft5x06_power_init(data, false);
+
+ input_unregister_device(data->input_dev);
+ kfree(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id ft5x06_ts_id[] = {
+ {"ft5x06_ts", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ft5x06_ts_id);
+
+static struct i2c_driver ft5x06_ts_driver = {
+ .probe = ft5x06_ts_probe,
+ .remove = __devexit_p(ft5x06_ts_remove),
+ .driver = {
+ .name = "ft5x06_ts",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &ft5x06_ts_pm_ops,
+#endif
+ },
+ .id_table = ft5x06_ts_id,
+};
+
+static int __init ft5x06_ts_init(void)
+{
+ return i2c_add_driver(&ft5x06_ts_driver);
+}
+module_init(ft5x06_ts_init);
+
+static void __exit ft5x06_ts_exit(void)
+{
+ i2c_del_driver(&ft5x06_ts_driver);
+}
+module_exit(ft5x06_ts_exit);
+
+MODULE_DESCRIPTION("FocalTech ft5x06 TouchScreen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/input/ft5x06_ts.h b/include/linux/input/ft5x06_ts.h
new file mode 100644
index 000000000000..b2fb3c4a56ae
--- /dev/null
+++ b/include/linux/input/ft5x06_ts.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * FocalTech ft5x06 TouchScreen driver header file.
+ *
+ * Copyright (c) 2010 Focal tech Ltd.
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+#ifndef __LINUX_FT5X06_TS_H__
+#define __LINUX_FT5X06_TS_H__
+
+struct ft5x06_ts_platform_data {
+ unsigned long irqflags;
+ u32 x_max;
+ u32 y_max;
+ u32 irq_gpio;
+ u32 reset_gpio;
+ int (*power_init) (bool);
+ int (*power_on) (bool);
+};
+
+#endif