diff --git a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt index 1d04a7e365e1..c852394254ff 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt @@ -27,6 +27,24 @@ Required properties: - focaltech,group-id : group id of this device - focaltech,hard-reset-delay-ms : hard reset delay in ms - focaltech,soft-reset-delay-ms : soft reset delay in ms + - focaltech,fw-delay-aa-ms : specify the delay in ms after programming 0xaa + register for firmware upgrade + - focaltech,fw-delay-55-ms : specify the delay in ms after programming 0x55 + register for firmware upgrade + - focaltech,fw-upgrade-id1 : specify the upgrade id1 for firmware upgrade + - focaltech,fw-upgrade-id2 : specify the upgrade id2 for firmware upgrade + - focaltech,fw-delay-readid-ms : specify the read id delay in ms for firmware upgrade + - focaltech,fw-delay-era-flsh-ms : specify the erase flash delay in ms for firmware upgrade + - pinctrl-names : This should be defined if a target uses pinctrl framework. + See "pinctrl" in Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt. + Specify the names of the configs that pinctrl can install in driver. + Following are the pinctrl configs that can be installed: + "pmx_ts_active" : Active configuration of pins, this should specify active + config defined in pin groups of interrupt and reset gpio. + "pmx_ts_suspend" : Disabled configuration of pins, this should specify sleep + config defined in pin groups of interrupt and reset gpio. + "pmx_ts_release" : Release configuration of pins, this should specify + release config defined in pin groups of interrupt and reset gpio. Optional properties: @@ -47,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{ @@ -57,6 +80,10 @@ Example: interrupts = <1 0x2>; vdd-supply = <&pm8110_l19>; vcc_i2c-supply = <&pm8110_l14>; + pinctrl-names = "pmx_ts_active","pmx_ts_suspend","pmx_ts_release"; + pinctrl-0 = <&ts_int_active &ts_reset_active>; + pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; + pinctrl-2 = <&ts_release>; focaltech,name = "ft6x06"; focaltech,family-id = <0x06>; focaltech,reset-gpio = <&msmgpio 0 0x00>; @@ -78,5 +105,6 @@ Example: focaltech,fw-delay-readid-ms = <10>; focaltech,fw-delay-era-flsh-ms = <2000>; focaltech,fw-auto-cal; + focaltech,psensor-support; }; }; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 69028bd45fdd..7c4a7c7c16e7 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -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 diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 4f5b5b4ecd7f..beb86c9dea11 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #if defined(CONFIG_FB) @@ -65,15 +66,28 @@ #define FT_REG_ID 0xA3 #define FT_REG_PMODE 0xA5 #define FT_REG_FW_VER 0xA6 +#define FT_REG_FW_VENDOR_ID 0xA8 #define FT_REG_POINT_RATE 0x88 #define FT_REG_THGROUP 0x80 #define FT_REG_ECC 0xCC #define FT_REG_RESET_FW 0x07 -#define FT_REG_FW_MAJ_VER 0xB1 #define FT_REG_FW_MIN_VER 0xB2 #define FT_REG_FW_SUB_MIN_VER 0xB3 -/* power register bits */ +/* 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 #define FT_PMODE_STANDBY 0x02 @@ -104,6 +118,7 @@ #define FT5316_ID 0x0A #define FT5306I_ID 0x55 #define FT6X06_ID 0x06 +#define FT6X36_ID 0x36 #define FT_UPGRADE_AA 0xAA #define FT_UPGRADE_55 0x55 @@ -115,11 +130,24 @@ #define FT_FW_FILE_MAJ_VER(x) ((x)->data[(x)->size - 2]) #define FT_FW_FILE_MIN_VER(x) 0 #define FT_FW_FILE_SUB_MIN_VER(x) 0 +#define FT_FW_FILE_VENDOR_ID(x) ((x)->data[(x)->size - 1]) -#define FT_FW_CHECK(x) \ +#define FT_FW_FILE_MAJ_VER_FT6X36(x) ((x)->data[0x10a]) +#define FT_FW_FILE_VENDOR_ID_FT6X36(x) ((x)->data[0x108]) + +/** +* Application data verification will be run before upgrade flow. +* Firmware image stores some flags with negative and positive value +* in corresponding addresses, we need pick them out do some check to +* make sure the application data is valid. +*/ +#define FT_FW_CHECK(x, ts_data) \ + (ts_data->family_id == FT6X36_ID ? \ + (((x)->data[0x104] ^ (x)->data[0x105]) == 0xFF \ + && ((x)->data[0x106] ^ (x)->data[0x107]) == 0xFF) : \ (((x)->data[(x)->size - 8] ^ (x)->data[(x)->size - 6]) == 0xFF \ - && (((x)->data[(x)->size - 7] ^ (x)->data[(x)->size - 5]) == 0xFF \ - && (((x)->data[(x)->size - 3] ^ (x)->data[(x)->size - 4]) == 0xFF))) + && ((x)->data[(x)->size - 7] ^ (x)->data[(x)->size - 5]) == 0xFF \ + && ((x)->data[(x)->size - 3] ^ (x)->data[(x)->size - 4]) == 0xFF)) #define FT_MAX_TRIES 5 #define FT_RETRY_DLY 20 @@ -131,8 +159,9 @@ #define FT_FW_PKT_DLY_MS 20 #define FT_FW_LAST_PKT 0x6ffa #define FT_EARSE_DLY_MS 100 +#define FT_55_AA_DLY_NS 5000 -#define FT_UPGRADE_LOOP 10 +#define FT_UPGRADE_LOOP 30 #define FT_CAL_START 0x04 #define FT_CAL_FIN 0x00 #define FT_CAL_STORE 0x05 @@ -142,6 +171,34 @@ #define FT_INFO_MAX_LEN 512 +#define FT_BLOADER_SIZE_OFF 12 +#define FT_BLOADER_NEW_SIZE 30 +#define FT_DATA_LEN_OFF_OLD_FW 8 +#define FT_DATA_LEN_OFF_NEW_FW 14 +#define FT_FINISHING_PKT_LEN_OLD_FW 6 +#define FT_FINISHING_PKT_LEN_NEW_FW 12 +#define FT_MAGIC_BLOADER_Z7 0x7bfa +#define FT_MAGIC_BLOADER_LZ4 0x6ffa +#define FT_MAGIC_BLOADER_GZF_30 0x7ff4 +#define FT_MAGIC_BLOADER_GZF 0x7bf4 + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +enum { + FT_BLOADER_VERSION_LZ4 = 0, + FT_BLOADER_VERSION_Z7 = 1, + FT_BLOADER_VERSION_GZF = 2, +}; + +enum { + FT_FT5336_FAMILY_ID_0x11 = 0x11, + FT_FT5336_FAMILY_ID_0x12 = 0x12, + FT_FT5336_FAMILY_ID_0x13 = 0x13, + FT_FT5336_FAMILY_ID_0x14 = 0x14, +}; + #define FT_STORE_TS_INFO(buf, id, name, max_tch, group_id, fw_vkey_support, \ fw_name, fw_maj, fw_min, fw_sub_min) \ snprintf(buf, FT_INFO_MAX_LEN, \ @@ -164,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]; @@ -176,13 +234,41 @@ struct ft5x06_ts_data { u8 *tch_data; u32 tch_data_len; u8 fw_ver[3]; + u8 fw_vendor_id; #if defined(CONFIG_FB) struct notifier_block fb_notif; #elif defined(CONFIG_HAS_EARLYSUSPEND) struct early_suspend early_suspend; #endif + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + 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) { @@ -258,13 +344,103 @@ 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; + u8 reg_addr; + int err; + + reg_addr = FT_REG_FW_VENDOR_ID; + err = ft5x06_i2c_read(client, ®_addr, 1, &data->fw_vendor_id, 1); + if (err < 0) + dev_err(&client->dev, "fw vendor id read failed"); +} + static void ft5x06_update_fw_ver(struct ft5x06_ts_data *data) { struct i2c_client *client = data->client; u8 reg_addr; int err; - reg_addr = FT_REG_FW_MAJ_VER; + reg_addr = FT_REG_FW_VER; err = ft5x06_i2c_read(client, ®_addr, 1, &data->fw_ver[0], 1); if (err < 0) dev_err(&client->dev, "fw major version read failed"); @@ -288,8 +464,8 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) struct ft5x06_ts_data *data = dev_id; struct input_dev *ip_dev; int rc, i; - u32 id, x, y, pressure, status, num_touches; - u8 reg = 0x00, *buf; + u32 id, x, y, status, num_touches; + u8 reg, *buf; bool update_input = false; if (!data) { @@ -300,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, ®, 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, ®, 1, buf, data->tch_data_len); if (rc < 0) { dev_err(&data->client->dev, "%s: read data fail\n", __func__); return IRQ_HANDLED; @@ -329,11 +528,9 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) input_mt_slot(ip_dev, id); if (status == FT_TOUCH_DOWN || status == FT_TOUCH_CONTACT) { - pressure = FT_PRESS; input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 1); input_report_abs(ip_dev, ABS_MT_POSITION_X, x); input_report_abs(ip_dev, ABS_MT_POSITION_Y, y); - input_report_abs(ip_dev, ABS_MT_PRESSURE, pressure); } else { input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0); } @@ -347,6 +544,79 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static int ft5x06_gpio_configure(struct ft5x06_ts_data *data, bool on) +{ + int err = 0; + + if (on) { + if (gpio_is_valid(data->pdata->irq_gpio)) { + err = gpio_request(data->pdata->irq_gpio, + "ft5x06_irq_gpio"); + if (err) { + dev_err(&data->client->dev, + "irq gpio request failed"); + goto err_irq_gpio_req; + } + + err = gpio_direction_input(data->pdata->irq_gpio); + if (err) { + dev_err(&data->client->dev, + "set_direction for irq gpio failed\n"); + goto err_irq_gpio_dir; + } + } + + if (gpio_is_valid(data->pdata->reset_gpio)) { + err = gpio_request(data->pdata->reset_gpio, + "ft5x06_reset_gpio"); + if (err) { + dev_err(&data->client->dev, + "reset gpio request failed"); + goto err_irq_gpio_dir; + } + + err = gpio_direction_output(data->pdata->reset_gpio, 0); + if (err) { + dev_err(&data->client->dev, + "set_direction for reset gpio failed\n"); + goto err_reset_gpio_dir; + } + msleep(data->pdata->hard_rst_dly); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + + return 0; + } + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); + if (gpio_is_valid(data->pdata->reset_gpio)) { + /* + * This is intended to save leakage current + * only. Even if the call(gpio_direction_input) + * fails, only leakage current will be more but + * functionality will not be affected. + */ + err = gpio_direction_input(data->pdata->reset_gpio); + if (err) { + dev_err(&data->client->dev, + "unable to set direction for gpio [%d]\n", + data->pdata->irq_gpio); + } + gpio_free(data->pdata->reset_gpio); + } + + return 0; + +err_reset_gpio_dir: + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); +err_irq_gpio_dir: + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); +err_irq_gpio_req: + return err; +} + static int ft5x06_power_on(struct ft5x06_ts_data *data, bool on) { int rc; @@ -382,7 +652,11 @@ power_off: if (rc) { dev_err(&data->client->dev, "Regulator vcc_i2c disable failed rc=%d\n", rc); - regulator_enable(data->vdd); + rc = regulator_enable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + } } return rc; @@ -455,6 +729,60 @@ pwr_deinit: return 0; } +static int ft5x06_ts_pinctrl_init(struct ft5x06_ts_data *ft5x06_data) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + ft5x06_data->ts_pinctrl = devm_pinctrl_get(&(ft5x06_data->client->dev)); + if (IS_ERR_OR_NULL(ft5x06_data->ts_pinctrl)) { + retval = PTR_ERR(ft5x06_data->ts_pinctrl); + dev_dbg(&ft5x06_data->client->dev, + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + ft5x06_data->pinctrl_state_active + = pinctrl_lookup_state(ft5x06_data->ts_pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_active)) { + retval = PTR_ERR(ft5x06_data->pinctrl_state_active); + dev_err(&ft5x06_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + ft5x06_data->pinctrl_state_suspend + = pinctrl_lookup_state(ft5x06_data->ts_pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_suspend)) { + retval = PTR_ERR(ft5x06_data->pinctrl_state_suspend); + dev_err(&ft5x06_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + ft5x06_data->pinctrl_state_release + = pinctrl_lookup_state(ft5x06_data->ts_pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_release)) { + retval = PTR_ERR(ft5x06_data->pinctrl_state_release); + dev_dbg(&ft5x06_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(ft5x06_data->ts_pinctrl); +err_pinctrl_get: + ft5x06_data->ts_pinctrl = NULL; + return retval; +} + #ifdef CONFIG_PM static int ft5x06_ts_suspend(struct device *dev) { @@ -472,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 */ @@ -479,7 +817,7 @@ static int ft5x06_ts_suspend(struct device *dev) input_mt_slot(data->input_dev, i); input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, 0); } - input_report_key(data->input_dev, BTN_TOUCH, 0); + input_mt_report_pointer_emulation(data->input_dev, false); input_sync(data->input_dev); if (gpio_is_valid(data->pdata->reset_gpio)) { @@ -502,10 +840,40 @@ static int ft5x06_ts_suspend(struct device *dev) } } + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_suspend); + if (err < 0) + dev_err(dev, "Cannot get suspend pinctrl state\n"); + } + + err = ft5x06_gpio_configure(data, false); + if (err < 0) { + dev_err(&data->client->dev, + "failed to put gpios in suspend state\n"); + goto gpio_configure_fail; + } + data->suspended = true; return 0; +gpio_configure_fail: + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_active); + if (err < 0) + dev_err(dev, "Cannot get active pinctrl state\n"); + } + if (data->pdata->power_on) { + err = data->pdata->power_on(true); + if (err) + dev_err(dev, "power on failed"); + } else { + err = ft5x06_power_on(data, true); + if (err) + dev_err(dev, "power on failed"); + } pwr_off_fail: if (gpio_is_valid(data->pdata->reset_gpio)) { gpio_set_value_cansleep(data->pdata->reset_gpio, 0); @@ -526,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) { @@ -540,6 +920,20 @@ static int ft5x06_ts_resume(struct device *dev) } } + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_active); + if (err < 0) + dev_err(dev, "Cannot get active pinctrl state\n"); + } + + err = ft5x06_gpio_configure(data, true); + if (err < 0) { + dev_err(&data->client->dev, + "failed to put gpios in resue state\n"); + goto err_gpio_configuration; + } + if (gpio_is_valid(data->pdata->reset_gpio)) { gpio_set_value_cansleep(data->pdata->reset_gpio, 0); msleep(data->pdata->hard_rst_dly); @@ -553,8 +947,46 @@ static int ft5x06_ts_resume(struct device *dev) data->suspended = false; return 0; + +err_gpio_configuration: + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_suspend); + if (err < 0) + dev_err(dev, "Cannot get suspend pinctrl state\n"); + } + if (data->pdata->power_on) { + err = data->pdata->power_on(false); + if (err) + dev_err(dev, "power off failed"); + } else { + err = ft5x06_power_on(data, false); + if (err) + dev_err(dev, "power off failed"); + } + return err; } +static const struct dev_pm_ops ft5x06_ts_pm_ops = { +#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) + .suspend = ft5x06_ts_suspend, + .resume = ft5x06_ts_resume, +#endif +}; + +#else +static int ft5x06_ts_suspend(struct device *dev) +{ + return 0; +} + +static int ft5x06_ts_resume(struct device *dev) +{ + return 0; +} + +#endif + #if defined(CONFIG_FB) static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) @@ -595,14 +1027,6 @@ static void ft5x06_ts_late_resume(struct early_suspend *handler) } #endif -static const struct dev_pm_ops ft5x06_ts_pm_ops = { -#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) - .suspend = ft5x06_ts_suspend, - .resume = ft5x06_ts_resume, -#endif -}; -#endif - static int ft5x06_auto_cal(struct i2c_client *client) { struct ft5x06_ts_data *data = i2c_get_clientdata(client); @@ -647,13 +1071,23 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, u8 reset_reg; u8 w_buf[FT_MAX_WR_BUF] = {0}, r_buf[FT_MAX_RD_BUF] = {0}; u8 pkt_buf[FT_FW_PKT_LEN + FT_FW_PKT_META_LEN]; - int rc, i, j, temp; + int i, j, temp; u32 pkt_num, pkt_len; + u8 is_5336_new_bootloader = false; + u8 is_5336_fwsize_30 = false; u8 fw_ecc; + /* determine firmware size */ + if (*(data + data_len - FT_BLOADER_SIZE_OFF) == FT_BLOADER_NEW_SIZE) + is_5336_fwsize_30 = true; + else + is_5336_fwsize_30 = false; + for (i = 0, j = 0; i < FT_UPGRADE_LOOP; i++) { + msleep(FT_EARSE_DLY_MS); /* reset - write 0xaa and 0x55 to reset register */ - if (ts_data->family_id == FT6X06_ID) + if (ts_data->family_id == FT6X06_ID + || ts_data->family_id == FT6X36_ID) reset_reg = FT_RST_CMD_REG2; else reset_reg = FT_RST_CMD_REG1; @@ -662,16 +1096,17 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, msleep(info.delay_aa); ft5x0x_write_reg(client, reset_reg, FT_UPGRADE_55); - msleep(info.delay_55); + if (i <= (FT_UPGRADE_LOOP / 2)) + msleep(info.delay_55 + i * 3); + else + msleep(info.delay_55 - (i - (FT_UPGRADE_LOOP / 2)) * 2); /* Enter upgrade mode */ w_buf[0] = FT_UPGRADE_55; - w_buf[1] = FT_UPGRADE_AA; - do { - j++; - rc = ft5x06_i2c_write(client, w_buf, 2); - msleep(FT_RETRY_DLY); - } while (rc <= 0 && j < FT_MAX_TRIES); + ft5x06_i2c_write(client, w_buf, 1); + usleep_range(FT_55_AA_DLY_NS, FT_55_AA_DLY_NS + 1); + w_buf[0] = FT_UPGRADE_AA; + ft5x06_i2c_write(client, w_buf, 1); /* check READ_ID */ msleep(info.delay_readid); @@ -684,7 +1119,9 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, if (r_buf[0] != info.upgrade_id_1 || r_buf[1] != info.upgrade_id_2) { - dev_err(&client->dev, "Upgrade ID mismatch(%d)\n", i); + dev_err(&client->dev, "Upgrade ID mismatch(%d), IC=0x%x 0x%x, info=0x%x 0x%x\n", + i, r_buf[0], r_buf[1], + info.upgrade_id_1, info.upgrade_id_2); } else break; } @@ -694,17 +1131,44 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, return -EIO; } + w_buf[0] = 0xcd; + ft5x06_i2c_read(client, w_buf, 1, r_buf, 1); + + if (r_buf[0] <= 4) + is_5336_new_bootloader = FT_BLOADER_VERSION_LZ4; + else if (r_buf[0] == 7) + is_5336_new_bootloader = FT_BLOADER_VERSION_Z7; + else if (r_buf[0] >= 0x0f && + ((ts_data->family_id == FT_FT5336_FAMILY_ID_0x11) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x12) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x13) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x14))) + is_5336_new_bootloader = FT_BLOADER_VERSION_GZF; + else + is_5336_new_bootloader = FT_BLOADER_VERSION_LZ4; + + dev_dbg(&client->dev, "bootloader type=%d, r_buf=0x%x, family_id=0x%x\n", + is_5336_new_bootloader, r_buf[0], ts_data->family_id); + /* is_5336_new_bootloader = FT_BLOADER_VERSION_GZF; */ + /* erase app and panel paramenter area */ w_buf[0] = FT_ERASE_APP_REG; ft5x06_i2c_write(client, w_buf, 1); msleep(info.delay_erase_flash); - w_buf[0] = FT_ERASE_PANEL_REG; - ft5x06_i2c_write(client, w_buf, 1); + if (is_5336_fwsize_30) { + w_buf[0] = FT_ERASE_PANEL_REG; + ft5x06_i2c_write(client, w_buf, 1); + } msleep(FT_EARSE_DLY_MS); /* program firmware */ - data_len = data_len - 8; + if (is_5336_new_bootloader == FT_BLOADER_VERSION_LZ4 + || is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) + data_len = data_len - FT_DATA_LEN_OFF_OLD_FW; + else + data_len = data_len - FT_DATA_LEN_OFF_NEW_FW; + pkt_num = (data_len) / FT_FW_PKT_LEN; pkt_len = FT_FW_PKT_LEN; pkt_buf[0] = FT_FW_START_REG; @@ -747,17 +1211,45 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, } /* send the finishing packet */ - for (i = 0; i < 6; i++) { - temp = FT_FW_LAST_PKT + i; - pkt_buf[2] = (u8) (temp >> 8); - pkt_buf[3] = (u8) temp; - temp = 1; - pkt_buf[4] = (u8) (temp >> 8); - pkt_buf[5] = (u8) temp; - pkt_buf[6] = data[data_len + i]; - fw_ecc ^= pkt_buf[6]; - ft5x06_i2c_write(client, pkt_buf, temp + FT_FW_PKT_META_LEN); - msleep(FT_FW_PKT_DLY_MS); + if (is_5336_new_bootloader == FT_BLOADER_VERSION_LZ4 || + is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) { + for (i = 0; i < FT_FINISHING_PKT_LEN_OLD_FW; i++) { + if (is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) + temp = FT_MAGIC_BLOADER_Z7 + i; + else if (is_5336_new_bootloader == + FT_BLOADER_VERSION_LZ4) + temp = FT_MAGIC_BLOADER_LZ4 + i; + pkt_buf[2] = (u8)(temp >> 8); + pkt_buf[3] = (u8)temp; + temp = 1; + pkt_buf[4] = (u8)(temp >> 8); + pkt_buf[5] = (u8)temp; + pkt_buf[6] = data[data_len + i]; + fw_ecc ^= pkt_buf[6]; + + ft5x06_i2c_write(client, + pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + } + } else if (is_5336_new_bootloader == FT_BLOADER_VERSION_GZF) { + for (i = 0; i < FT_FINISHING_PKT_LEN_NEW_FW; i++) { + if (is_5336_fwsize_30) + temp = FT_MAGIC_BLOADER_GZF_30 + i; + else + temp = FT_MAGIC_BLOADER_GZF + i; + pkt_buf[2] = (u8)(temp >> 8); + pkt_buf[3] = (u8)temp; + temp = 1; + pkt_buf[4] = (u8)(temp >> 8); + pkt_buf[5] = (u8)temp; + pkt_buf[6] = data[data_len + i]; + fw_ecc ^= pkt_buf[6]; + + ft5x06_i2c_write(client, + pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + + } } /* verify checksum */ @@ -784,9 +1276,14 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) struct ft5x06_ts_data *data = dev_get_drvdata(dev); const struct firmware *fw = NULL; int rc; - u8 fw_file_maj, fw_file_min, fw_file_sub_min; + u8 fw_file_maj, fw_file_min, fw_file_sub_min, fw_file_vendor_id; bool fw_upgrade = false; + if (data->suspended) { + dev_err(dev, "Device is in suspend state: Exit FW upgrade\n"); + return -EBUSY; + } + rc = request_firmware(&fw, data->fw_name, dev); if (rc < 0) { dev_err(dev, "Request firmware failed - %s (%d)\n", @@ -795,31 +1292,32 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) } if (fw->size < FT_FW_MIN_SIZE || fw->size > FT_FW_MAX_SIZE) { - dev_err(dev, "Invalid firmware size (%d)\n", fw->size); + dev_err(dev, "Invalid firmware size (%zu)\n", fw->size); rc = -EIO; goto rel_fw; } - fw_file_maj = FT_FW_FILE_MAJ_VER(fw); + if (data->family_id == FT6X36_ID) { + fw_file_maj = FT_FW_FILE_MAJ_VER_FT6X36(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID_FT6X36(fw); + } else { + fw_file_maj = FT_FW_FILE_MAJ_VER(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID(fw); + } fw_file_min = FT_FW_FILE_MIN_VER(fw); fw_file_sub_min = FT_FW_FILE_SUB_MIN_VER(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID(fw); dev_info(dev, "Current firmware: %d.%d.%d", data->fw_ver[0], data->fw_ver[1], data->fw_ver[2]); dev_info(dev, "New firmware: %d.%d.%d", fw_file_maj, fw_file_min, fw_file_sub_min); - if (force) { + if (force) + fw_upgrade = true; + else if ((data->fw_ver[0] < fw_file_maj) && + data->fw_vendor_id == fw_file_vendor_id) fw_upgrade = true; - } else if (data->fw_ver[0] == fw_file_maj) { - if (data->fw_ver[1] < fw_file_min) - fw_upgrade = true; - else if (data->fw_ver[2] < fw_file_sub_min) - fw_upgrade = true; - else - dev_info(dev, "No need to upgrade\n"); - } else - dev_info(dev, "Firmware versions do not match\n"); if (!fw_upgrade) { dev_info(dev, "Exiting fw upgrade...\n"); @@ -828,7 +1326,7 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) } /* start firmware upgrade */ - if (FT_FW_CHECK(fw)) { + if (FT_FW_CHECK(fw, data)) { rc = ft5x06_fw_upgrade_start(data->client, fw->data, fw->size); if (rc < 0) dev_err(dev, "update failed (%d). try later...\n", rc); @@ -1251,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; @@ -1286,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; @@ -1335,7 +1837,7 @@ static int ft5x06_ts_probe(struct i2c_client *client, data->tch_data_len = FT_TCH_LEN(pdata->num_max_touches); data->tch_data = devm_kzalloc(&client->dev, data->tch_data_len, GFP_KERNEL); - if (!data) + if (!data->tch_data) return -ENOMEM; input_dev = input_allocate_device(); @@ -1360,12 +1862,11 @@ static int ft5x06_ts_probe(struct i2c_client *client, __set_bit(BTN_TOUCH, input_dev->keybit); __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); - input_mt_init_slots(input_dev, pdata->num_max_touches); + input_mt_init_slots(input_dev, pdata->num_max_touches, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, pdata->x_max, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, pdata->y_max, 0, 0); - input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, FT_PRESS, 0, 0); err = input_register_device(input_dev); if (err) { @@ -1401,35 +1902,26 @@ static int ft5x06_ts_probe(struct i2c_client *client, } } - 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) { + err = ft5x06_ts_pinctrl_init(data); + if (!err && data->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_active); + if (err < 0) { dev_err(&client->dev, - "set_direction for irq gpio failed\n"); - goto free_irq_gpio; + "failed to select pin to active state"); } } - 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(data->pdata->hard_rst_dly); - gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + err = ft5x06_gpio_configure(data, true); + if (err < 0) { + dev_err(&client->dev, + "Failed to configure the gpios\n"); + goto err_gpio_req; } /* make sure CTP already finish startup process */ @@ -1440,30 +1932,75 @@ static int ft5x06_ts_probe(struct i2c_client *client, err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); if (err < 0) { dev_err(&client->dev, "version read failed"); - goto free_reset_gpio; + goto free_gpio; } dev_info(&client->dev, "Device ID = 0x%x\n", reg_value); if ((pdata->family_id != reg_value) && (!pdata->ignore_id_check)) { dev_err(&client->dev, "%s:Unsupported controller\n", __func__); - goto free_reset_gpio; + goto free_gpio; } - data->family_id = reg_value; + data->family_id = pdata->family_id; err = request_threaded_irq(client->irq, NULL, - ft5x06_ts_interrupt, pdata->irqflags, - client->dev.driver->name, data); + ft5x06_ts_interrupt, + IRQF_ONESHOT, + client->dev.driver->name, data); if (err) { dev_err(&client->dev, "request irq failed\n"); - goto free_reset_gpio; + 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); @@ -1538,6 +2075,7 @@ static int ft5x06_ts_probe(struct i2c_client *client, dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4); ft5x06_update_fw_ver(data); + ft5x06_update_fw_vendor_id(data); FT_STORE_TS_INFO(data->ts_info, data->family_id, data->pdata->name, data->pdata->num_max_touches, data->pdata->group_id, @@ -1571,15 +2109,41 @@ 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_reset_gpio: +free_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->irq_gpio); -pwr_off: +err_gpio_req: + if (data->ts_pinctrl) { + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { + devm_pinctrl_put(data->ts_pinctrl); + data->ts_pinctrl = NULL; + } else { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_release); + if (err) + pr_err("failed to select relase pinctrl state\n"); + } + } if (pdata->power_on) pdata->power_on(false); else @@ -1591,15 +2155,26 @@ 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; } 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); @@ -1620,6 +2195,18 @@ static int ft5x06_ts_remove(struct i2c_client *client) if (gpio_is_valid(data->pdata->irq_gpio)) gpio_free(data->pdata->irq_gpio); + if (data->ts_pinctrl) { + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { + devm_pinctrl_put(data->ts_pinctrl); + data->ts_pinctrl = NULL; + } else { + retval = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_release); + if (retval < 0) + pr_err("failed to select release pinctrl state\n"); + } + } + if (data->pdata->power_on) data->pdata->power_on(false); else @@ -1653,7 +2240,7 @@ static const struct of_device_id ft5x06_match_table[] = { static struct i2c_driver ft5x06_ts_driver = { .probe = ft5x06_ts_probe, - .remove = __devexit_p(ft5x06_ts_remove), + .remove = ft5x06_ts_remove, .driver = { .name = "ft5x06_ts", .owner = THIS_MODULE, diff --git a/include/linux/input/ft5x06_ts.h b/include/linux/input/ft5x06_ts.h index 1340737070f7..bd37af71fe0d 100644 --- a/include/linux/input/ft5x06_ts.h +++ b/include/linux/input/ft5x06_ts.h @@ -22,6 +22,7 @@ #define FT5X16_ID 0x0A #define FT5X36_ID 0x14 #define FT6X06_ID 0x06 +#define FT6X36_ID 0x36 struct fw_upgrade_info { bool auto_cal; @@ -33,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; @@ -59,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); };