input: ft5x06_ts: add proximity feature support

Focaltech's CTP FT6436 is able to behave like a proximity sensor.
Enable the driver support this new feature.

Also cleared the chekpatch warning on 3.18 kernel.

Change-Id: I7a6ec3a387536c512637b0bd8dab95e7cceca212
Signed-off-by: Mao Li <maol@codeaurora.org>
Signed-off-by: Sudhakar Manapati <smanap@codeaurora.org>
Signed-off-by: Abinaya P <abinayap@codeaurora.org>
This commit is contained in:
Mao Li 2014-10-21 06:42:56 -04:00 committed by Abinaya P
parent 49aaf48f47
commit fd296540ae
4 changed files with 266 additions and 7 deletions

View file

@ -65,6 +65,11 @@ Optional properties:
- focaltech,fw-auto-cal : specify whether calibration is needed after firmware upgrade
- focaltech,fw-vkey-support : specify if virtual keys are supported through firmware
- focaltech,ignore-id-check : specify ignore family-id check
- focaltech,panel-coords : panel coordinates for the chip in pixels.
It is a four tuple consisting of min x,
min y, max x and max y values
- focaltech,fw-name : specify the firmware file name
- focaltech,psensor-support : specify whether support the proximity sensor
Example:
i2c@f9923000{
@ -100,5 +105,6 @@ Example:
focaltech,fw-delay-readid-ms = <10>;
focaltech,fw-delay-era-flsh-ms = <2000>;
focaltech,fw-auto-cal;
focaltech,psensor-support;
};
};

View file

@ -1110,6 +1110,15 @@ config TOUCHSCREEN_COLIBRI_VF50
To compile this driver as a module, choose M here: the
module will be called colibri_vf50_ts.
config TOUCHSCREEN_FT5X06_PSENSOR
tristate "FocalTech proximity feature support"
depends on TOUCHSCREEN_FT5X06 && SENSORS
help
Say Y here if you want to support ft5x06's proximity
feature.
If unsure, say N.
config TOUCHSCREEN_MSTAR21XX
tristate "Mstar touchscreens"
depends on I2C

View file

