diff --git a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt index 1c7c2c5ea99f..b5e12a1951dd 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt @@ -22,7 +22,11 @@ Required properties: - focaltech,reset-gpio : reset gpio to control the reset of chip. - focaltech,display-coords : display coordinates in pixels. It is a four tuple consisting of min x, min y, max x and - max y values. + max y values + - focaltech,name : name of the controller + - 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 Optional properties: @@ -32,22 +36,44 @@ Optional properties: - focaltech,i2c-pull-up : to specify pull up is required. - focaltech,no-force-update : to specify force update is allowed. - focaltech,button-map : button map of key codes. The number - of key codes depend on panel. - + of key codes depend on panel + - focaltech,fw-name : specify the firmware file name + - focaltech,fw-delay-aa-ms : specify the "aa" delay in ms for firmware upgrade + - focaltech,fw-delay-55-ms : specify the "55" delay in ms 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 + - focaltech,fw-auto-cal : specify whether calibration is needed after firmware upgrade Example: - i2c@f9924000 { - ft5x06_ts@38 { + i2c@f9923000{ + focaltech@38{ compatible = "focaltech,5x06"; reg = <0x38>; interrupt-parent = <&msmgpio>; - interrupts = <61 0x2>; - vdd-supply = <&pm8941_l22>; - vcc_i2c-supply = <&pm8941_s3>; - focaltech,reset-gpio = <&msmgpio 60 0x00>; - focaltech,irq-gpio = <&msmgpio 61 0x00>; - focaltech,panel-coords = <0 0 480 800>; + interrupts = <1 0x2>; + vdd-supply = <&pm8110_l19>; + vcc_i2c-supply = <&pm8110_l14>; + focaltech,name = "ft6x06"; + focaltech,family-id = <0x06>; + focaltech,reset-gpio = <&msmgpio 0 0x00>; + focaltech,irq-gpio = <&msmgpio 1 0x00>; focaltech,display-coords = <0 0 480 800>; - focaltech,button-map= <158 102 139 217>; - focaltech,family-id = <0x0a>; + focaltech,panel-coords = <0 0 480 800>; + focaltech,button-map= <139 102 158>; + focaltech,no-force-update; + focaltech,i2c-pull-up; + focaltech,group-id = <1>; + focaltech,hard-reset-delay = <20>; + focaltech,soft-reset-delay = <150>; + focaltech,num-max-touches = <2>; + focaltech,fw-name = "ft_8610_qrd_fw.bin"; + focaltech,fw-delay-aa-ms = <100>; + focaltech,fw-delay-55-ms = <30>; + focaltech,fw-upgrade-id1 = <0x79>; + focaltech,fw-upgrade-id2 = <0x08>; + focaltech,fw-delay-readid-ms = <10>; + focaltech,fw-delay-era-flsh-ms = <2000>; + focaltech,fw-auto-cal; }; }; 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 #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #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) -{ - 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); - } - - input_report_key(data->input_dev, BTN_TOUCH, !!fingerdown); - input_sync(data->input_dev); -} - -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) - break; - - event->touch_point++; - - 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; - } - - ft5x06_report_value(data); - - return 0; -} - static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) { struct ft5x06_ts_data *data = dev_id; - int rc; + struct input_dev *ip_dev; + int rc, i; + u32 id, x, y, pressure, status, num_touches; + u8 reg = 0x00, *buf; + bool update_input = false; - rc = ft5x06_handle_touchdata(data); - if (rc) - pr_err("%s: handling touchdata failed\n", __func__); + if (!data) { + pr_err("%s: Invalid data\n", __func__); + return IRQ_HANDLED; + } + + ip_dev = data->input_dev; + buf = data->tch_data; + + 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; + } + + 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; + + update_input = true; + + 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]); + + status = buf[FT_TOUCH_EVENT_POS + FT_ONE_TCH_LEN * i] >> 6; + + num_touches = buf[FT_TD_STATUS] & FT_STATUS_NUM_TP_MASK; + + /* invalid combination */ + if (!num_touches && !status && !id) + break; + + 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, ®); + 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, ®_addr, 1, ®_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, ®_addr, 1, ®_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, ®_addr, 1, ®_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, ®_addr, 1, ®_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, ®_addr, 1, ®_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, ®_addr, 1, ®_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, ®_addr, 1, ®_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; } diff --git a/include/linux/input/ft5x06_ts.h b/include/linux/input/ft5x06_ts.h index 08ccbc9bd71c..89e3143eda77 100644 --- a/include/linux/input/ft5x06_ts.h +++ b/include/linux/input/ft5x06_ts.h @@ -23,7 +23,20 @@ #define FT5X36_ID 0x14 #define FT6X06_ID 0x06 +struct fw_upgrade_info { + bool auto_cal; + u16 delay_aa; + u16 delay_55; + u8 upgrade_id_1; + u8 upgrade_id_2; + u16 delay_readid; + u16 delay_erase_flash; +}; + struct ft5x06_ts_platform_data { + struct fw_upgrade_info info; + const char *name; + const char *fw_name; u32 irqflags; u32 irq_gpio; u32 irq_gpio_flags; @@ -38,6 +51,10 @@ struct ft5x06_ts_platform_data { u32 panel_miny; u32 panel_maxx; u32 panel_maxy; + u32 group_id; + u32 hard_rst_dly; + u32 soft_rst_dly; + u32 num_max_touches; bool no_force_update; bool i2c_pull_up; int (*power_init)(bool);