Merge changes If10efc64,I5122171f,I39eb1175 into msm-3.0

* changes:
  defconfig: 7627a: Enable Focaltech driver
  board-qrd7627a: Add platform device for focaltech controller
  input: ft5x06_ts: Add driver for ft5x06 series
This commit is contained in:
Linux Build Service Account
2012-03-27 08:10:44 -07:00
committed by QuIC Gerrit Code Review
8 changed files with 790 additions and 0 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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) = {

View File

@@ -29,6 +29,7 @@
#include <linux/i2c/atmel_mxt_ts.h>
#include <linux/regulator/consumer.h>
#include <linux/memblock.h>
#include <linux/input/ft5x06_ts.h>
#include <asm/mach/mmc.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -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 */

View File

@@ -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

View File

@@ -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

View File

@@ -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");

View File

@@ -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