diff --git a/arch/arm/configs/msm7627a-perf_defconfig b/arch/arm/configs/msm7627a-perf_defconfig index 4f0212f6fc0..6a5497d3541 100644 --- a/arch/arm/configs/msm7627a-perf_defconfig +++ b/arch/arm/configs/msm7627a-perf_defconfig @@ -208,6 +208,7 @@ CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH=y CONFIG_TOUCHSCREEN_ATMEL_MXT=y CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C=y +CONFIG_TOUCHSCREEN_FT5X06=y CONFIG_INPUT_MISC=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y diff --git a/arch/arm/configs/msm7627a_defconfig b/arch/arm/configs/msm7627a_defconfig index 3dde3521d6d..11c42248b39 100644 --- a/arch/arm/configs/msm7627a_defconfig +++ b/arch/arm/configs/msm7627a_defconfig @@ -208,6 +208,7 @@ CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH=y CONFIG_TOUCHSCREEN_ATMEL_MXT=y CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C=y +CONFIG_TOUCHSCREEN_FT5X06=y CONFIG_INPUT_MISC=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y diff --git a/arch/arm/mach-msm/board-msm7x27a-regulator.c b/arch/arm/mach-msm/board-msm7x27a-regulator.c index b045f7c346a..469c5430777 100644 --- a/arch/arm/mach-msm/board-msm7x27a-regulator.c +++ b/arch/arm/mach-msm/board-msm7x27a-regulator.c @@ -85,6 +85,7 @@ PCOM_VREG_CONSUMERS(smps3) = { REGULATOR_SUPPLY("smps3", NULL), REGULATOR_SUPPLY("msme1", NULL), REGULATOR_SUPPLY("vcc_i2c", "1-004a"), + REGULATOR_SUPPLY("vcc_i2c", "1-0038"), }; PCOM_VREG_CONSUMERS(smps4) = { @@ -160,6 +161,7 @@ PCOM_VREG_CONSUMERS(ldo12) = { REGULATOR_SUPPLY("ldo12", NULL), REGULATOR_SUPPLY("gp2", NULL), REGULATOR_SUPPLY("vdd_ana", "1-004a"), + REGULATOR_SUPPLY("vdd", "1-0038"), }; PCOM_VREG_CONSUMERS(ldo13) = { diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c index bc9f3cbdf87..ba534e8c9b2 100644 --- a/arch/arm/mach-msm/board-qrd7627a.c +++ b/arch/arm/mach-msm/board-qrd7627a.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -258,6 +259,89 @@ static int synaptics_touchpad_setup(void) } #endif +#define FT5X06_IRQ_GPIO 48 +#define FT5X06_RESET_GPIO 26 + +static ssize_t +ft5x06_virtual_keys_register(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return snprintf(buf, 200, + __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":40:510:80:60" + ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":120:510:80:60" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":200:510:80:60" + ":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":280:510:80:60" + "\n"); +} + +static struct kobj_attribute ft5x06_virtual_keys_attr = { + .attr = { + .name = "virtualkeys.ft5x06_ts", + .mode = S_IRUGO, + }, + .show = &ft5x06_virtual_keys_register, +}; + +static struct attribute *ft5x06_virtual_key_properties_attrs[] = { + &ft5x06_virtual_keys_attr.attr, + NULL, +}; + +static struct attribute_group ft5x06_virtual_key_properties_attr_group = { + .attrs = ft5x06_virtual_key_properties_attrs, +}; + +struct kobject *ft5x06_virtual_key_properties_kobj; + +static struct ft5x06_ts_platform_data ft5x06_platformdata = { + .x_max = 320, + .y_max = 480, + .reset_gpio = FT5X06_RESET_GPIO, + .irq_gpio = FT5X06_IRQ_GPIO, + .irqflags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +}; + +static struct i2c_board_info ft5x06_device_info[] __initdata = { + { + I2C_BOARD_INFO("ft5x06_ts", 0x38), + .platform_data = &ft5x06_platformdata, + .irq = MSM_GPIO_TO_INT(FT5X06_IRQ_GPIO), + }, +}; + +static void ft5x06_touchpad_setup(void) +{ + int rc; + + rc = gpio_tlmm_config(GPIO_CFG(FT5X06_IRQ_GPIO, 0, + GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, + GPIO_CFG_8MA), GPIO_CFG_ENABLE); + if (rc) + pr_err("%s: gpio_tlmm_config for %d failed\n", + __func__, FT5X06_IRQ_GPIO); + + rc = gpio_tlmm_config(GPIO_CFG(FT5X06_RESET_GPIO, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, + GPIO_CFG_8MA), GPIO_CFG_ENABLE); + if (rc) + pr_err("%s: gpio_tlmm_config for %d failed\n", + __func__, FT5X06_RESET_GPIO); + + ft5x06_virtual_key_properties_kobj = + kobject_create_and_add("board_properties", NULL); + + if (ft5x06_virtual_key_properties_kobj) + rc = sysfs_create_group(ft5x06_virtual_key_properties_kobj, + &ft5x06_virtual_key_properties_attr_group); + + if (!ft5x06_virtual_key_properties_kobj || rc) + pr_err("%s: failed to create board_properties\n", __func__); + + i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID, + ft5x06_device_info, + ARRAY_SIZE(ft5x06_device_info)); +} static struct android_usb_platform_data android_usb_pdata = { .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, @@ -1096,6 +1180,8 @@ static void msm7627a_add_io_devices(void) i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID, mxt_device_info, ARRAY_SIZE(mxt_device_info)); + } else if (machine_is_msm7627a_qrd3()) { + ft5x06_touchpad_setup(); } /* headset */ diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 87cdb0215bd..eef08916db8 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -817,4 +817,18 @@ config TOUCHSCREEN_CYTTSP_I2C To compile this driver as a module, choose M here: the module will be called cyttsp-i2c. + +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 1d67427e275..616b3a48ecb 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELAN_I2C_8232) += elan8232_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o +obj-$(CONFIG_TOUCHSCREEN_FT5X06) += ft5x06_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c new file mode 100644 index 00000000000..c9905a4a87d --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +/* 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, ®_addr, 1, ®_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, ®_addr, 1, ®_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, ®_addr, 1, ®_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 00000000000..b2fb3c4a56a --- /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