summaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-08-18 12:03:56 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-08-18 12:03:55 -0700
commitcfa9893e4ab36bf84146d2e69cd6736d1f20c824 (patch)
tree2b1c2c593c052e309b522e5f0d104222f381d5e9 /drivers/input
parent6e22b01c91afca47f413870bfec134267b2bf2f1 (diff)
parent72436be454654749d7c64676543ae6e7a579b042 (diff)
Merge "input: ft5x06_ts: Parse board specific parameters from pdata"
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/touchscreen/ft5x06_ts.c716
1 files changed, 504 insertions, 212 deletions
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
index 45ce036b1693..794ba264abdf 100644
--- a/drivers/input/touchscreen/ft5x06_ts.c
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -18,6 +18,7 @@
#include <linux/i2c.h>
#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
@@ -27,6 +28,7 @@
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/firmware.h>
+#include <linux/debugfs.h>
#include <linux/input/ft5x06_ts.h>
#if defined(CONFIG_FB)
@@ -39,22 +41,23 @@
#define FT_SUSPEND_LEVEL 1
#endif
-#define CFG_MAX_TOUCH_POINTS 5
+#define FT_DRIVER_VERSION 0x01
-#define FT_STARTUP_DLY 150
-#define FT_RESET_DLY 20
+#define FT_META_REGS 3
+#define FT_ONE_TCH_LEN 6
+#define FT_TCH_LEN(x) (FT_META_REGS + FT_ONE_TCH_LEN * x)
#define FT_PRESS 0x7F
#define FT_MAX_ID 0x0F
-#define FT_TOUCH_STEP 6
#define FT_TOUCH_X_H_POS 3
#define FT_TOUCH_X_L_POS 4
#define FT_TOUCH_Y_H_POS 5
#define FT_TOUCH_Y_L_POS 6
+#define FT_TD_STATUS 2
#define FT_TOUCH_EVENT_POS 3
#define FT_TOUCH_ID_POS 5
-
-#define POINT_READ_BUF (3 + FT_TOUCH_STEP * CFG_MAX_TOUCH_POINTS)
+#define FT_TOUCH_DOWN 0
+#define FT_TOUCH_CONTACT 2
/* register address*/
#define FT_REG_DEV_MODE 0x00
@@ -74,12 +77,14 @@
#define FT_PMODE_HIBERNATE 0x03
#define FT_FACTORYMODE_VALUE 0x40
#define FT_WORKMODE_VALUE 0x00
-#define FT_RST_CMD_REG 0xFC
+#define FT_RST_CMD_REG1 0xFC
+#define FT_RST_CMD_REG2 0xBC
#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_STATUS_NUM_TP_MASK 0x0F
#define FT_VTG_MIN_UV 2600000
#define FT_VTG_MAX_UV 3300000
@@ -95,51 +100,11 @@
#define FT5316_ID 0x0A
#define FT5306I_ID 0x55
+#define FT6X06_ID 0x06
#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])
@@ -159,7 +124,7 @@
#define FT_FW_LAST_PKT 0x6ffa
#define FT_EARSE_DLY_MS 100
-#define FT_UPGRADE_LOOP 3
+#define FT_UPGRADE_LOOP 10
#define FT_CAL_START 0x04
#define FT_CAL_FIN 0x00
#define FT_CAL_STORE 0x05
@@ -167,35 +132,38 @@
#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 */
- /* touch event: 0 -- down; 1-- contact; 2 -- contact */
- u8 touch_event[CFG_MAX_TOUCH_POINTS];
- u8 finger_id[CFG_MAX_TOUCH_POINTS]; /* touch ID */
- u16 pressure;
- u8 touch_point;
-};
+#define FT_INFO_MAX_LEN 512
-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 */
-};
+#define FT_STORE_TS_INFO(buf, id, name, max_tch, group_id, fw_name, fw_ver) \
+ snprintf(buf, FT_INFO_MAX_LEN, \
+ "controller\t= focaltech\n" \
+ "model\t\t= 0x%x\n" \
+ "name\t\t= %s\n" \
+ "max_touches\t= %d\n" \
+ "drv_ver\t\t= 0x%x\n" \
+ "group_id\t= 0x%x\n" \
+ "fw_name\t\t= %s\n" \
+ "fw_ver\t\t= 0x%x\n", id, name, \
+ max_tch, FT_DRIVER_VERSION, group_id, \
+ fw_name, fw_ver)
+
+#define FT_DEBUG_DIR_NAME "ts_debug"
struct ft5x06_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
- struct ts_event event;
const struct ft5x06_ts_platform_data *pdata;
struct regulator *vdd;
struct regulator *vcc_i2c;
char fw_name[FT_FW_NAME_MAX_LEN];
bool loading_fw;
u8 family_id;
+ struct dentry *dir;
+ u16 addr;
+ bool suspended;
+ char *ts_info;
+ u8 *tch_data;
+ u32 tch_data_len;
#if defined(CONFIG_FB)
struct notifier_block fb_notif;
#elif defined(CONFIG_HAS_EARLYSUSPEND)
@@ -278,85 +246,65 @@ 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)
+static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
{
- struct ts_event *event = &data->event;
- int i;
- int fingerdown = 0;
-
- for (i = 0; i < event->touch_point; i++) {
- if (event->touch_event[i] == 0 || event->touch_event[i] == 2) {
- event->pressure = FT_PRESS;
- fingerdown++;
- } else {
- event->pressure = 0;
- }
-
- input_report_abs(data->input_dev, ABS_MT_POSITION_X,
- event->x[i]);
- input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
- event->y[i]);
- input_report_abs(data->input_dev, ABS_MT_PRESSURE,
- event->pressure);
- input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
- event->finger_id[i]);
- input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
- event->pressure);
- input_mt_sync(data->input_dev);
+ 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;
+ bool update_input = false;
+
+ if (!data) {
+ pr_err("%s: Invalid data\n", __func__);
+ return IRQ_HANDLED;
}
- input_report_key(data->input_dev, BTN_TOUCH, !!fingerdown);
- input_sync(data->input_dev);
-}
+ ip_dev = data->input_dev;
+ buf = data->tch_data;
-static int ft5x06_handle_touchdata(struct ft5x06_ts_data *data)
-{
- struct ts_event *event = &data->event;
- int ret, i;
- u8 buf[POINT_READ_BUF] = { 0 };
- u8 pointid = FT_MAX_ID;
-
- ret = ft5x06_i2c_read(data->client, buf, 1, buf, POINT_READ_BUF);
- if (ret < 0) {
- dev_err(&data->client->dev, "%s read touchdata failed.\n",
- __func__);
- return ret;
- }
- memset(event, 0, sizeof(struct ts_event));
-
- event->touch_point = 0;
- for (i = 0; i < CFG_MAX_TOUCH_POINTS; i++) {
- pointid = (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4;
- if (pointid >= FT_MAX_ID)
+ 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;
+ }
+
+ for (i = 0; i < data->pdata->num_max_touches; i++) {
+ id = (buf[FT_TOUCH_ID_POS + FT_ONE_TCH_LEN * i]) >> 4;
+ if (id >= FT_MAX_ID)
break;
- event->touch_point++;
+ update_input = true;
- event->x[i] =
- (s16) (buf[FT_TOUCH_X_H_POS + FT_TOUCH_STEP * i] & 0x0F) <<
- 8 | (s16) buf[FT_TOUCH_X_L_POS + FT_TOUCH_STEP * i];
- event->y[i] =
- (s16) (buf[FT_TOUCH_Y_H_POS + FT_TOUCH_STEP * i] & 0x0F) <<
- 8 | (s16) buf[FT_TOUCH_Y_L_POS + FT_TOUCH_STEP * i];
- event->touch_event[i] =
- buf[FT_TOUCH_EVENT_POS + FT_TOUCH_STEP * i] >> 6;
- event->finger_id[i] =
- (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4;
- }
+ x = (buf[FT_TOUCH_X_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 |
+ (buf[FT_TOUCH_X_L_POS + FT_ONE_TCH_LEN * i]);
+ y = (buf[FT_TOUCH_Y_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 |
+ (buf[FT_TOUCH_Y_L_POS + FT_ONE_TCH_LEN * i]);
- ft5x06_report_value(data);
+ status = buf[FT_TOUCH_EVENT_POS + FT_ONE_TCH_LEN * i] >> 6;
- return 0;
-}
+ num_touches = buf[FT_TD_STATUS] & FT_STATUS_NUM_TP_MASK;
-static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
-{
- struct ft5x06_ts_data *data = dev_id;
- int rc;
+ /* invalid combination */
+ if (!num_touches && !status && !id)
+ break;
- rc = ft5x06_handle_touchdata(data);
- if (rc)
- pr_err("%s: handling touchdata failed\n", __func__);
+ 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);
+ }
+
+ if (update_input) {
+ input_mt_report_pointer_emulation(ip_dev, false);
+ input_sync(ip_dev);
+ }
return IRQ_HANDLED;
}
@@ -473,30 +421,99 @@ pwr_deinit:
static int ft5x06_ts_suspend(struct device *dev)
{
struct ft5x06_ts_data *data = dev_get_drvdata(dev);
- char txbuf[2];
+ char txbuf[2], i;
+ int err;
+
+ if (data->loading_fw) {
+ dev_info(dev, "Firmware loading in process...\n");
+ return 0;
+ }
+
+ if (data->suspended) {
+ dev_info(dev, "Already in suspend state\n");
+ return 0;
+ }
disable_irq(data->client->irq);
+ /* release all touches */
+ for (i = 0; i < data->pdata->num_max_touches; i++) {
+ 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_sync(data->input_dev);
+
if (gpio_is_valid(data->pdata->reset_gpio)) {
txbuf[0] = FT_REG_PMODE;
txbuf[1] = FT_PMODE_HIBERNATE;
ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf));
}
+ if (data->pdata->power_on) {
+ err = data->pdata->power_on(false);
+ if (err) {
+ dev_err(dev, "power off failed");
+ goto pwr_off_fail;
+ }
+ } else {
+ err = ft5x06_power_on(data, false);
+ if (err) {
+ dev_err(dev, "power off failed");
+ goto pwr_off_fail;
+ }
+ }
+
+ data->suspended = true;
+
return 0;
+
+pwr_off_fail:
+ if (gpio_is_valid(data->pdata->reset_gpio)) {
+ gpio_set_value_cansleep(data->pdata->reset_gpio, 0);
+ msleep(data->pdata->hard_rst_dly);
+ gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
+ }
+ enable_irq(data->client->irq);
+ return err;
}
static int ft5x06_ts_resume(struct device *dev)
{
struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ int err;
+
+ if (!data->suspended) {
+ dev_dbg(dev, "Already in awake state\n");
+ return 0;
+ }
+
+ if (data->pdata->power_on) {
+ err = data->pdata->power_on(true);
+ if (err) {
+ dev_err(dev, "power on failed");
+ return err;
+ }
+ } else {
+ err = ft5x06_power_on(data, true);
+ if (err) {
+ dev_err(dev, "power on failed");
+ return err;
+ }
+ }
if (gpio_is_valid(data->pdata->reset_gpio)) {
gpio_set_value_cansleep(data->pdata->reset_gpio, 0);
- msleep(FT_RESET_DLY);
+ msleep(data->pdata->hard_rst_dly);
gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
}
+
+ msleep(data->pdata->soft_rst_dly);
+
enable_irq(data->client->irq);
+ data->suspended = false;
+
return 0;
}
@@ -550,16 +567,17 @@ static const struct dev_pm_ops ft5x06_ts_pm_ops = {
static int ft5x06_auto_cal(struct i2c_client *client)
{
+ struct ft5x06_ts_data *data = i2c_get_clientdata(client);
u8 temp = 0, i;
/* set to factory mode */
- msleep(2 * FT_STARTUP_DLY);
+ msleep(2 * data->pdata->soft_rst_dly);
ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE);
- msleep(FT_STARTUP_DLY);
+ msleep(data->pdata->soft_rst_dly);
/* start calibration */
ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_START);
- msleep(2 * FT_STARTUP_DLY);
+ msleep(2 * data->pdata->soft_rst_dly);
for (i = 0; i < FT_CAL_RETRY; i++) {
ft5x0x_read_reg(client, FT_REG_CAL, &temp);
/* return to normal mode, calibration finish */
@@ -567,34 +585,18 @@ static int ft5x06_auto_cal(struct i2c_client *client)
break;
}
- /* calibration OK */
- msleep(2 * FT_STARTUP_DLY);
+ /*calibration OK */
+ msleep(2 * data->pdata->soft_rst_dly);
ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE);
- msleep(FT_STARTUP_DLY);
+ msleep(data->pdata->soft_rst_dly);
/* store calibration data */
ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_STORE);
- msleep(2 * FT_STARTUP_DLY);
+ msleep(2 * data->pdata->soft_rst_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;
- }
+ msleep(2 * data->pdata->soft_rst_dly);
return 0;
}
@@ -603,35 +605,35 @@ 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;
+ struct fw_upgrade_info info = ts_data->pdata->info;
+ 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;
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, j = 0; i < FT_UPGRADE_LOOP; i++) {
+ /* reset - write 0xaa and 0x55 to reset register */
+ if (ts_data->family_id == FT6X06_ID)
+ reset_reg = FT_RST_CMD_REG2;
+ else
+ reset_reg = FT_RST_CMD_REG1;
- 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);
+ ft5x0x_write_reg(client, reset_reg, FT_UPGRADE_AA);
msleep(info.delay_aa);
- ft5x0x_write_reg(client, FT_RST_CMD_REG, FT_UPGRADE_55);
+ ft5x0x_write_reg(client, reset_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++;
+ j++;
rc = ft5x06_i2c_write(client, w_buf, 2);
msleep(FT_RETRY_DLY);
- } while (rc <= 0 && i < FT_MAX_TRIES);
+ } while (rc <= 0 && j < FT_MAX_TRIES);
/* check READ_ID */
msleep(info.delay_readid);
@@ -657,7 +659,7 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client,
/* 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);
+ msleep(info.delay_erase_flash);
w_buf[0] = FT_ERASE_PANEL_REG;
ft5x06_i2c_write(client, w_buf, 1);
@@ -732,7 +734,9 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client,
/* reset */
w_buf[0] = FT_REG_RESET_FW;
ft5x06_i2c_write(client, w_buf, 1);
- msleep(FT_STARTUP_DLY);
+ msleep(ts_data->pdata->soft_rst_dly);
+
+ dev_info(&client->dev, "Firmware upgrade successful\n");
return 0;
}
@@ -778,13 +782,16 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force)
rc = ft5x06_fw_upgrade_start(data->client, fw->data, fw->size);
if (rc < 0)
dev_err(dev, "update failed (%d)\n", rc);
- else
+ else if (data->pdata->info.auto_cal)
ft5x06_auto_cal(data->client);
} else {
dev_err(dev, "FW format error\n");
rc = -EIO;
}
+ FT_STORE_TS_INFO(data->ts_info, data->family_id, data->pdata->name,
+ data->pdata->num_max_touches, data->pdata->group_id,
+ data->pdata->fw_name, FT_FW_FILE_VER(fw));
rel_fw:
release_firmware(fw);
return rc;
@@ -882,6 +889,138 @@ static ssize_t ft5x06_fw_name_store(struct device *dev,
static DEVICE_ATTR(fw_name, 0664, ft5x06_fw_name_show, ft5x06_fw_name_store);
+static bool ft5x06_debug_addr_is_valid(int addr)
+{
+ if (addr < 0 || addr > 0xFF) {
+ pr_err("FT reg address is invalid: 0x%x\n", addr);
+ return false;
+ }
+
+ return true;
+}
+
+static int ft5x06_debug_data_set(void *_data, u64 val)
+{
+ struct ft5x06_ts_data *data = _data;
+
+ mutex_lock(&data->input_dev->mutex);
+
+ if (ft5x06_debug_addr_is_valid(data->addr))
+ dev_info(&data->client->dev,
+ "Writing into FT registers not supported\n");
+
+ mutex_unlock(&data->input_dev->mutex);
+
+ return 0;
+}
+
+static int ft5x06_debug_data_get(void *_data, u64 *val)
+{
+ struct ft5x06_ts_data *data = _data;
+ int rc;
+ u8 reg;
+
+ mutex_lock(&data->input_dev->mutex);
+
+ if (ft5x06_debug_addr_is_valid(data->addr)) {
+ rc = ft5x0x_read_reg(data->client, data->addr, &reg);
+ if (rc < 0)
+ dev_err(&data->client->dev,
+ "FT read register 0x%x failed (%d)\n",
+ data->addr, rc);
+ else
+ *val = reg;
+ }
+
+ mutex_unlock(&data->input_dev->mutex);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, ft5x06_debug_data_get,
+ ft5x06_debug_data_set, "0x%02llX\n");
+
+static int ft5x06_debug_addr_set(void *_data, u64 val)
+{
+ struct ft5x06_ts_data *data = _data;
+
+ if (ft5x06_debug_addr_is_valid(val)) {
+ mutex_lock(&data->input_dev->mutex);
+ data->addr = val;
+ mutex_unlock(&data->input_dev->mutex);
+ }
+
+ return 0;
+}
+
+static int ft5x06_debug_addr_get(void *_data, u64 *val)
+{
+ struct ft5x06_ts_data *data = _data;
+
+ mutex_lock(&data->input_dev->mutex);
+
+ if (ft5x06_debug_addr_is_valid(data->addr))
+ *val = data->addr;
+
+ mutex_unlock(&data->input_dev->mutex);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, ft5x06_debug_addr_get,
+ ft5x06_debug_addr_set, "0x%02llX\n");
+
+static int ft5x06_debug_suspend_set(void *_data, u64 val)
+{
+ struct ft5x06_ts_data *data = _data;
+
+ mutex_lock(&data->input_dev->mutex);
+
+ if (val)
+ ft5x06_ts_suspend(&data->client->dev);
+ else
+ ft5x06_ts_resume(&data->client->dev);
+
+ mutex_unlock(&data->input_dev->mutex);
+
+ return 0;
+}
+
+static int ft5x06_debug_suspend_get(void *_data, u64 *val)
+{
+ struct ft5x06_ts_data *data = _data;
+
+ mutex_lock(&data->input_dev->mutex);
+ *val = data->suspended;
+ mutex_unlock(&data->input_dev->mutex);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, ft5x06_debug_suspend_get,
+ ft5x06_debug_suspend_set, "%lld\n");
+
+static int ft5x06_debug_dump_info(struct seq_file *m, void *v)
+{
+ struct ft5x06_ts_data *data = m->private;
+
+ seq_printf(m, "%s\n", data->ts_info);
+
+ return 0;
+}
+
+static int debugfs_dump_info_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ft5x06_debug_dump_info, inode->i_private);
+}
+
+static const struct file_operations debug_dump_info_fops = {
+ .owner = THIS_MODULE,
+ .open = debugfs_dump_info_open,
+ .read = seq_read,
+ .release = single_release,
+};
+
#ifdef CONFIG_OF
static int ft5x06_get_dt_coords(struct device *dev, char *name,
struct ft5x06_ts_platform_data *pdata)
@@ -936,6 +1075,13 @@ static int ft5x06_parse_dt(struct device *dev,
u32 temp_val, num_buttons;
u32 button_map[MAX_BUTTONS];
+ pdata->name = "focaltech";
+ rc = of_property_read_string(np, "focaltech,name", &pdata->name);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read name\n");
+ return rc;
+ }
+
rc = ft5x06_get_dt_coords(dev, "focaltech,panel-coords", pdata);
if (rc && (rc != -EINVAL))
return rc;
@@ -960,6 +1106,86 @@ static int ft5x06_parse_dt(struct device *dev,
if (pdata->irq_gpio < 0)
return pdata->irq_gpio;
+ pdata->fw_name = "ft_fw.bin";
+ rc = of_property_read_string(np, "focaltech,fw-name", &pdata->fw_name);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read fw name\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(np, "focaltech,group-id", &temp_val);
+ if (!rc)
+ pdata->group_id = temp_val;
+ else
+ return rc;
+
+ rc = of_property_read_u32(np, "focaltech,hard-reset-delay-ms",
+ &temp_val);
+ if (!rc)
+ pdata->hard_rst_dly = temp_val;
+ else
+ return rc;
+
+ rc = of_property_read_u32(np, "focaltech,soft-reset-delay-ms",
+ &temp_val);
+ if (!rc)
+ pdata->soft_rst_dly = temp_val;
+ else
+ return rc;
+
+ rc = of_property_read_u32(np, "focaltech,num-max-touches", &temp_val);
+ if (!rc)
+ pdata->num_max_touches = temp_val;
+ else
+ return rc;
+
+ rc = of_property_read_u32(np, "focaltech,fw-delay-aa-ms", &temp_val);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read fw delay aa\n");
+ return rc;
+ } else if (rc != -EINVAL)
+ pdata->info.delay_aa = temp_val;
+
+ rc = of_property_read_u32(np, "focaltech,fw-delay-55-ms", &temp_val);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read fw delay 55\n");
+ return rc;
+ } else if (rc != -EINVAL)
+ pdata->info.delay_55 = temp_val;
+
+ rc = of_property_read_u32(np, "focaltech,fw-upgrade-id1", &temp_val);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read fw upgrade id1\n");
+ return rc;
+ } else if (rc != -EINVAL)
+ pdata->info.upgrade_id_1 = temp_val;
+
+ rc = of_property_read_u32(np, "focaltech,fw-upgrade-id2", &temp_val);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read fw upgrade id2\n");
+ return rc;
+ } else if (rc != -EINVAL)
+ pdata->info.upgrade_id_2 = temp_val;
+
+ rc = of_property_read_u32(np, "focaltech,fw-delay-readid-ms",
+ &temp_val);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read fw delay read id\n");
+ return rc;
+ } else if (rc != -EINVAL)
+ pdata->info.delay_readid = temp_val;
+
+ rc = of_property_read_u32(np, "focaltech,fw-delay-era-flsh-ms",
+ &temp_val);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read fw delay erase flash\n");
+ return rc;
+ } else if (rc != -EINVAL)
+ pdata->info.delay_erase_flash = temp_val;
+
+ pdata->info.auto_cal = of_property_read_bool(np,
+ "focaltech,fw-auto-cal");
+
rc = of_property_read_u32(np, "focaltech,family-id", &temp_val);
if (!rc)
pdata->family_id = temp_val;
@@ -997,9 +1223,10 @@ static int ft5x06_ts_probe(struct i2c_client *client,
struct ft5x06_ts_platform_data *pdata;
struct ft5x06_ts_data *data;
struct input_dev *input_dev;
+ struct dentry *temp;
u8 reg_value;
u8 reg_addr;
- int err;
+ int err, len;
if (client->dev.of_node) {
pdata = devm_kzalloc(&client->dev,
@@ -1008,8 +1235,10 @@ static int ft5x06_ts_probe(struct i2c_client *client,
return -ENOMEM;
err = ft5x06_parse_dt(&client->dev, pdata);
- if (err)
+ if (err) {
+ dev_err(&client->dev, "DT parsing failed\n");
return err;
+ }
} else
pdata = client->dev.platform_data;
@@ -1023,15 +1252,31 @@ static int ft5x06_ts_probe(struct i2c_client *client,
return -ENODEV;
}
- data = kzalloc(sizeof(struct ft5x06_ts_data), GFP_KERNEL);
+ data = devm_kzalloc(&client->dev,
+ sizeof(struct ft5x06_ts_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (pdata->fw_name) {
+ len = strlen(pdata->fw_name);
+ if (len > FT_FW_NAME_MAX_LEN - 1) {
+ dev_err(&client->dev, "Invalid firmware name\n");
+ return -EINVAL;
+ }
+
+ strlcpy(data->fw_name, pdata->fw_name, len + 1);
+ }
+
+ 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)
return -ENOMEM;
input_dev = input_allocate_device();
if (!input_dev) {
- err = -ENOMEM;
dev_err(&client->dev, "failed to allocate input device\n");
- goto free_mem;
+ return -ENOMEM;
}
data->input_dev = input_dev;
@@ -1050,13 +1295,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_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_TRACKING_ID, 0,
- CFG_MAX_TOUCH_POINTS, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, FT_PRESS, 0, 0);
input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, FT_PRESS, 0, 0);
err = input_register_device(input_dev);
@@ -1120,19 +1363,19 @@ static int ft5x06_ts_probe(struct i2c_client *client,
"set_direction for reset gpio failed\n");
goto free_reset_gpio;
}
- msleep(FT_RESET_DLY);
+ msleep(data->pdata->hard_rst_dly);
gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
}
/* make sure CTP already finish startup process */
- msleep(FT_STARTUP_DLY);
+ msleep(data->pdata->soft_rst_dly);
/* check the controller 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;
+ goto free_reset_gpio;
}
dev_info(&client->dev, "Device ID = 0x%x\n", reg_value);
@@ -1144,28 +1387,6 @@ static int ft5x06_ts_probe(struct i2c_client *client,
data->family_id = reg_value;
- /* get some register information */
- reg_addr = FT_REG_FW_VER;
- err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
- if (err < 0)
- dev_err(&client->dev, "version read failed");
-
- dev_info(&client->dev, "Firmware version = 0x%x\n", reg_value);
-
- reg_addr = FT_REG_POINT_RATE;
- ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
- if (err < 0)
- dev_err(&client->dev, "report rate read failed");
-
- 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 < 0)
- dev_err(&client->dev, "threshold read failed");
-
- dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4);
-
err = request_threaded_irq(client->irq, NULL,
ft5x06_ts_interrupt, pdata->irqflags,
client->dev.driver->name, data);
@@ -1192,6 +1413,75 @@ static int ft5x06_ts_probe(struct i2c_client *client,
goto free_update_fw_sys;
}
+ data->dir = debugfs_create_dir(FT_DEBUG_DIR_NAME, NULL);
+ if (data->dir == NULL || IS_ERR(data->dir)) {
+ pr_err("debugfs_create_dir failed(%ld)\n", PTR_ERR(data->dir));
+ err = PTR_ERR(data->dir);
+ goto free_force_update_fw_sys;
+ }
+
+ temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, data->dir, data,
+ &debug_addr_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+ err = PTR_ERR(temp);
+ goto free_debug_dir;
+ }
+
+ temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, data->dir, data,
+ &debug_data_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+ err = PTR_ERR(temp);
+ goto free_debug_dir;
+ }
+
+ temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, data->dir,
+ data, &debug_suspend_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+ err = PTR_ERR(temp);
+ goto free_debug_dir;
+ }
+
+ temp = debugfs_create_file("dump_info", S_IRUSR | S_IWUSR, data->dir,
+ data, &debug_dump_info_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+ err = PTR_ERR(temp);
+ goto free_debug_dir;
+ }
+
+ data->ts_info = devm_kzalloc(&client->dev,
+ FT_INFO_MAX_LEN, GFP_KERNEL);
+ if (!data->ts_info)
+ goto free_debug_dir;
+
+ /*get some register information */
+ reg_addr = FT_REG_POINT_RATE;
+ ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
+ if (err < 0)
+ dev_err(&client->dev, "report rate read failed");
+
+ dev_info(&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 < 0)
+ dev_err(&client->dev, "threshold read failed");
+
+ dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4);
+
+ reg_addr = FT_REG_FW_VER;
+ err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
+ if (err < 0)
+ dev_err(&client->dev, "version read failed");
+
+ dev_info(&client->dev, "Firmware version = 0x%x\n", reg_value);
+
+ FT_STORE_TS_INFO(data->ts_info, data->family_id, data->pdata->name,
+ data->pdata->num_max_touches, data->pdata->group_id,
+ data->pdata->fw_name, reg_value);
#if defined(CONFIG_FB)
data->fb_notif.notifier_call = fb_notifier_callback;
@@ -1211,6 +1501,10 @@ static int ft5x06_ts_probe(struct i2c_client *client,
return 0;
+free_debug_dir:
+ debugfs_remove_recursive(data->dir);
+free_force_update_fw_sys:
+ device_remove_file(&client->dev, &dev_attr_force_update_fw);
free_update_fw_sys:
device_remove_file(&client->dev, &dev_attr_update_fw);
free_fw_name_sys:
@@ -1238,8 +1532,6 @@ unreg_inputdev:
input_dev = NULL;
free_inputdev:
input_free_device(input_dev);
-free_mem:
- kfree(data);
return err;
}
@@ -1247,6 +1539,7 @@ static int ft5x06_ts_remove(struct i2c_client *client)
{
struct ft5x06_ts_data *data = i2c_get_clientdata(client);
+ debugfs_remove_recursive(data->dir);
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);
@@ -1276,7 +1569,6 @@ static int ft5x06_ts_remove(struct i2c_client *client)
ft5x06_power_init(data, false);
input_unregister_device(data->input_dev);
- kfree(data);
return 0;
}