diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2016-08-11 12:48:45 -0700 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-08-11 12:48:44 -0700 |
| commit | ea85807058c736ff0b46293ae7a9ad21140d5cbf (patch) | |
| tree | a3f7706933ecf00945017a9b4b8bcc275b694f41 /drivers/input | |
| parent | f3c95d6021a356a029b35b08542d30709bca2737 (diff) | |
| parent | fad9dc0827b43a04073cd26105794fc045d1928f (diff) | |
Merge "input: ft5x06: Add support for firmware upgrade"
Diffstat (limited to 'drivers/input')
| -rw-r--r-- | drivers/input/touchscreen/ft5x06_ts.c | 632 |
1 files changed, 578 insertions, 54 deletions
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 79aadf4507b9..45ce036b1693 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -26,12 +26,17 @@ #include <linux/gpio.h> #include <linux/of_gpio.h> #include <linux/regulator/consumer.h> +#include <linux/firmware.h> #include <linux/input/ft5x06_ts.h> -#ifdef CONFIG_HAS_EARLYSUSPEND +#if defined(CONFIG_FB) +#include <linux/notifier.h> +#include <linux/fb.h> + +#elif defined(CONFIG_HAS_EARLYSUSPEND) #include <linux/earlysuspend.h> /* Early-suspend level */ -#define FT5X06_SUSPEND_LEVEL 1 +#define FT_SUSPEND_LEVEL 1 #endif #define CFG_MAX_TOUCH_POINTS 5 @@ -51,37 +56,136 @@ #define POINT_READ_BUF (3 + FT_TOUCH_STEP * CFG_MAX_TOUCH_POINTS) -/*register address*/ -#define FT5X06_REG_ID 0xA3 -#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 - -#define FT5X06_COORDS_ARR_SIZE 4 +/* register address*/ +#define FT_REG_DEV_MODE 0x00 +#define FT_DEV_MODE_REG_CAL 0x02 +#define FT_REG_ID 0xA3 +#define FT_REG_PMODE 0xA5 +#define FT_REG_FW_VER 0xA6 +#define FT_REG_POINT_RATE 0x88 +#define FT_REG_THGROUP 0x80 +#define FT_REG_ECC 0xCC +#define FT_REG_RESET_FW 0x07 + +/* power register bits */ +#define FT_PMODE_ACTIVE 0x00 +#define FT_PMODE_MONITOR 0x01 +#define FT_PMODE_STANDBY 0x02 +#define FT_PMODE_HIBERNATE 0x03 +#define FT_FACTORYMODE_VALUE 0x40 +#define FT_WORKMODE_VALUE 0x00 +#define FT_RST_CMD_REG 0xFC +#define FT_READ_ID_REG 0x90 +#define FT_ERASE_APP_REG 0x61 +#define FT_ERASE_PANEL_REG 0x63 +#define FT_FW_START_REG 0xBF + + +#define FT_VTG_MIN_UV 2600000 +#define FT_VTG_MAX_UV 3300000 +#define FT_I2C_VTG_MIN_UV 1800000 +#define FT_I2C_VTG_MAX_UV 1800000 + +#define FT_COORDS_ARR_SIZE 4 #define MAX_BUTTONS 4 +#define FT_8BIT_SHIFT 8 +#define FT_4BIT_SHIFT 4 +#define FT_FW_NAME_MAX_LEN 50 + +#define FT5316_ID 0x0A +#define FT5306I_ID 0x55 + +#define FT_UPGRADE_AA 0xAA +#define FT_UPGRADE_55 0x55 + +/* upgrade config of FT5606 */ +#define FT5606_UPGRADE_AA_DELAY 50 +#define FT5606_UPGRADE_55_DELAY 10 +#define FT5606_UPGRADE_ID_1 0x79 +#define FT5606_UPGRADE_ID_2 0x06 +#define FT5606_UPGRADE_READID_DELAY 100 +#define FT5606_UPGRADE_EARSE_DELAY 2000 + +/* upgrade config of FT5316 */ +#define FT5316_UPGRADE_AA_DELAY 50 +#define FT5316_UPGRADE_55_DELAY 30 +#define FT5316_UPGRADE_ID_1 0x79 +#define FT5316_UPGRADE_ID_2 0x07 +#define FT5316_UPGRADE_READID_DELAY 1 +#define FT5316_UPGRADE_EARSE_DELAY 1500 + +/* upgrade config of FT5x06(x=2,3,4) */ +#define FT5X06_UPGRADE_AA_DELAY 50 +#define FT5X06_UPGRADE_55_DELAY 30 +#define FT5X06_UPGRADE_ID_1 0x79 +#define FT5X06_UPGRADE_ID_2 0x03 +#define FT5X06_UPGRADE_READID_DELAY 1 +#define FT5X06_UPGRADE_EARSE_DELAY 2000 + +/* upgrade config of FT6208 */ +#define FT6208_UPGRADE_AA_DELAY 60 +#define FT6208_UPGRADE_55_DELAY 10 +#define FT6208_UPGRADE_ID_1 0x79 +#define FT6208_UPGRADE_ID_2 0x05 +#define FT6208_UPGRADE_READID_DELAY 10 +#define FT6208_UPGRADE_EARSE_DELAY 2000 + +#define FT_UPGRADE_INFO(x, y) do { \ + x->delay_55 = y##_UPGRADE_55_DELAY; \ + x->delay_aa = y##_UPGRADE_AA_DELAY; \ + x->upgrade_id_1 = y##_UPGRADE_ID_1; \ + x->upgrade_id_2 = y##_UPGRADE_ID_2; \ + x->delay_readid = y##_UPGRADE_READID_DELAY; \ + x->delay_earse_flash = y##_UPGRADE_EARSE_DELAY; \ + } while (0) + +#define FT_FW_MIN_SIZE 8 +#define FT_FW_MAX_SIZE 32768 +#define FT_FW_FILE_VER(x) ((x)->data[(x)->size - 2]) +#define FT_FW_CHECK(x) \ + (((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))) + +#define FT_MAX_TRIES 5 +#define FT_RETRY_DLY 20 + +#define FT_MAX_WR_BUF 10 +#define FT_MAX_RD_BUF 2 +#define FT_FW_PKT_LEN 128 +#define FT_FW_PKT_META_LEN 6 +#define FT_FW_PKT_DLY_MS 20 +#define FT_FW_LAST_PKT 0x6ffa +#define FT_EARSE_DLY_MS 100 + +#define FT_UPGRADE_LOOP 3 +#define FT_CAL_START 0x04 +#define FT_CAL_FIN 0x00 +#define FT_CAL_STORE 0x05 +#define FT_CAL_RETRY 100 +#define FT_REG_CAL 0x00 +#define FT_CAL_MASK 0x70 + struct ts_event { - u16 x[CFG_MAX_TOUCH_POINTS]; /*x coordinate */ - u16 y[CFG_MAX_TOUCH_POINTS]; /*y coordinate */ + 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 */ + u8 finger_id[CFG_MAX_TOUCH_POINTS]; /* touch ID */ u16 pressure; u8 touch_point; }; +struct upgrade_info { + u16 delay_aa; /* delay of write FT_UPGRADE_AA */ + u16 delay_55; /* delay of write FT_UPGRADE_55 */ + u8 upgrade_id_1; /* upgrade id 1 */ + u8 upgrade_id_2; /* upgrade id 2 */ + u16 delay_readid; /* delay of read id */ + u16 delay_earse_flash; /* delay of earse flash */ +}; + struct ft5x06_ts_data { struct i2c_client *client; struct input_dev *input_dev; @@ -89,7 +193,12 @@ struct ft5x06_ts_data { const struct ft5x06_ts_platform_data *pdata; struct regulator *vdd; struct regulator *vcc_i2c; -#ifdef CONFIG_HAS_EARLYSUSPEND + char fw_name[FT_FW_NAME_MAX_LEN]; + bool loading_fw; + u8 family_id; +#if defined(CONFIG_FB) + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) struct early_suspend early_suspend; #endif }; @@ -154,6 +263,21 @@ static int ft5x06_i2c_write(struct i2c_client *client, char *writebuf, return ret; } +static int ft5x0x_write_reg(struct i2c_client *client, u8 addr, const u8 val) +{ + u8 buf[2] = {0}; + + buf[0] = addr; + buf[1] = val; + + return ft5x06_i2c_write(client, buf, sizeof(buf)); +} + +static int ft5x0x_read_reg(struct i2c_client *client, u8 addr, u8 *val) +{ + return ft5x06_i2c_read(client, &addr, 1, val, 1); +} + static void ft5x06_report_value(struct ft5x06_ts_data *data) { struct ts_event *event = &data->event; @@ -294,8 +418,8 @@ static int ft5x06_power_init(struct ft5x06_ts_data *data, bool on) } if (regulator_count_voltages(data->vdd) > 0) { - rc = regulator_set_voltage(data->vdd, FT5X06_VTG_MIN_UV, - FT5X06_VTG_MAX_UV); + rc = regulator_set_voltage(data->vdd, FT_VTG_MIN_UV, + FT_VTG_MAX_UV); if (rc) { dev_err(&data->client->dev, "Regulator set_vtg failed vdd rc=%d\n", rc); @@ -312,8 +436,8 @@ static int ft5x06_power_init(struct ft5x06_ts_data *data, bool on) } 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); + rc = regulator_set_voltage(data->vcc_i2c, FT_I2C_VTG_MIN_UV, + FT_I2C_VTG_MAX_UV); if (rc) { dev_err(&data->client->dev, "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); @@ -327,19 +451,19 @@ 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); + regulator_set_voltage(data->vdd, 0, FT_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_set_voltage(data->vdd, 0, FT_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_set_voltage(data->vcc_i2c, 0, FT_I2C_VTG_MAX_UV); regulator_put(data->vcc_i2c); return 0; @@ -354,8 +478,8 @@ static int ft5x06_ts_suspend(struct device *dev) disable_irq(data->client->irq); if (gpio_is_valid(data->pdata->reset_gpio)) { - txbuf[0] = FT5X06_REG_PMODE; - txbuf[1] = FT5X06_PMODE_HIBERNATE; + txbuf[0] = FT_REG_PMODE; + txbuf[1] = FT_PMODE_HIBERNATE; ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf)); } @@ -376,7 +500,27 @@ static int ft5x06_ts_resume(struct device *dev) return 0; } -#ifdef CONFIG_HAS_EARLYSUSPEND +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct ft5x06_ts_data *ft5x06_data = + container_of(self, struct ft5x06_ts_data, fb_notif); + + if (evdata && evdata->data && event == FB_EVENT_BLANK && + ft5x06_data && ft5x06_data->client) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) + ft5x06_ts_resume(&ft5x06_data->client->dev); + else if (*blank == FB_BLANK_POWERDOWN) + ft5x06_ts_suspend(&ft5x06_data->client->dev); + } + + return 0; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) static void ft5x06_ts_early_suspend(struct early_suspend *handler) { struct ft5x06_ts_data *data = container_of(handler, @@ -397,18 +541,352 @@ static void ft5x06_ts_late_resume(struct early_suspend *handler) #endif static const struct dev_pm_ops ft5x06_ts_pm_ops = { -#ifndef CONFIG_HAS_EARLYSUSPEND +#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) +{ + u8 temp = 0, i; + + /* set to factory mode */ + msleep(2 * FT_STARTUP_DLY); + ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE); + msleep(FT_STARTUP_DLY); + + /* start calibration */ + ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_START); + msleep(2 * FT_STARTUP_DLY); + for (i = 0; i < FT_CAL_RETRY; i++) { + ft5x0x_read_reg(client, FT_REG_CAL, &temp); + /* return to normal mode, calibration finish */ + if (((temp & FT_CAL_MASK) >> FT_4BIT_SHIFT) == FT_CAL_FIN) + break; + } + + /* calibration OK */ + msleep(2 * FT_STARTUP_DLY); + ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE); + msleep(FT_STARTUP_DLY); + + /* store calibration data */ + ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_STORE); + msleep(2 * FT_STARTUP_DLY); + + /* set to normal mode */ + ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_WORKMODE_VALUE); + msleep(2 * FT_STARTUP_DLY); + + return 0; +} + +static int ft5x06_get_upgrade_info(u8 family_id, struct upgrade_info *info) +{ + switch (family_id) { + case FT5306I_ID: + FT_UPGRADE_INFO(info, FT5X06); + break; + case FT5316_ID: + FT_UPGRADE_INFO(info, FT5316); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ft5x06_fw_upgrade_start(struct i2c_client *client, + const u8 *data, u32 data_len) +{ + struct ft5x06_ts_data *ts_data = i2c_get_clientdata(client); + struct upgrade_info info; + 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; + u32 pkt_num, pkt_len; + u8 fw_ecc; + + rc = ft5x06_get_upgrade_info(ts_data->family_id, &info); + if (rc < 0) { + dev_err(&client->dev, "Cannot get upgrade information!\n"); + return -EINVAL; + } + + for (i = 0; i < FT_UPGRADE_LOOP; i++) { + /* reset - write 0xaa and 0x55 to register 0xfc */ + ft5x0x_write_reg(client, FT_RST_CMD_REG, FT_UPGRADE_AA); + msleep(info.delay_aa); + + ft5x0x_write_reg(client, FT_RST_CMD_REG, FT_UPGRADE_55); + msleep(info.delay_55); + + /* Enter upgrade mode */ + w_buf[0] = FT_UPGRADE_55; + w_buf[1] = FT_UPGRADE_AA; + do { + i++; + rc = ft5x06_i2c_write(client, w_buf, 2); + msleep(FT_RETRY_DLY); + } while (rc <= 0 && i < FT_MAX_TRIES); + + /* check READ_ID */ + msleep(info.delay_readid); + w_buf[0] = FT_READ_ID_REG; + w_buf[1] = 0x00; + w_buf[2] = 0x00; + w_buf[3] = 0x00; + + ft5x06_i2c_read(client, w_buf, 4, r_buf, 2); + + 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); + } else + break; + } + + if (i >= FT_UPGRADE_LOOP) { + dev_err(&client->dev, "Abort upgrade\n"); + return -EIO; + } + + /* erase app and panel paramenter area */ + w_buf[0] = FT_ERASE_APP_REG; + ft5x06_i2c_write(client, w_buf, 1); + msleep(info.delay_earse_flash); + + 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; + pkt_num = (data_len) / FT_FW_PKT_LEN; + pkt_len = FT_FW_PKT_LEN; + pkt_buf[0] = FT_FW_START_REG; + pkt_buf[1] = 0x00; + fw_ecc = 0; + + for (i = 0; i < pkt_num; i++) { + temp = i * FT_FW_PKT_LEN; + pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT); + pkt_buf[3] = (u8) temp; + pkt_buf[4] = (u8) (pkt_len >> FT_8BIT_SHIFT); + pkt_buf[5] = (u8) pkt_len; + + for (j = 0; j < FT_FW_PKT_LEN; j++) { + pkt_buf[6 + j] = data[i * FT_FW_PKT_LEN + j]; + fw_ecc ^= pkt_buf[6 + j]; + } + + ft5x06_i2c_write(client, pkt_buf, + FT_FW_PKT_LEN + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + } + + /* send remaining bytes */ + if ((data_len) % FT_FW_PKT_LEN > 0) { + temp = pkt_num * FT_FW_PKT_LEN; + pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT); + pkt_buf[3] = (u8) temp; + temp = (data_len) % FT_FW_PKT_LEN; + pkt_buf[4] = (u8) (temp >> FT_8BIT_SHIFT); + pkt_buf[5] = (u8) temp; + + for (i = 0; i < temp; i++) { + pkt_buf[6 + i] = data[pkt_num * FT_FW_PKT_LEN + i]; + fw_ecc ^= pkt_buf[6 + i]; + } + + ft5x06_i2c_write(client, pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + } + + /* 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); + } + + /* verify checksum */ + w_buf[0] = FT_REG_ECC; + ft5x06_i2c_read(client, w_buf, 1, r_buf, 1); + if (r_buf[0] != fw_ecc) { + dev_err(&client->dev, "ECC error! dev_ecc=%02x fw_ecc=%02x\n", + r_buf[0], fw_ecc); + return -EIO; + } + + /* reset */ + w_buf[0] = FT_REG_RESET_FW; + ft5x06_i2c_write(client, w_buf, 1); + msleep(FT_STARTUP_DLY); + + return 0; +} + +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 val = 0; + + rc = request_firmware(&fw, data->fw_name, dev); + if (rc < 0) { + dev_err(dev, "Request firmware failed - %s (%d)\n", + data->fw_name, rc); + return rc; + } + + if (fw->size < FT_FW_MIN_SIZE || fw->size > FT_FW_MAX_SIZE) { + dev_err(dev, "Invalid firmware size (%d)\n", fw->size); + rc = -EIO; + goto rel_fw; + } + + /* check firmware version */ + rc = ft5x0x_read_reg(data->client, FT_REG_FW_VER, &val); + if (rc < 0) { + dev_err(dev, "Get firmware version failed\n"); + goto rel_fw; + } + + if (val == FT_FW_FILE_VER(fw) && !force) { + dev_err(dev, "No need to update (0x%x)\n", val); + rc = -EFAULT; + goto rel_fw; + } + + dev_info(dev, "upgrade to fw ver 0x%x from 0x%x\n", + FT_FW_FILE_VER(fw), val); + + /* start firmware upgrade */ + if (FT_FW_CHECK(fw)) { + rc = ft5x06_fw_upgrade_start(data->client, fw->data, fw->size); + if (rc < 0) + dev_err(dev, "update failed (%d)\n", rc); + else + ft5x06_auto_cal(data->client); + } else { + dev_err(dev, "FW format error\n"); + rc = -EIO; + } + +rel_fw: + release_firmware(fw); + return rc; +} + +static ssize_t ft5x06_update_fw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 2, "%d\n", data->loading_fw); +} + +static ssize_t ft5x06_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + unsigned long val; + int rc; + + if (size > 2) + return -EINVAL; + + rc = kstrtoul(buf, 10, &val); + if (rc != 0) + return rc; + + mutex_lock(&data->input_dev->mutex); + if (!data->loading_fw && val) { + data->loading_fw = true; + ft5x06_fw_upgrade(dev, false); + data->loading_fw = false; + } + mutex_unlock(&data->input_dev->mutex); + + return size; +} + +static DEVICE_ATTR(update_fw, 0664, ft5x06_update_fw_show, + ft5x06_update_fw_store); + +static ssize_t ft5x06_force_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + unsigned long val; + int rc; + + if (size > 2) + return -EINVAL; + + rc = kstrtoul(buf, 10, &val); + if (rc != 0) + return rc; + + mutex_lock(&data->input_dev->mutex); + if (!data->loading_fw && val) { + data->loading_fw = true; + ft5x06_fw_upgrade(dev, true); + data->loading_fw = false; + } + mutex_unlock(&data->input_dev->mutex); + + return size; +} + +static DEVICE_ATTR(force_update_fw, 0664, ft5x06_update_fw_show, + ft5x06_force_update_fw_store); + +static ssize_t ft5x06_fw_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + + return snprintf(buf, FT_FW_NAME_MAX_LEN - 1, "%s\n", data->fw_name); +} + +static ssize_t ft5x06_fw_name_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + + if (size > FT_FW_NAME_MAX_LEN - 1) + return -EINVAL; + + strlcpy(data->fw_name, buf, size); + if (data->fw_name[size-1] == '\n') + data->fw_name[size-1] = 0; + + return size; +} + +static DEVICE_ATTR(fw_name, 0664, ft5x06_fw_name_show, ft5x06_fw_name_store); + #ifdef CONFIG_OF static int ft5x06_get_dt_coords(struct device *dev, char *name, struct ft5x06_ts_platform_data *pdata) { - u32 coords[FT5X06_COORDS_ARR_SIZE]; + u32 coords[FT_COORDS_ARR_SIZE]; struct property *prop; struct device_node *np = dev->of_node; int coords_size, rc; @@ -420,7 +898,7 @@ static int ft5x06_get_dt_coords(struct device *dev, char *name, return -ENODATA; coords_size = prop->length / sizeof(u32); - if (coords_size != FT5X06_COORDS_ARR_SIZE) { + if (coords_size != FT_COORDS_ARR_SIZE) { dev_err(dev, "invalid %s\n", name); return -EINVAL; } @@ -650,37 +1128,43 @@ static int ft5x06_ts_probe(struct i2c_client *client, msleep(FT_STARTUP_DLY); /* check the controller id */ - reg_addr = FT5X06_REG_ID; + reg_addr = FT_REG_ID; err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); if (err < 0) { dev_err(&client->dev, "version read failed"); return err; } + dev_info(&client->dev, "Device ID = 0x%x\n", reg_value); + if (pdata->family_id != reg_value) { dev_err(&client->dev, "%s:Unsupported controller\n", __func__); goto free_reset_gpio; } - /*get some register information */ - reg_addr = FT5X06_REG_FW_VER; + data->family_id = reg_value; + + /* get some register information */ + reg_addr = FT_REG_FW_VER; err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); - if (err) + if (err < 0) dev_err(&client->dev, "version read failed"); - dev_info(&client->dev, "[FTS] Firmware version = 0x%x\n", reg_value); + dev_info(&client->dev, "Firmware version = 0x%x\n", reg_value); - reg_addr = FT5X06_REG_POINT_RATE; + reg_addr = FT_REG_POINT_RATE; ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); - if (err) + if (err < 0) 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; + dev_dbg(&client->dev, "report rate = %dHz\n", reg_value * 10); + + reg_addr = FT_REG_THGROUP; err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); - if (err) + if (err < 0) dev_err(&client->dev, "threshold read failed"); - dev_dbg(&client->dev, "[FTS] touch threshold is %d.\n", reg_value * 4); + + dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4); err = request_threaded_irq(client->irq, NULL, ft5x06_ts_interrupt, pdata->irqflags, @@ -690,9 +1174,36 @@ static int ft5x06_ts_probe(struct i2c_client *client, goto free_reset_gpio; } -#ifdef CONFIG_HAS_EARLYSUSPEND + err = device_create_file(&client->dev, &dev_attr_fw_name); + if (err) { + dev_err(&client->dev, "sys file creation failed\n"); + goto irq_free; + } + + err = device_create_file(&client->dev, &dev_attr_update_fw); + if (err) { + dev_err(&client->dev, "sys file creation failed\n"); + goto free_fw_name_sys; + } + + err = device_create_file(&client->dev, &dev_attr_force_update_fw); + if (err) { + dev_err(&client->dev, "sys file creation failed\n"); + goto free_update_fw_sys; + } + + +#if defined(CONFIG_FB) + data->fb_notif.notifier_call = fb_notifier_callback; + + err = fb_register_client(&data->fb_notif); + + if (err) + dev_err(&client->dev, "Unable to register fb_notifier: %d\n", + err); +#elif defined(CONFIG_HAS_EARLYSUSPEND) data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + - FT5X06_SUSPEND_LEVEL; + FT_SUSPEND_LEVEL; data->early_suspend.suspend = ft5x06_ts_early_suspend; data->early_suspend.resume = ft5x06_ts_late_resume; register_early_suspend(&data->early_suspend); @@ -700,12 +1211,18 @@ static int ft5x06_ts_probe(struct i2c_client *client, return 0; +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); +irq_free: + free_irq(client->irq, data); 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); + gpio_free(pdata->irq_gpio); pwr_off: if (pdata->power_on) pdata->power_on(false); @@ -730,7 +1247,14 @@ static int ft5x06_ts_remove(struct i2c_client *client) { struct ft5x06_ts_data *data = i2c_get_clientdata(client); -#ifdef CONFIG_HAS_EARLYSUSPEND + device_remove_file(&client->dev, &dev_attr_force_update_fw); + device_remove_file(&client->dev, &dev_attr_update_fw); + device_remove_file(&client->dev, &dev_attr_fw_name); + +#if defined(CONFIG_FB) + if (fb_unregister_client(&data->fb_notif)) + dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); +#elif defined(CONFIG_HAS_EARLYSUSPEND) unregister_early_suspend(&data->early_suspend); #endif free_irq(client->irq, data); @@ -739,7 +1263,7 @@ static int ft5x06_ts_remove(struct i2c_client *client) gpio_free(data->pdata->reset_gpio); if (gpio_is_valid(data->pdata->irq_gpio)) - gpio_free(data->pdata->reset_gpio); + gpio_free(data->pdata->irq_gpio); if (data->pdata->power_on) data->pdata->power_on(false); |
