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:
committed by
QuIC Gerrit Code Review
commit
5b2f5cf055
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) = {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
654
drivers/input/touchscreen/ft5x06_ts.c
Normal file
654
drivers/input/touchscreen/ft5x06_ts.c
Normal 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, ®_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");
|
||||
31
include/linux/input/ft5x06_ts.h
Normal file
31
include/linux/input/ft5x06_ts.h
Normal 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
|
||||
Reference in New Issue
Block a user