@ -29,6 +29,7 @@
#include <linux/regulator/consumer.h>
#include <linux/firmware.h>
#include <linux/debugfs.h>
#include <linux/sensors.h>
#include <linux/input/ft5x06_ts.h>
#if defined(CONFIG_FB)
@ -73,6 +74,19 @@
#define FT_REG_FW_MIN_VER 0xB2
#define FT_REG_FW_SUB_MIN_VER 0xB3
/* psensor register address*/
#define FT_REG_PSENSOR_ENABLE 0xB0
#define FT_REG_PSENSOR_STATUS 0x01
/* psensor register bits*/
#define FT_PSENSOR_ENABLE_MASK 0x01
#define FT_PSENSOR_STATUS_NEAR 0xC0
#define FT_PSENSOR_STATUS_FAR 0xE0
#define FT_PSENSOR_FAR_TO_NEAR 0
#define FT_PSENSOR_NEAR_TO_FAR 1
#define FT_PSENSOR_ORIGINAL_STATE_FAR 1
#define FT_PSENSOR_WAKEUP_TIMEOUT 100
/* power register bits*/
#define FT_PMODE_ACTIVE 0x00
#define FT_PMODE_MONITOR 0x01
@ -207,6 +221,7 @@ struct ft5x06_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
const struct ft5x06_ts_platform_data *pdata;
struct ft5x06_psensor_platform_data *psensor_pdata;
struct regulator *vdd;
struct regulator *vcc_i2c;
char fw_name[FT_FW_NAME_MAX_LEN];
@ -231,6 +246,29 @@ struct ft5x06_ts_data {
struct pinctrl_state *pinctrl_state_release;
};
static struct sensors_classdev __maybe_unused sensors_proximity_cdev = {
.name = "ft5x06-proximity",
.vendor = "FocalTech",
.version = 1,
.handle = SENSORS_PROXIMITY_HANDLE,
.type = SENSOR_TYPE_PROXIMITY,
.max_range = "5.0",
.resolution = "5.0",
.sensor_power = "0.1",
.min_delay = 0,
.fifo_reserved_event_count = 0,
.fifo_max_event_count = 0,
.enabled = 0,
.delay_msec = 200,
.sensors_enable = NULL,
.sensors_poll_delay = NULL,
};
static inline bool ft5x06_psensor_support_enabled(void)
{
return config_enabled(CONFIG_TOUCHSCREEN_FT5X06_PSENSOR);
}
static int ft5x06_i2c_read(struct i2c_client *client, char *writebuf,
int writelen, char *readbuf, int readlen)
{
@ -306,6 +344,84 @@ static int ft5x0x_read_reg(struct i2c_client *client, u8 addr, u8 *val)
return ft5x06_i2c_read(client, &addr, 1, val, 1);
}
#ifdef CONFIG_TOUCHSCREEN_FT5X06_PSENSOR
static void ft5x06_psensor_enable(struct ft5x06_ts_data *data, int enable)
{
u8 state;
int ret = -1;
if (data->client == NULL)
return;
ft5x0x_read_reg(data->client, FT_REG_PSENSOR_ENABLE, &state);
if (enable)
state |= FT_PSENSOR_ENABLE_MASK;
else
state &= ~FT_PSENSOR_ENABLE_MASK;
ret = ft5x0x_write_reg(data->client, FT_REG_PSENSOR_ENABLE, state);
if (ret < 0)
dev_err(&data->client->dev,
"write psensor switch command failed\n");
}
static int ft5x06_psensor_enable_set(struct sensors_classdev *sensors_cdev,
unsigned int enable)
{
struct ft5x06_psensor_platform_data *psensor_pdata =
container_of(sensors_cdev,
struct ft5x06_psensor_platform_data, ps_cdev);
struct ft5x06_ts_data *data = psensor_pdata->data;
struct input_dev *input_dev = data->psensor_pdata->input_psensor_dev;
mutex_lock(&input_dev->mutex);
ft5x06_psensor_enable(data, enable);
psensor_pdata->tp_psensor_data = FT_PSENSOR_ORIGINAL_STATE_FAR;
if (enable)
psensor_pdata->tp_psensor_opened = 1;
else
psensor_pdata->tp_psensor_opened = 0;
mutex_unlock(&input_dev->mutex);
return enable;
}
static int ft5x06_read_tp_psensor_data(struct ft5x06_ts_data *data)
{
u8 psensor_status;
char tmp;
int ret = 0;
ft5x0x_read_reg(data->client,
FT_REG_PSENSOR_STATUS, &psensor_status);
tmp = data->psensor_pdata->tp_psensor_data;
if (psensor_status == FT_PSENSOR_STATUS_NEAR)
data->psensor_pdata->tp_psensor_data =
FT_PSENSOR_FAR_TO_NEAR;
else if (psensor_status == FT_PSENSOR_STATUS_FAR)
data->psensor_pdata->tp_psensor_data =
FT_PSENSOR_NEAR_TO_FAR;
if (tmp != data->psensor_pdata->tp_psensor_data) {
dev_info(&data->client->dev,
"%s sensor data changed\n", __func__);
ret = 1;
}
return ret;
}
#else
static int ft5x06_psensor_enable_set(struct sensors_classdev *sensors_cdev,
unsigned int enable)
{
return enable;
}
static int ft5x06_read_tp_psensor_data(struct ft5x06_ts_data *data)
{
return 0;
}
#endif
static void ft5x06_update_fw_vendor_id(struct ft5x06_ts_data *data)
{
struct i2c_client *client = data->client;
@ -349,7 +465,7 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
struct input_dev *ip_dev;
int rc, i;
u32 id, x, y, status, num_touches;
u8 reg = 0x00, *buf;
u8 reg, *buf;
bool update_input = false;
if (!data) {
@ -360,8 +476,31 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
ip_dev = data->input_dev;
buf = data->tch_data;
rc = ft5x06_i2c_read(data->client, &reg, 1,
buf, data->tch_data_len);
if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support &&
data->psensor_pdata->tp_psensor_opened) {
rc = ft5x06_read_tp_psensor_data(data);
if (rc) {
if (data->suspended)
pm_wakeup_event(&data->client->dev,
FT_PSENSOR_WAKEUP_TIMEOUT);
input_report_abs(data->psensor_pdata->input_psensor_dev,
ABS_DISTANCE,
data->psensor_pdata->tp_psensor_data);
input_sync(data->psensor_pdata->input_psensor_dev);
if (data->suspended)
return IRQ_HANDLED;
}
if (data->suspended)
return IRQ_HANDLED;
}
/**
* Read touch data start from register FT_REG_DEV_MODE.
* The touch x/y value start from FT_TOUCH_X_H/L_POS and
* FT_TOUCH_Y_H/L_POS in buf.
*/
reg = FT_REG_DEV_MODE;
rc = ft5x06_i2c_read(data->client, &reg, 1, buf, data->tch_data_len);
if (rc < 0) {
dev_err(&data->client->dev, "%s: read data fail\n", __func__);
return IRQ_HANDLED;
@ -661,6 +800,16 @@ static int ft5x06_ts_suspend(struct device *dev)
return 0;
}
if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support &&
device_may_wakeup(dev) &&
data->psensor_pdata->tp_psensor_opened) {
err = enable_irq_wake(data->client->irq);
if (err)
dev_err(&data->client->dev,
"%s: set_irq_wake failed\n", __func__);
data->suspended = true;
return err;
}
disable_irq(data->client->irq);
/* release all touches */
@ -745,6 +894,18 @@ static int ft5x06_ts_resume(struct device *dev)
return 0;
}
if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support &&
device_may_wakeup(dev) &&
data->psensor_pdata->tp_psensor_opened) {
err = disable_irq_wake(data->client->irq);
if (err)
dev_err(&data->client->dev,
"%s: disable_irq_wake failed\n",
__func__);
data->suspended = false;
return err;
}
if (data->pdata->power_on) {
err = data->pdata->power_on(true);
if (err) {
@ -1588,6 +1749,8 @@ static int ft5x06_parse_dt(struct device *dev,
pdata->ignore_id_check = of_property_read_bool(np,
"focaltech,ignore-id-check");
pdata->psensor_support = of_property_read_bool(np,
"focaltech,psensor-support");
rc = of_property_read_u32(np, "focaltech,family-id", &temp_val);
if (!rc)
pdata->family_id = temp_val;
@ -1623,8 +1786,10 @@ static int ft5x06_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ft5x06_ts_platform_data *pdata;
struct ft5x06_psensor_platform_data *psensor_pdata;
struct ft5x06_ts_data *data;
struct input_dev *input_dev;
struct input_dev *psensor_input_dev;
struct dentry *temp;
u8 reg_value;
u8 reg_addr;
@ -1781,17 +1946,61 @@ static int ft5x06_ts_probe(struct i2c_client *client,
err = request_threaded_irq(client->irq, NULL,
ft5x06_ts_interrupt,
pdata->irqflags | IRQF_ONESHOT,
IRQF_ONESHOT,
client->dev.driver->name, data);
if (err) {
dev_err(&client->dev, "request irq failed\n");
goto free_gpio;
}
if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support) {
device_init_wakeup(&client->dev, 1);
psensor_pdata = devm_kzalloc(&client->dev,
sizeof(struct ft5x06_psensor_platform_data),
GFP_KERNEL);
if (!psensor_pdata) {
dev_err(&client->dev, "Failed to allocate memory\n");
goto irq_free;
}
data->psensor_pdata = psensor_pdata;
psensor_input_dev = input_allocate_device();
if (!psensor_input_dev) {
dev_err(&data->client->dev,
"Failed to allocate device\n");
goto free_psensor_pdata;
}
__set_bit(EV_ABS, psensor_input_dev->evbit);
input_set_abs_params(psensor_input_dev,
ABS_DISTANCE, 0, 1, 0, 0);
psensor_input_dev->name = "proximity";
psensor_input_dev->id.bustype = BUS_I2C;
psensor_input_dev->dev.parent = &data->client->dev;
data->psensor_pdata->input_psensor_dev = psensor_input_dev;
err = input_register_device(psensor_input_dev);
if (err) {
dev_err(&data->client->dev,
"Unable to register device, err=%d\n", err);
goto free_psensor_input_dev;
}
psensor_pdata->ps_cdev = sensors_proximity_cdev;
psensor_pdata->ps_cdev.sensors_enable =
ft5x06_psensor_enable_set;
psensor_pdata->data = data;
err = sensors_classdev_register(&client->dev,
&psensor_pdata->ps_cdev);
if (err)
goto unregister_psensor_input_device;
}
err = device_create_file(&client->dev, &dev_attr_fw_name);
if (err) {
dev_err(&client->dev, "sys file creation failed\n");
goto irq_free;
goto free_psensor_class_sysfs;
}
err = device_create_file(&client->dev, &dev_attr_update_fw);
@ -1900,7 +2109,23 @@ free_update_fw_sys:
device_remove_file(&client->dev, &dev_attr_update_fw);
free_fw_name_sys:
device_remove_file(&client->dev, &dev_attr_fw_name);
free_psensor_class_sysfs:
if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support)
sensors_classdev_unregister(&psensor_pdata->ps_cdev);
unregister_psensor_input_device:
if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support)
input_unregister_device(data->psensor_pdata->input_psensor_dev);
free_psensor_input_dev:
if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support)
input_free_device(data->psensor_pdata->input_psensor_dev);
free_psensor_pdata:
if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support) {
devm_kfree(&client->dev, psensor_pdata);
data->psensor_pdata = NULL;
}
irq_free:
if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support)
device_init_wakeup(&client->dev, 0);
free_irq(client->irq, data);
free_gpio:
if (gpio_is_valid(pdata->reset_gpio))
@ -1930,9 +2155,9 @@ pwr_deinit:
ft5x06_power_init(data, false);
unreg_inputdev:
input_unregister_device(input_dev);
input_dev = NULL;
free_inputdev:
input_free_device(input_dev);
input_dev = NULL;
return err;
}
@ -1941,6 +2166,16 @@ static int ft5x06_ts_remove(struct i2c_client *client)
struct ft5x06_ts_data *data = i2c_get_clientdata(client);
int retval;
if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support) {
device_init_wakeup(&client->dev, 0);
sensors_classdev_unregister(&data->psensor_pdata->ps_cdev);
input_unregister_device(data->psensor_pdata->input_psensor_dev);
input_free_device(data->psensor_pdata->input_psensor_dev);
devm_kfree(&client->dev, data->psensor_pdata);
data->psensor_pdata = NULL;
}
debugfs_remove_recursive(data->dir);
device_remove_file(&client->dev, &dev_attr_force_update_fw);
device_remove_file(&client->dev, &dev_attr_update_fw);

View file

@ -34,6 +34,14 @@ struct fw_upgrade_info {
u16 delay_erase_flash;
};
struct ft5x06_psensor_platform_data {
struct input_dev *input_psensor_dev;
struct sensors_classdev ps_cdev;
int tp_psensor_opened;
char tp_psensor_data; /* 0 near, 1 far */
struct ft5x06_ts_data *data;
};
struct ft5x06_ts_platform_data {
struct fw_upgrade_info info;
const char *name;
@ -60,6 +68,7 @@ struct ft5x06_ts_platform_data {
bool no_force_update;
bool i2c_pull_up;
bool ignore_id_check;
bool psensor_support;
int (*power_init)(bool);
int (*power_on)(bool);
};