Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cfb1bb2f authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "input: ft5x06: Add support for firmware upgrade"

parents d03ba919 d898291c
Loading
Loading
Loading
Loading
+536 −48
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/firmware.h>
#include <linux/input/ft5x06_ts.h>

#if defined(CONFIG_FB)
@@ -35,7 +36,7 @@
#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
@@ -56,26 +57,116 @@
#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
#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 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
#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 */
@@ -86,6 +177,15 @@ struct ts_event {
	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;
@@ -93,6 +193,9 @@ struct ft5x06_ts_data {
	const struct ft5x06_ts_platform_data *pdata;
	struct regulator *vdd;
	struct regulator *vcc_i2c;
	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)
@@ -160,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;
@@ -300,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);
@@ -318,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);
@@ -333,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;
@@ -360,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));
	}

@@ -430,11 +548,345 @@ static const struct dev_pm_ops ft5x06_ts_pm_ops = {
};
#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;
@@ -446,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;
	}
@@ -676,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, &reg_addr, 1, &reg_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;
	}

	data->family_id = reg_value;

	/* get some register information */
	reg_addr = FT5X06_REG_FW_VER;
	reg_addr = FT_REG_FW_VER;
	err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_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, &reg_addr, 1, &reg_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, &reg_addr, 1, &reg_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,
@@ -715,6 +1173,26 @@ static int ft5x06_ts_probe(struct i2c_client *client,
		dev_err(&client->dev, "request irq failed\n");
		goto free_reset_gpio;
	}

	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;

@@ -725,7 +1203,7 @@ static int ft5x06_ts_probe(struct i2c_client *client,
			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);
@@ -733,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);
@@ -763,6 +1247,10 @@ static int ft5x06_ts_remove(struct i2c_client *client)
{
	struct ft5x06_ts_data *data = i2c_get_clientdata(client);

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