diff options
author | Simone Willett <swillett@nvidia.com> | 2012-10-22 18:38:18 -0700 |
---|---|---|
committer | Gerrit Code Review <gerrit2@nvidia.com> | 2012-10-22 18:38:18 -0700 |
commit | feee59a76e4796b9365a9a639e28bea2102b75a8 (patch) | |
tree | 89332530461c1e0b9e600ce07729d1784b83452b /drivers/input | |
parent | 8445fbe6f618c8eabe2d066127aca086adb6d863 (diff) | |
parent | 373b012a4013732013dde99281c1678f0f5c4908 (diff) |
Merge "Merge commit 'maxtouch-v3.0-16-Oct-2012' into ToT_2012-10-16_10-30-AM" into android-tegra-nv-3.4
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/touchscreen/atmel_mxt_ts.c | 2558 |
1 files changed, 1613 insertions, 945 deletions
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 93f4f8967601..f29a8b591acb 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3,7 +3,6 @@ * * Copyright (C) 2010 Samsung Electronics Co.Ltd * Copyright (C) 2011 Atmel Corporation - * Copyright (C) 2011-2012 NVIDIA Corporation * Author: Joonyoung Shim <jy0922.shim@samsung.com> * * This program is free software; you can redistribute it and/or modify it @@ -19,31 +18,22 @@ #include <linux/firmware.h> #include <linux/i2c.h> #include <linux/i2c/atmel_mxt_ts.h> -#include <linux/input.h> +#include <linux/input/mt.h> #include <linux/interrupt.h> #include <linux/slab.h> - -#define CREATE_TRACE_POINTS -#include <trace/events/nvevent.h> - -/* Family ID */ -#define MXT224_ID 0x80 -#define MXT768E_ID 0xA1 -#define MXT1386_ID 0xA0 +#ifdef CONFIG_HAS_EARLYSUSPEND +#include <linux/earlysuspend.h> +#endif /* Version */ #define MXT_VER_20 20 #define MXT_VER_21 21 #define MXT_VER_22 22 -/* Slave addresses */ -#define MXT_APP_LOW 0x4a -#define MXT_APP_HIGH 0x4b -#define MXT_BOOT_LOW 0x24 -#define MXT_BOOT_HIGH 0x25 - -/* Firmware */ +/* Firmware files */ #define MXT_FW_NAME "maxtouch.fw" +#define MXT_CFG_NAME "maxtouch.cfg" +#define MXT_CFG_MAGIC "OBP_RAW V1" /* Registers */ #define MXT_FAMILY_ID 0x00 @@ -57,6 +47,8 @@ #define MXT_OBJECT_SIZE 6 +#define MXT_MAX_BLOCK_WRITE 256 + /* Object types */ #define MXT_DEBUG_DIAGNOSTIC_T37 37 #define MXT_GEN_MESSAGE_T5 5 @@ -70,6 +62,7 @@ #define MXT_TOUCH_PROXKEY_T52 52 #define MXT_PROCI_GRIPFACE_T20 20 #define MXT_PROCG_NOISE_T22 22 +#define MXT_PROCI_ACTIVE_STYLUS_T63 63 #define MXT_PROCI_ONETOUCH_T24 24 #define MXT_PROCI_TWOTOUCH_T27 27 #define MXT_PROCI_GRIP_T40 40 @@ -85,6 +78,10 @@ #define MXT_SPT_DIGITIZER_T43 43 #define MXT_SPT_MESSAGECOUNT_T44 44 #define MXT_SPT_CTECONFIG_T46 46 +#define MXT_SPT_NOISESUPPRESSION_T48 48 + +/* MXT_GEN_MESSAGE_T5 object */ +#define MXT_RPTID_NOMSG 0xff /* MXT_GEN_COMMAND_T6 field */ #define MXT_COMMAND_RESET 0 @@ -98,6 +95,9 @@ #define MXT_POWER_ACTVACQINT 1 #define MXT_POWER_ACTV2IDLETO 2 +#define MXT_POWER_CFG_RUN 0 +#define MXT_POWER_CFG_DEEPSLEEP 1 + /* MXT_GEN_ACQUIRE_T8 field */ #define MXT_ACQUIRE_CHRGTIME 0 #define MXT_ACQUIRE_TCHDRIFT 2 @@ -182,36 +182,20 @@ #define MXT_VOLTAGE_DEFAULT 2700000 #define MXT_VOLTAGE_STEP 10000 -/* Defines for Suspend/Resume */ -#define MXT_SUSPEND_STATIC 0 -#define MXT_SUSPEND_DYNAMIC 1 -#define MXT_T7_IDLEACQ_DISABLE 0 -#define MXT_T7_ACTVACQ_DISABLE 0 -#define MXT_T7_ACTV2IDLE_DISABLE 0 -#define MXT_T9_DISABLE 0 -#define MXT_T9_ENABLE 0x83 -#define MXT_T22_DISABLE 0 - /* Define for MXT_GEN_COMMAND_T6 */ #define MXT_BOOT_VALUE 0xa5 +#define MXT_RESET_VALUE 0x01 #define MXT_BACKUP_VALUE 0x55 -#define MXT_BACKUP_TIME 200 /* msec */ -#define MXT224_RESET_TIME 65 /* msec */ -#define MXT768E_RESET_TIME 250 /* msec */ -#define MXT1386_RESET_TIME 200 /* msec */ -#define MXT_RESET_TIME 200 /* msec */ -#define MXT_RESET_NOCHGREAD 400 /* msec */ - -#define MXT_WAKEUP_TIME 25 /* msec */ -#define MXT_FWRESET_TIME 175 /* msec */ +/* Define for MXT_PROCG_NOISESUPPRESSION_T42 */ +#define MXT_T42_MSG_TCHSUP (1 << 0) -/* Defines for MXT_SLOWSCAN_EXTENSIONS */ -#define SLOSCAN_DISABLE 0 /* Disable slow scan */ -#define SLOSCAN_ENABLE 1 /* Enable slow scan */ -#define SLOSCAN_SET_ACTVACQINT 2 /* Set ACTV scan rate */ -#define SLOSCAN_SET_IDLEACQINT 3 /* Set IDLE scan rate */ -#define SLOSCAN_SET_ACTV2IDLETO 4 /* Set the ACTIVE to IDLE TimeOut */ +/* Delay times */ +#define MXT_BACKUP_TIME 25 /* msec */ +#define MXT_RESET_TIME 200 /* msec */ +#define MXT_RESET_NOCHGREAD 400 /* msec */ +#define MXT_FWRESET_TIME 1000 /* msec */ +#define MXT_WAKEUP_TIME 25 /* msec */ /* Command to unlock bootloader */ #define MXT_UNLOCK_CMD_MSB 0xaa @@ -225,32 +209,52 @@ #define MXT_FRAME_CRC_PASS 0x04 #define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ #define MXT_BOOT_STATUS_MASK 0x3f - -/* Touch status */ -#define MXT_SUPPRESS (1 << 1) -#define MXT_AMP (1 << 2) -#define MXT_VECTOR (1 << 3) -#define MXT_MOVE (1 << 4) -#define MXT_RELEASE (1 << 5) -#define MXT_PRESS (1 << 6) -#define MXT_DETECT (1 << 7) +#define MXT_BOOT_EXTENDED_ID (1 << 5) +#define MXT_BOOT_ID_MASK 0x1f + +/* Define for T6 status byte */ +#define MXT_STATUS_RESET (1 << 7) +#define MXT_STATUS_OFL (1 << 6) +#define MXT_STATUS_SIGERR (1 << 5) +#define MXT_STATUS_CAL (1 << 4) +#define MXT_STATUS_CFGERR (1 << 3) +#define MXT_STATUS_COMSERR (1 << 2) + +/* T9 Touch status */ +#define MXT_T9_UNGRIP (1 << 0) +#define MXT_T9_SUPPRESS (1 << 1) +#define MXT_T9_AMP (1 << 2) +#define MXT_T9_VECTOR (1 << 3) +#define MXT_T9_MOVE (1 << 4) +#define MXT_T9_RELEASE (1 << 5) +#define MXT_T9_PRESS (1 << 6) +#define MXT_T9_DETECT (1 << 7) /* Touch orient bits */ #define MXT_XY_SWITCH (1 << 0) #define MXT_X_INVERT (1 << 1) #define MXT_Y_INVERT (1 << 2) -/* Touchscreen absolute values */ -#define MXT_MAX_AREA 0xff +/* T63 Stylus */ +#define MXT_STYLUS_PRESS (1 << 0) +#define MXT_STYLUS_RELEASE (1 << 1) +#define MXT_STYLUS_MOVE (1 << 2) +#define MXT_STYLUS_SUPPRESS (1 << 3) -/* Fixed Report ID values */ -#define MXT_RPTID_NOMSG 0xFF /* No messages available to read */ +#define MXT_STYLUS_DETECT (1 << 4) +#define MXT_STYLUS_TIP (1 << 5) +#define MXT_STYLUS_ERASER (1 << 6) +#define MXT_STYLUS_BARREL (1 << 7) -#define MXT_MAX_FINGER 10 +#define MXT_STYLUS_PRESSURE_MASK 0x3F -#define RESUME_READS 100 +/* T15 Key array */ +int mxt_t15_keys[] = { }; -#define MXT_DEFAULT_PRESSURE 100 +static unsigned long mxt_t15_keystatus; + +/* Touchscreen absolute values */ +#define MXT_MAX_AREA 0xff struct mxt_info { u8 family_id; @@ -262,6 +266,11 @@ struct mxt_info { u8 object_num; }; +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mxt_early_suspend(struct early_suspend *es); +static void mxt_late_resume(struct early_suspend *es); +#endif + struct mxt_object { u8 type; u16 start_address; @@ -274,292 +283,359 @@ struct mxt_object { u8 max_reportid; }; -struct mxt_message { - u8 reportid; - u8 message[7]; - u8 checksum; -}; - -struct mxt_finger { - int status; - int x; - int y; - int area; - int pressure; -}; - -/* This structure is used to save/restore values during suspend/resume */ -struct mxt_suspend { - u8 suspend_obj; - u8 suspend_reg; - u8 suspend_val; - u8 suspend_flags; - u8 restore_val; -}; +enum mxt_device_state { INIT, APPMODE, BOOTLOADER, FAILED, SHUTDOWN }; /* Each client has this additional data */ struct mxt_data { struct i2c_client *client; struct input_dev *input_dev; const struct mxt_platform_data *pdata; + enum mxt_device_state state; struct mxt_object *object_table; + u16 mem_size; struct mxt_info info; - struct mxt_finger finger[MXT_MAX_FINGER]; unsigned int irq; unsigned int max_x; unsigned int max_y; - u8(*read_chg) (void); - u16 msg_address; - u16 last_address; + struct bin_attribute mem_access_attr; + bool debug_enabled; + bool driver_paused; + u8 bootloader_addr; u8 actv_cycle_time; u8 idle_cycle_time; - u8 actv2idle_timeout; u8 is_stopped; - struct mutex access_mutex; - unsigned int driver_paused; - struct bin_attribute mem_access_attr; - int debug_enabled; - int slowscan_enabled; - u8 slowscan_actv_cycle_time; - u8 slowscan_idle_cycle_time; - u8 slowscan_actv2idle_timeout; - u8 slowscan_shad_actv_cycle_time; - u8 slowscan_shad_idle_cycle_time; - u8 slowscan_shad_actv2idle_timeout; + u8 max_reportid; + u32 config_crc; + u32 info_block_crc; + u8 num_touchids; + u8 num_stylusids; + u8 *msg_buf; + u8 last_message_count; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + + /* Cached parameters from object table */ + u16 T5_address; + u8 T5_msg_size; + u8 T6_reportid; + u16 T7_address; + u8 T9_reportid_min; + u8 T9_reportid_max; + u8 T15_reportid_min; + u8 T15_reportid_max; + u8 T42_reportid_min; + u8 T42_reportid_max; + u16 T44_address; + u8 T48_reportid; + u8 T63_reportid_min; + u8 T63_reportid_max; }; -static struct mxt_suspend mxt_save[] = { - {MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, MXT_T9_DISABLE, MXT_SUSPEND_DYNAMIC, 0}, - {MXT_PROCG_NOISE_T22, MXT_NOISE_CTRL, MXT_T22_DISABLE, MXT_SUSPEND_DYNAMIC, 0}, - {MXT_GEN_POWER_T7, MXT_POWER_IDLEACQINT, MXT_T7_IDLEACQ_DISABLE, MXT_SUSPEND_DYNAMIC, 0}, - {MXT_GEN_POWER_T7, MXT_POWER_ACTVACQINT, MXT_T7_ACTVACQ_DISABLE, MXT_SUSPEND_DYNAMIC, 0}, - {MXT_GEN_POWER_T7, MXT_POWER_ACTV2IDLETO, MXT_T7_ACTV2IDLE_DISABLE, MXT_SUSPEND_DYNAMIC, 0} +/* I2C slave address pairs */ +struct mxt_i2c_address_pair { + u8 bootloader; + u8 application; }; -static bool mxt_object_readable(unsigned int type) -{ - switch (type) { - case MXT_GEN_MESSAGE_T5: - case MXT_GEN_COMMAND_T6: - case MXT_GEN_POWER_T7: - case MXT_GEN_ACQUIRE_T8: - case MXT_GEN_DATASOURCE_T53: - case MXT_TOUCH_MULTI_T9: - case MXT_TOUCH_KEYARRAY_T15: - case MXT_TOUCH_PROXIMITY_T23: - case MXT_TOUCH_PROXKEY_T52: - case MXT_PROCI_GRIPFACE_T20: - case MXT_PROCG_NOISE_T22: - case MXT_PROCI_ONETOUCH_T24: - case MXT_PROCI_TWOTOUCH_T27: - case MXT_PROCI_GRIP_T40: - case MXT_PROCI_PALM_T41: - case MXT_PROCI_TOUCHSUPPRESSION_T42: - case MXT_PROCI_STYLUS_T47: - case MXT_PROCG_NOISESUPPRESSION_T48: - case MXT_SPT_COMMSCONFIG_T18: - case MXT_SPT_GPIOPWM_T19: - case MXT_SPT_SELFTEST_T25: - case MXT_SPT_CTECONFIG_T28: - case MXT_SPT_USERDATA_T38: - case MXT_SPT_DIGITIZER_T43: - case MXT_SPT_CTECONFIG_T46: - return true; - default: - return false; - } -} - -static bool mxt_object_writable(unsigned int type) -{ - switch (type) { - case MXT_GEN_COMMAND_T6: - case MXT_GEN_POWER_T7: - case MXT_GEN_ACQUIRE_T8: - case MXT_TOUCH_MULTI_T9: - case MXT_TOUCH_KEYARRAY_T15: - case MXT_TOUCH_PROXIMITY_T23: - case MXT_TOUCH_PROXKEY_T52: - case MXT_PROCI_GRIPFACE_T20: - case MXT_PROCG_NOISE_T22: - case MXT_PROCI_ONETOUCH_T24: - case MXT_PROCI_TWOTOUCH_T27: - case MXT_PROCI_GRIP_T40: - case MXT_PROCI_PALM_T41: - case MXT_PROCI_TOUCHSUPPRESSION_T42: - case MXT_PROCI_STYLUS_T47: - case MXT_PROCG_NOISESUPPRESSION_T48: - case MXT_SPT_COMMSCONFIG_T18: - case MXT_SPT_GPIOPWM_T19: - case MXT_SPT_SELFTEST_T25: - case MXT_SPT_CTECONFIG_T28: - case MXT_SPT_DIGITIZER_T43: - case MXT_SPT_CTECONFIG_T46: - return true; - default: - return false; +static const struct mxt_i2c_address_pair mxt_i2c_addresses[] = { +#ifdef BOOTLOADER_1664_1188 + { 0x26, 0x4a }, + { 0x27, 0x4b }, +#else + { 0x24, 0x4a }, + { 0x25, 0x4b }, + { 0x26, 0x4c }, + { 0x27, 0x4d }, + { 0x34, 0x5a }, + { 0x35, 0x5b }, +#endif +}; + +static int mxt_bootloader_read(struct mxt_data *data, u8 *val, unsigned int count) +{ + int ret; + struct i2c_msg msg; + + msg.addr = data->bootloader_addr; + msg.flags = data->client->flags & I2C_M_TEN; + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = val; + + ret = i2c_transfer(data->client->adapter, &msg, 1); + + return (ret == 1) ? 0 : ret; +} + +static int mxt_bootloader_write(struct mxt_data *data, const u8 * const val, + unsigned int count) +{ + int ret; + struct i2c_msg msg; + + msg.addr = data->bootloader_addr; + msg.flags = data->client->flags & I2C_M_TEN; + msg.len = count; + msg.buf = (u8 *)val; + + ret = i2c_transfer(data->client->adapter, &msg, 1); + + return (ret == 1) ? 0 : ret; +} + +static int mxt_get_bootloader_address(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int i; + + for (i = 0; i < ARRAY_SIZE(mxt_i2c_addresses); i++) { + if (mxt_i2c_addresses[i].application == client->addr) { + data->bootloader_addr = mxt_i2c_addresses[i].bootloader; + + dev_info(&client->dev, "Bootloader i2c addr: 0x%02x\n", + data->bootloader_addr); + + return 0; + } } + + dev_err(&client->dev, "Address 0x%02x not found in address table\n", + client->addr); + return -EINVAL; } -static void mxt_dump_message(struct device *dev, - struct mxt_message *message) +static int mxt_probe_bootloader(struct mxt_data *data) { - dev_dbg(dev, "reportid:\t0x%x\n", message->reportid); - dev_dbg(dev, "message1:\t0x%x\n", message->message[0]); - dev_dbg(dev, "message2:\t0x%x\n", message->message[1]); - dev_dbg(dev, "message3:\t0x%x\n", message->message[2]); - dev_dbg(dev, "message4:\t0x%x\n", message->message[3]); - dev_dbg(dev, "message5:\t0x%x\n", message->message[4]); - dev_dbg(dev, "message6:\t0x%x\n", message->message[5]); - dev_dbg(dev, "message7:\t0x%x\n", message->message[6]); - dev_dbg(dev, "checksum:\t0x%x\n", message->checksum); + struct device *dev = &data->client->dev; + int ret; + u8 val; + bool crc_failure; + + ret = mxt_get_bootloader_address(data); + if (ret) + return ret; + + ret = mxt_bootloader_read(data, &val, 1); + if (ret) { + dev_err(dev, "%s: i2c recv failed\n", __func__); + return -EIO; + } + + /* Check app crc fail mode */ + crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL; + + dev_err(dev, "Detected bootloader, status:%02X%s\n", + val, crc_failure ? ", APP_CRC_FAIL" : ""); + + return 0; } -static int mxt_check_bootloader(struct i2c_client *client, - unsigned int state) +static int mxt_wait_for_chg(struct mxt_data *data) { + int timeout_counter = 0; + int count = 1E6; + + if (data->pdata->read_chg == NULL) { + msleep(10); + return 0; + } + + while ((timeout_counter++ <= count) && data->pdata->read_chg()) + udelay(20); + + if (timeout_counter > count) { + dev_err(&data->client->dev, "mxt_wait_for_chg() timeout!\n"); + return -EIO; + } + + return 0; +} + +static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) +{ + struct device *dev = &data->client->dev; + u8 buf[3]; + + if (val & MXT_BOOT_EXTENDED_ID) { + if (mxt_bootloader_read(data, &buf[0], 3) != 0) { + dev_err(dev, "%s: i2c failure\n", __func__); + return -EIO; + } + + dev_info(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]); + + return buf[0]; + } else { + dev_info(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK); + + return val; + } +} + +static int mxt_check_bootloader(struct mxt_data *data, + unsigned int state) +{ + struct device *dev = &data->client->dev; + int ret; u8 val; recheck: - if (i2c_master_recv(client, &val, 1) != 1) { - dev_err(&client->dev, "%s: i2c recv failed\n", __func__); - return -EIO; + ret = mxt_bootloader_read(data, &val, 1); + if (ret) { + dev_err(dev, "%s: i2c recv failed, ret=%d\n", + __func__, ret); + return ret; + } + + if (state == MXT_WAITING_BOOTLOAD_CMD) { + val = mxt_get_bootloader_version(data, val); } switch (state) { case MXT_WAITING_BOOTLOAD_CMD: + val &= ~MXT_BOOT_STATUS_MASK; + break; case MXT_WAITING_FRAME_DATA: + case MXT_APP_CRC_FAIL: val &= ~MXT_BOOT_STATUS_MASK; break; case MXT_FRAME_CRC_PASS: - if (val == MXT_FRAME_CRC_CHECK) + if (val == MXT_FRAME_CRC_CHECK) { + mxt_wait_for_chg(data); goto recheck; + } else if (val == MXT_FRAME_CRC_FAIL) { + dev_err(dev, "Bootloader CRC fail\n"); + return -EINVAL; + } break; default: return -EINVAL; } if (val != state) { - dev_err(&client->dev, "Unvalid bootloader mode state\n"); + dev_err(dev, "Invalid bootloader mode state 0x%02X\n", val); return -EINVAL; } return 0; } -static int mxt_unlock_bootloader(struct i2c_client *client) +static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock) { + int ret; u8 buf[2]; - buf[0] = MXT_UNLOCK_CMD_LSB; - buf[1] = MXT_UNLOCK_CMD_MSB; - - if (i2c_master_send(client, buf, 2) != 2) { - dev_err(&client->dev, "%s: i2c send failed\n", __func__); - return -EIO; + if (unlock) { + buf[0] = MXT_UNLOCK_CMD_LSB; + buf[1] = MXT_UNLOCK_CMD_MSB; + } else { + buf[0] = 0x01; + buf[1] = 0x01; } - return 0; -} - -static int mxt_fw_write(struct i2c_client *client, - const u8 *data, unsigned int frame_size) -{ - if (i2c_master_send(client, data, frame_size) != frame_size) { - dev_err(&client->dev, "%s: i2c send failed\n", __func__); - return -EIO; + ret = mxt_bootloader_write(data, buf, 2); + if (ret) { + dev_err(&data->client->dev, "%s: i2c send failed, ret=%d\n", + __func__, ret); + return ret; } return 0; } -static int __mxt_read_reg(struct i2c_client *client, - u16 reg, u16 len, void *val) +static int mxt_read_reg(struct i2c_client *client, + u16 reg, u16 len, void *val) { + struct device *dev = &client->dev; + struct i2c_msg xfer[2]; u8 buf[2]; - int retval = 0; - struct mxt_data *data = i2c_get_clientdata(client); + int ret; + u8 retry = 0; buf[0] = reg & 0xff; buf[1] = (reg >> 8) & 0xff; - mutex_lock(&data->access_mutex); - - if ((data->last_address != reg) || (reg != data->msg_address)) { - if (i2c_master_send(client, (u8 *)buf, 2) != 2) { - dev_dbg(&client->dev, "i2c retry\n"); + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = buf; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = len; + xfer[1].buf = val; + +retry_read: + ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer)); + if (ret != ARRAY_SIZE(xfer)) { + if (!retry) { + dev_dbg(dev, "%s: i2c retry\n", __func__); msleep(MXT_WAKEUP_TIME); - - if (i2c_master_send(client, (u8 *)buf, 2) != 2) { - dev_err(&client->dev, "%s: i2c send failed\n", - __func__); - retval = -EIO; - goto mxt_read_exit; - } - } - } - - if (i2c_master_recv(client, (u8 *)val, len) != len) { - dev_dbg(&client->dev, "i2c retry\n"); - msleep(MXT_WAKEUP_TIME); - - if (i2c_master_recv(client, (u8 *)val, len) != len) { - dev_err(&client->dev, "%s: i2c recv failed\n", - __func__); - retval = -EIO; - goto mxt_read_exit; + retry = 1; + goto retry_read; + } else { + dev_err(dev, "%s: i2c transfer failed (%d)\n", + __func__, ret); + return -EIO; } } - data->last_address = reg; - -mxt_read_exit: - mutex_unlock(&data->access_mutex); - return retval; -} - -static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val) -{ - return __mxt_read_reg(client, reg, 1, val); + return 0; } static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val) { + struct device *dev = &client->dev; + u8 retry = 0; u8 buf[3]; - int retval = 0; - struct mxt_data *data = i2c_get_clientdata(client); buf[0] = reg & 0xff; buf[1] = (reg >> 8) & 0xff; buf[2] = val; - mutex_lock(&data->access_mutex); +retry_write: if (i2c_master_send(client, buf, 3) != 3) { - dev_dbg(&client->dev, "i2c retry\n"); - msleep(MXT_WAKEUP_TIME); - - if (i2c_master_send(client, buf, 3) != 3) { - dev_err(&client->dev, "%s: i2c send failed\n", __func__); - retval = -EIO; - goto mxt_write_exit; + if (!retry) { + dev_dbg(dev, "%s: i2c retry\n", __func__); + msleep(MXT_WAKEUP_TIME); + retry = 1; + goto retry_write; + } else { + dev_err(dev, "%s: i2c send failed\n", __func__); + return -EIO; } } - data->last_address = reg + 1; -mxt_write_exit: - mutex_unlock(&data->access_mutex); - return retval; + return 0; } -static int mxt_read_object_table(struct i2c_client *client, - u16 reg, u8 *object_buf) +int mxt_write_block(struct i2c_client *client, u16 addr, u16 length, u8 *value) { - return __mxt_read_reg(client, reg, MXT_OBJECT_SIZE, - object_buf); + int i; + struct { + __le16 le_addr; + u8 data[MXT_MAX_BLOCK_WRITE]; + } i2c_block_transfer; + + if (length > MXT_MAX_BLOCK_WRITE) + return -EINVAL; + + memcpy(i2c_block_transfer.data, value, length); + + i2c_block_transfer.le_addr = cpu_to_le16(addr); + + i = i2c_master_send(client, (u8 *) &i2c_block_transfer, length + 2); + + if (i == (length + 2)) + return 0; + else + return -EIO; } -static struct mxt_object * -mxt_get_object(struct mxt_data *data, u8 type) +static struct mxt_object *mxt_get_object(struct mxt_data *data, u8 type) { struct mxt_object *object; int i; @@ -570,25 +646,10 @@ mxt_get_object(struct mxt_data *data, u8 type) return object; } - dev_err(&data->client->dev, "Invalid object type T%d\n", type); + dev_err(&data->client->dev, "Invalid object type T%u\n", type); return NULL; } -static int mxt_read_message(struct mxt_data *data, - struct mxt_message *message) -{ - struct mxt_object *object; - u16 reg; - - object = mxt_get_object(data, MXT_GEN_MESSAGE_T5); - if (!object) - return -EINVAL; - - reg = object->start_address; - return __mxt_read_reg(data->client, reg, - sizeof(struct mxt_message), message); -} - static int mxt_read_object(struct mxt_data *data, u8 type, u8 offset, u8 *val) { @@ -600,7 +661,7 @@ static int mxt_read_object(struct mxt_data *data, return -EINVAL; reg = object->start_address; - return __mxt_read_reg(data->client, reg + offset, 1, val); + return mxt_read_reg(data->client, reg + offset, 1, val); } static int mxt_write_object(struct mxt_data *data, @@ -610,328 +671,735 @@ static int mxt_write_object(struct mxt_data *data, u16 reg; object = mxt_get_object(data, type); - if (!object) + if (!object || offset >= object->size) return -EINVAL; + if (offset >= object->size * object->instances) { + dev_err(&data->client->dev, "Tried to write outside object T%d" + " offset:%d, size:%d\n", type, offset, object->size); + return -EINVAL; + } + reg = object->start_address; return mxt_write_reg(data->client, reg + offset, val); } -static void mxt_input_report(struct mxt_data *data, int single_id) +static int mxt_soft_reset(struct mxt_data *data, u8 value) { - struct mxt_finger *finger = data->finger; - struct input_dev *input_dev = data->input_dev; - int status = finger[single_id].status; - int finger_num = 0; - int id; + int timeout_counter = 0; + struct device *dev = &data->client->dev; - for (id = 0; id < MXT_MAX_FINGER; id++) { - if (!finger[id].status) - continue; + dev_info(dev, "Resetting chip\n"); - input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, - finger[id].status != MXT_RELEASE ? - finger[id].area : 0); - input_report_abs(input_dev, ABS_MT_POSITION_X, - finger[id].x); - input_report_abs(input_dev, ABS_MT_POSITION_Y, - finger[id].y); - input_report_abs(input_dev, ABS_MT_PRESSURE, - finger[id].pressure); - input_mt_sync(input_dev); + mxt_write_object(data, MXT_GEN_COMMAND_T6, + MXT_COMMAND_RESET, value); - if (finger[id].status == MXT_RELEASE) - finger[id].status = 0; - else - finger_num++; + if (data->pdata->read_chg == NULL) { + msleep(MXT_RESET_NOCHGREAD); + } else { + msleep(MXT_RESET_TIME); + + timeout_counter = 0; + while ((timeout_counter++ <= 100) && data->pdata->read_chg()) + msleep(20); + if (timeout_counter > 100) { + dev_err(dev, "No response after reset!\n"); + return -EIO; + } } - input_report_key(input_dev, BTN_TOUCH, finger_num > 0); + return 0; +} - if (status != MXT_RELEASE) { - input_report_abs(input_dev, ABS_X, finger[single_id].x); - input_report_abs(input_dev, ABS_Y, finger[single_id].y); - input_report_abs(input_dev, ABS_PRESSURE, finger[single_id].pressure); +static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u32 crc; + u8 status = msg[1]; + + crc = msg[2] | (msg[3] << 8) | (msg[4] << 16); + + if (crc != data->config_crc) { + data->config_crc = crc; + dev_dbg(dev, "T6 cfg crc 0x%06X\n", crc); } - input_sync(input_dev); + if (status) + dev_dbg(dev, "T6 status %s%s%s%s%s%s\n", + (status & MXT_STATUS_RESET) ? "RESET " : "", + (status & MXT_STATUS_OFL) ? "OFL " : "", + (status & MXT_STATUS_SIGERR) ? "SIGERR " : "", + (status & MXT_STATUS_CAL) ? "CAL " : "", + (status & MXT_STATUS_CFGERR) ? "CFGERR " : "", + (status & MXT_STATUS_COMSERR) ? "COMSERR " : ""); +} + +static void mxt_input_sync(struct mxt_data *data) +{ + input_mt_report_pointer_emulation(data->input_dev, false); + input_sync(data->input_dev); } -static void mxt_input_touchevent(struct mxt_data *data, - struct mxt_message *message, int id) +static void mxt_proc_t9_messages(struct mxt_data *data, u8 *message) { - struct mxt_finger *finger = data->finger; struct device *dev = &data->client->dev; - u8 status = message->message[0]; + struct input_dev *input_dev = data->input_dev; + u8 status; int x; int y; int area; - int pressure; + int amplitude; + u8 vector; + int id; + + if (!input_dev || data->driver_paused) + return; - /* Check the touch is present on the screen */ - if (!(status & MXT_DETECT)) { - if (status & MXT_RELEASE) { - dev_dbg(dev, "[%d] released\n", id); + id = message[0] - data->T9_reportid_min; - finger[id].status = MXT_RELEASE; - finger[id].pressure = 0; - mxt_input_report(data, id); - } + if (id < 0 || id > data->num_touchids) { + dev_err(dev, "invalid touch id %d, total num touch is %d\n", + id, data->num_touchids); return; } - /* Check only AMP detection */ - if (!(status & (MXT_PRESS | MXT_MOVE))) - return; + status = message[1]; - x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf); - y = (message->message[2] << 4) | ((message->message[3] & 0xf)); + x = (message[2] << 4) | ((message[4] >> 4) & 0xf); + y = (message[3] << 4) | ((message[4] & 0xf)); if (data->max_x < 1024) - x = x >> 2; + x >>= 2; if (data->max_y < 1024) - y = y >> 2; + y >>= 2; + area = message[5]; + amplitude = message[6]; + vector = message[7]; + + dev_dbg(dev, + "[%d] %c%c%c%c%c%c%c%c x: %d y: %d area: %d amp: %d vector: %02X\n", + id, + (status & MXT_T9_DETECT) ? 'D' : '.', + (status & MXT_T9_PRESS) ? 'P' : '.', + (status & MXT_T9_RELEASE) ? 'R' : '.', + (status & MXT_T9_MOVE) ? 'M' : '.', + (status & MXT_T9_VECTOR) ? 'V' : '.', + (status & MXT_T9_AMP) ? 'A' : '.', + (status & MXT_T9_SUPPRESS) ? 'S' : '.', + (status & MXT_T9_UNGRIP) ? 'U' : '.', + x, y, area, amplitude, vector); + + input_mt_slot(input_dev, id); + + if ((status & MXT_T9_DETECT) && (status & MXT_T9_RELEASE)) { + /* Touch in detect, just after being released, so + * get new touch tracking ID */ + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + mxt_input_sync(data); + } - area = message->message[4]; - pressure = message->message[5]; + if (status & MXT_T9_DETECT) { + /* Touch in detect, report X/Y position */ + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1); - if ((pressure <= 0) || (pressure > 255)) - pressure = MXT_DEFAULT_PRESSURE; + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area); + input_report_abs(input_dev, ABS_MT_ORIENTATION, vector); + } else { + /* Touch no longer in detect, so close out slot */ + mxt_input_sync(data); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + } +} - dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id, - status & MXT_MOVE ? "moved" : "pressed", - x, y, area); +static void mxt_proc_t15_messages(struct mxt_data *data, u8 *msg) +{ + struct input_dev *input_dev = data->input_dev; + struct device *dev = &data->client->dev; + u8 key; + bool curr_state, new_state; + bool sync = false; + unsigned long keystates = le32_to_cpu(msg[2]); + + for (key = 0; key < ARRAY_SIZE(mxt_t15_keys); key++) { + curr_state = test_bit(key, &mxt_t15_keystatus); + new_state = test_bit(key, &keystates); + + if (!curr_state && new_state) { + dev_dbg(dev, "T15 key press: %u\n", key); + __set_bit(key, &mxt_t15_keystatus); + input_event(input_dev, EV_KEY, mxt_t15_keys[key], 1); + sync = true; + } else if (curr_state && !new_state) { + dev_dbg(dev, "T15 key release: %u\n", key); + __clear_bit(key, &mxt_t15_keystatus); + input_event(input_dev, EV_KEY, mxt_t15_keys[key], 0); + sync = true; + } + } - finger[id].status = status & MXT_MOVE ? - MXT_MOVE : MXT_PRESS; - finger[id].x = x; - finger[id].y = y; - finger[id].area = area; - finger[id].pressure = pressure; + if (sync) + input_sync(input_dev); +} - trace_nvevent_irq_data_submit("mxt_input_touchevent"); - mxt_input_report(data, id); +static void mxt_proc_t42_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u8 status = msg[1]; + + if (status & MXT_T42_MSG_TCHSUP) + dev_info(dev, "T42 suppress\n"); + else + dev_info(dev, "T42 normal\n"); } -static irqreturn_t mxt_interrupt(int irq, void *dev_id) +static int mxt_proc_t48_messages(struct mxt_data *data, u8 *msg) { - struct mxt_data *data = dev_id; - struct mxt_message message; - struct mxt_object *touch_object; struct device *dev = &data->client->dev; - int touchid; - u8 reportid; + u8 status, state; - trace_nvevent_irq_data_read_start_series("mxt_input_interrupt"); - do { - trace_nvevent_irq_data_read_start_single("mxt_input_interrupt"); - if (mxt_read_message(data, &message)) { - dev_err(dev, "Failed to read message\n"); - goto end; - } - trace_nvevent_irq_data_read_finish_single( - "mxt_input_interrupt"); + status = msg[1]; + state = msg[4]; - reportid = message.reportid; + dev_dbg(dev, "T48 state %d status %02X %s%s%s%s%s\n", + state, + status, + (status & 0x01) ? "FREQCHG " : "", + (status & 0x02) ? "APXCHG " : "", + (status & 0x04) ? "ALGOERR " : "", + (status & 0x10) ? "STATCHG " : "", + (status & 0x20) ? "NLVLCHG " : ""); - touch_object = mxt_get_object(data, MXT_TOUCH_MULTI_T9); - if (!touch_object) - goto end; + return 0; +} - if (data->debug_enabled) - print_hex_dump(KERN_DEBUG, "MXT MSG:", DUMP_PREFIX_NONE, - 16, 1, &message, sizeof(struct mxt_message), false); +static void mxt_proc_t63_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev = data->input_dev; + u8 id; + u16 x, y; + u8 pressure; + + if (!input_dev) + return; - if (reportid >= touch_object->min_reportid - && reportid <= touch_object->max_reportid) { - touchid = reportid - touch_object->min_reportid; - mxt_input_touchevent(data, &message, touchid); - } else if (reportid != MXT_RPTID_NOMSG) - mxt_dump_message(dev, &message); - } while (reportid != MXT_RPTID_NOMSG); - trace_nvevent_irq_data_read_finish_series("mxt_input_interrupt"); + /* stylus slots come after touch slots */ + id = data->num_touchids + (msg[0] - data->T63_reportid_min); -end: - return IRQ_HANDLED; + if (id < 0 || id > (data->num_touchids + data->num_stylusids)) { + dev_err(dev, "invalid stylus id %d, max slot is %d\n", + id, data->num_stylusids); + return; + } + + x = msg[3] | (msg[4] << 8); + y = msg[5] | (msg[6] << 8); + pressure = msg[7] & MXT_STYLUS_PRESSURE_MASK; + + dev_dbg(dev, + "[%d] %c%c%c%c x: %d y: %d pressure: %d stylus:%c%c%c%c\n", + id, + (msg[1] & MXT_STYLUS_SUPPRESS) ? 'S' : '.', + (msg[1] & MXT_STYLUS_MOVE) ? 'M' : '.', + (msg[1] & MXT_STYLUS_RELEASE) ? 'R' : '.', + (msg[1] & MXT_STYLUS_PRESS) ? 'P' : '.', + x, y, pressure, + (msg[2] & MXT_STYLUS_BARREL) ? 'B' : '.', + (msg[2] & MXT_STYLUS_ERASER) ? 'E' : '.', + (msg[2] & MXT_STYLUS_TIP) ? 'T' : '.', + (msg[2] & MXT_STYLUS_DETECT) ? 'D' : '.'); + + input_mt_slot(input_dev, id); + + if (msg[2] & MXT_STYLUS_DETECT) { + input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); + } else { + input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 0); + } + + input_report_key(input_dev, BTN_STYLUS, (msg[2] & MXT_STYLUS_ERASER)); + input_report_key(input_dev, BTN_STYLUS2, (msg[2] & MXT_STYLUS_BARREL)); + + mxt_input_sync(data); } -static int mxt_make_highchg(struct mxt_data *data) +static int mxt_proc_message(struct mxt_data *data, u8 *msg) +{ + u8 report_id = msg[0]; + + if (report_id == MXT_RPTID_NOMSG) + return -1; + + if (data->debug_enabled) + print_hex_dump(KERN_DEBUG, "MXT MSG:", DUMP_PREFIX_NONE, 16, 1, + msg, data->T5_msg_size, false); + + if (report_id >= data->T9_reportid_min + && report_id <= data->T9_reportid_max) { + mxt_proc_t9_messages(data, msg); + } else if (report_id >= data->T63_reportid_min + && report_id <= data->T63_reportid_max) { + mxt_proc_t63_messages(data, msg); + } else if (report_id >= data->T15_reportid_min + && report_id <= data->T15_reportid_max) { + mxt_proc_t15_messages(data, msg); + } else if (report_id == data->T6_reportid) { + mxt_proc_t6_messages(data, msg); + } else if (report_id == data->T48_reportid) { + mxt_proc_t48_messages(data, msg); + } else if (report_id >= data->T42_reportid_min + && report_id <= data->T42_reportid_max) { + mxt_proc_t42_messages(data, msg); + } + + return 0; +} + +static int mxt_read_count_messages(struct mxt_data *data, u8 count) { struct device *dev = &data->client->dev; - struct mxt_message message; - int count = 30; - int error; + int ret; + int i; + u8 num_valid = 0; - /* Read dummy message to make high CHG pin */ - do { - error = mxt_read_message(data, &message); - if (error) - return error; - } while (message.reportid != MXT_RPTID_NOMSG && --count); + /* Safety check for msg_buf */ + if (count > data->max_reportid) + return -EINVAL; + + /* Process remaining messages if necessary */ + ret = mxt_read_reg(data->client, data->T5_address, + data->T5_msg_size * count, data->msg_buf); + if (ret) { + dev_err(dev, "Failed to read %u messages (%d)\n", count, ret); + return ret; + } - if (!count) { - dev_err(dev, "CHG pin isn't cleared\n"); - return -EBUSY; + for (i = 0; i < count; i++) { + ret = mxt_proc_message(data, + data->msg_buf + data->T5_msg_size * i); + + if (ret == 0) + num_valid++; } - return 0; + /* return number of messages read */ + return num_valid; } -static int mxt_check_reg_init(struct mxt_data *data) +static irqreturn_t mxt_read_messages_t44(struct mxt_data *data) { - struct i2c_client *client = data->client; - const struct mxt_platform_data *pdata = data->pdata; - struct mxt_object *object; - struct mxt_message message; struct device *dev = &data->client->dev; - int index = 0; - int timeout_counter = 0; - int i, j, config_offset; - int error; - unsigned long current_crc; - u8 command_register; + int ret; + u8 count, num_left; - if (!pdata->config) { - dev_dbg(dev, "No cfg data defined, skipping reg init\n"); - return 0; + /* Read T44 and T5 together */ + ret = mxt_read_reg(data->client, data->T44_address, + data->T5_msg_size + 1, data->msg_buf); + if (ret) { + dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret); + return IRQ_NONE; } - /* Try to read the config checksum of the existing cfg */ + count = data->msg_buf[0]; + + if (count == 0) { + dev_warn(dev, "Interrupt triggered but zero messages\n"); + return IRQ_NONE; + } else if (count > data->max_reportid) { + dev_err(dev, "T44 count exceeded max report id\n"); + count = data->max_reportid; + } + + /* Process first message */ + ret = mxt_proc_message(data, data->msg_buf + 1); + if (ret < 0) { + dev_warn(dev, "Unexpected invalid message\n"); + return IRQ_NONE; + } + + num_left = count - 1; + + /* Process remaining messages if necessary */ + if (num_left) { + ret = mxt_read_count_messages(data, num_left); + if (ret < 0) { + mxt_input_sync(data); + return IRQ_NONE; + } else if (ret != num_left) { + dev_warn(dev, "Unexpected invalid message\n"); + } + } + + mxt_input_sync(data); + + return IRQ_HANDLED; +} + +static int mxt_read_t9_messages_until_invalid(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int count, read; + u8 tries = 2; + + count = data->max_reportid; + + /* Read messages until we force an invalid */ + do { + read = mxt_read_count_messages(data, count); + if (read < count) + return 0; + } while (--tries); + + dev_err(dev, "CHG pin isn't cleared\n"); + return -EBUSY; +} + +static irqreturn_t mxt_read_t9_messages(struct mxt_data *data) +{ + int total_handled, num_handled; + u8 count = data->last_message_count; + + if (count < 1 || count > data->max_reportid) + count = 1; + + /* include final invalid message */ + total_handled = mxt_read_count_messages(data, count + 1); + if (total_handled < 0) + return IRQ_NONE; + /* if there were invalid messages, then we are done */ + else if (total_handled <= count) + goto update_count; + + /* read two at a time until an invalid message or else we reach + * reportid limit */ + do { + num_handled = mxt_read_count_messages(data, 2); + if (num_handled < 0) + return IRQ_NONE; + + total_handled += num_handled; + + if (num_handled < 2) + break; + } while (total_handled < data->num_touchids); + +update_count: + data->last_message_count = total_handled; + mxt_input_sync(data); + return IRQ_HANDLED; +} + +static irqreturn_t mxt_interrupt(int irq, void *dev_id) +{ + struct mxt_data *data = dev_id; + + if (data->T44_address) + return mxt_read_messages_t44(data); + else + return mxt_read_t9_messages(data); +} + +static void mxt_read_current_crc(struct mxt_data *data) +{ + /* CRC has already been read */ + if (data->config_crc > 0) + return; + mxt_write_object(data, MXT_GEN_COMMAND_T6, - MXT_COMMAND_REPORTALL, 1); + MXT_COMMAND_REPORTALL, 1); + msleep(30); - error = mxt_read_message(data, &message); - if (error) - return error; + /* Read all messages until invalid, this will update the + config crc stored in mxt_data */ + mxt_read_t9_messages_until_invalid(data); - object = mxt_get_object(data, MXT_GEN_COMMAND_T6); - if (!object) - return -EIO; + /* on failure, CRC is set to 0 and config will always be downloaded */ +} - /* Check if this message is from command processor (which has - only one reporting ID), if so, bytes 1-3 are the checksum. */ - if (message.reportid == object->max_reportid) { - current_crc = message.message[1] | (message.message[2] << 8) | - (message.message[3] << 16); - } else { - dev_info(dev, "Couldn't retrieve the current cfg checksum, " - "forcing load\n"); - current_crc = 0xFFFFFFFF; +static u32 mxt_calculate_crc32(u32 crc, u8 firstbyte, u8 secondbyte) +{ + static const unsigned int crcpoly = 0x80001B; + u32 result; + u16 data_word; + + data_word = (u16)((u16)(secondbyte << 8) | firstbyte); + result = ((crc << 1) ^ (u32)data_word); + + /* if bit 25 is set, XOR with crcpoly */ + if (result & 0x1000000) { + result ^= crcpoly; } - dev_info(dev, - "Config CRC read from the mXT: %X\n", - (unsigned int) current_crc); - if (current_crc == pdata->config_crc) { - dev_info(dev, - "Matching CRC's, skipping CFG load.\n"); + return result; +} + +static u32 mxt_calculate_config_crc(u8 *base, off_t start_off, off_t end_off) +{ + u8 *i; + u32 crc = 0; + u8 *last_val = base + end_off - 1; + + if (end_off < start_off) + return -EINVAL; + + for (i = base + start_off; i < last_val; i += 2) { + crc = mxt_calculate_crc32(crc, *i, *(i + 1)); + } + + /* if len is odd, fill the last byte with 0 */ + if (i == last_val) + crc = mxt_calculate_crc32(crc, *i, 0); + + /* Mask to 24-bit */ + return (crc & 0x00FFFFFF); +} + +int mxt_download_config(struct mxt_data *data, const char *fn) +{ + struct device *dev = &data->client->dev; + struct mxt_info cfg_info; + struct mxt_object *object; + const struct firmware *cfg = NULL; + int ret; + int offset; + int data_pos; + int byte_offset; + int i; + int config_start_offset; + u32 info_crc, config_crc, calculated_crc; + u8 *config_mem; + size_t config_mem_size; + unsigned int type, instance, size; + u8 val; + u16 reg; + + ret = request_firmware(&cfg, fn, dev); + if (ret < 0) { + dev_err(dev, "Failure to request config file %s\n", fn); return 0; - } else { - dev_info(dev, "Doesn't match platform data config CRC (%X), " - "writing config from platform data...\n", - (unsigned int) pdata->config_crc); } - for (i = 0; i < data->info.object_num; i++) { - object = data->object_table + i; + mxt_read_current_crc(data); - if (!mxt_object_writable(object->type)) - continue; - dev_info(dev, "Writing object type %d, config offset %d", data->object_table[i].type, index); - for (j = 0; - j < object->size * object->instances; - j++) { - config_offset = index + j; - if (config_offset > pdata->config_length) { - dev_err(dev, "Not enough config data!\n"); - dev_err(dev, "config base is %d, offset is %d\n", index, config_offset); - return -EINVAL; - } - mxt_write_object(data, object->type, j, - pdata->config[config_offset]); - } - index += object->size * object->instances; + if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { + dev_err(dev, "Unrecognised config file\n"); + ret = -EINVAL; + goto release; } - dev_info(dev, "Config written!"); - error = mxt_make_highchg(data); - if (error) - return error; + data_pos = strlen(MXT_CFG_MAGIC); + + /* Load information block and check */ + for (i = 0; i < sizeof(struct mxt_info); i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + (unsigned char *)&cfg_info + i, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release; + } - /* Backup to memory */ - mxt_write_object(data, MXT_GEN_COMMAND_T6, - MXT_COMMAND_BACKUPNV, - MXT_BACKUP_VALUE); - msleep(MXT_BACKUP_TIME); - do { - error = mxt_read_object(data, MXT_GEN_COMMAND_T6, - MXT_COMMAND_BACKUPNV, - &command_register); - if (error) - return error; - msleep(10); - } while ((command_register != 0) && (timeout_counter++ <= 100)); - if (timeout_counter > 100) { - dev_err(&client->dev, "No response after backup!\n"); - return -EIO; + data_pos += offset; } - /* Clear the interrupt line */ - error = mxt_make_highchg(data); - if (error) - return error; + /* Read CRCs */ + ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release; + } + data_pos += offset; - /* Soft reset */ - mxt_write_object(data, MXT_GEN_COMMAND_T6, - MXT_COMMAND_RESET, 1); - if (data->pdata->read_chg == NULL) { - msleep(MXT_RESET_NOCHGREAD); + ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release; + } + data_pos += offset; + + /* The Info Block CRC is calculated over mxt_info and the object table + * If it does not match then we are trying to load the configuration + * from a different chip or firmware version, so the configuration CRC + * is invalid anyway. */ + if (info_crc == data->info_block_crc) { + if (config_crc == 0 || data->config_crc == 0) { + dev_info(dev, "CRC zero, attempting to apply config\n"); + } else if (config_crc == data->config_crc) { + dev_info(dev, "Config CRC 0x%06X: OK\n", data->config_crc); + ret = 0; + goto release; + } else { + dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", + data->config_crc, config_crc); + } } else { - switch (data->info.family_id) { - case MXT224_ID: - msleep(MXT224_RESET_TIME); - break; - case MXT768E_ID: - msleep(MXT768E_RESET_TIME); - break; - case MXT1386_ID: - msleep(MXT1386_RESET_TIME); + dev_warn(dev, "Info block CRC mismatch - attempting to apply config\n"); + } + + /* Malloc memory to store configuration */ + config_start_offset = MXT_OBJECT_START + + data->info.object_num * MXT_OBJECT_SIZE; + config_mem_size = data->mem_size - config_start_offset; + config_mem = kzalloc(config_mem_size, GFP_KERNEL); + if (!config_mem) { + dev_err(dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto release; + } + + while (data_pos < cfg->size) { + /* Read type, instance, length */ + ret = sscanf(cfg->data + data_pos, "%x %x %x%n", + &type, &instance, &size, &offset); + if (ret == 0) { + /* EOF */ break; - default: - msleep(MXT_RESET_TIME); + } else if (ret != 3) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release_mem; } - timeout_counter = 0; - while ((timeout_counter++ <= 100) && data->pdata->read_chg()) - msleep(10); - if (timeout_counter > 100) { - dev_err(&client->dev, "No response after reset!\n"); - return -EIO; + data_pos += offset; + + object = mxt_get_object(data, type); + if (!object) { + ret = -EINVAL; + goto release_mem; + } + + if (instance >= object->instances) { + dev_err(dev, "Object instances exceeded!\n"); + ret = -EINVAL; + goto release_mem; + } + + reg = object->start_address + object->size * instance; + + if (size > object->size) { + /* Either we are in fallback mode due to wrong + * config or config from a later fw version, + * or the file is corrupt or hand-edited */ + dev_warn(dev, "Discarding %u bytes in T%u!\n", + size - object->size, type); + + size = object->size; + } else if (object->size > size) { + /* If firmware is upgraded, new bytes may be added to + * end of objects. It is generally forward compatible + * to zero these bytes - previous behaviour will be + * retained. However this does invalidate the CRC and + * will force fallback mode until the configuration is + * updated. We warn here but do nothing else - the + * malloc has zeroed the entire configuration. */ + dev_warn(dev, "Zeroing %d byte(s) in T%d\n", + object->size - size, type); + } + + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + &val, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release_mem; + } + + byte_offset = reg + i - config_start_offset; + + if ((byte_offset >= 0) + && (byte_offset <= config_mem_size)) { + *(config_mem + byte_offset) = val; + } else { + dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", + reg, object->type, byte_offset); + ret = -EINVAL; + goto release_mem; + } + + data_pos += offset; } + } - return 0; -} + /* calculate crc of the received configs (not the raw config file) */ + if (data->T7_address < config_start_offset) { + dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n", + data->T7_address, config_start_offset); + ret = 0; + goto release_mem; + } + calculated_crc = mxt_calculate_config_crc(config_mem, + data->T7_address - config_start_offset, config_mem_size); -static void mxt_handle_pdata(struct mxt_data *data) -{ - const struct mxt_platform_data *pdata = data->pdata; + /* check the crc, calculated should same as what's in file */ + if (config_crc > 0 && (config_crc != calculated_crc)) { + dev_err(dev, "CRC mismatch in config file, calculated=%06X, file=%06X\n", + calculated_crc, config_crc); + ret = 0; + goto release_mem; + } + + /* Write configuration as blocks */ + byte_offset = 0; + while (byte_offset < config_mem_size) { + size = config_mem_size - byte_offset; + + if (size > MXT_MAX_BLOCK_WRITE) + size = MXT_MAX_BLOCK_WRITE; + + ret = mxt_write_block(data->client, + config_start_offset + byte_offset, + size, config_mem + byte_offset); + if (ret != 0) { + dev_err(dev, "Config write error, ret=%d\n", ret); + goto release_mem; + } + + byte_offset += size; + } + + ret = 1; /* tell the caller config has been sent */ - if (pdata->read_chg != NULL) - data->read_chg = pdata->read_chg; +release_mem: + kfree(config_mem); +release: + release_firmware(cfg); + return ret; } -static int mxt_set_power_cfg(struct mxt_data *data, u8 sleep) +static int mxt_set_power_cfg(struct mxt_data *data, u8 mode) { struct device *dev = &data->client->dev; int error; - u8 actv_cycle_time = 0; - u8 idle_cycle_time = 0; - u8 actv2idle_timeout = data->actv2idle_timeout; + u8 actv_cycle_time; + u8 idle_cycle_time; - if (!sleep) { + if (data->state != APPMODE) { + dev_err(dev, "Not in APPMODE\n"); + return -EINVAL; + } + + switch (mode) { + case MXT_POWER_CFG_DEEPSLEEP: + actv_cycle_time = 0; + idle_cycle_time = 0; + break; + case MXT_POWER_CFG_RUN: + default: actv_cycle_time = data->actv_cycle_time; idle_cycle_time = data->idle_cycle_time; + break; } error = mxt_write_object(data, MXT_GEN_POWER_T7, MXT_POWER_ACTVACQINT, @@ -944,127 +1412,175 @@ static int mxt_set_power_cfg(struct mxt_data *data, u8 sleep) if (error) goto i2c_error; - error = mxt_write_object(data, MXT_GEN_POWER_T7, MXT_POWER_ACTV2IDLETO, - actv2idle_timeout); - if (error) - goto i2c_error; + dev_dbg(dev, "Set ACTV %d, IDLE %d\n", actv_cycle_time, idle_cycle_time); - dev_dbg(dev, "%s: Set ACTV %d, IDLE %d", __func__, - actv_cycle_time, idle_cycle_time); + data->is_stopped = (mode == MXT_POWER_CFG_DEEPSLEEP) ? 1 : 0; return 0; i2c_error: - dev_err(dev, "Failed to set power cfg"); + dev_err(dev, "Failed to set power cfg\n"); return error; } -static int mxt_init_power_cfg(struct mxt_data *data) +static int mxt_read_power_cfg(struct mxt_data *data, u8 *actv_cycle_time, + u8 *idle_cycle_time) { - const struct mxt_platform_data *pdata = data->pdata; - struct device *dev = &data->client->dev; int error; - data->slowscan_actv_cycle_time = 120; /* 120mS */ - data->slowscan_idle_cycle_time = 10; /* 10mS */ - data->slowscan_actv2idle_timeout = 100; /* 10 seconds */ - if (pdata->actv_cycle_time > 0 && pdata->idle_cycle_time > 0) { - data->actv_cycle_time = pdata->actv_cycle_time; - data->idle_cycle_time = pdata->idle_cycle_time; - } else { - error = mxt_read_object(data, MXT_GEN_POWER_T7, - MXT_POWER_ACTVACQINT, - &data->actv_cycle_time); + error = mxt_read_object(data, MXT_GEN_POWER_T7, + MXT_POWER_ACTVACQINT, + actv_cycle_time); + if (error) + return error; - if (error) - return error; + error = mxt_read_object(data, MXT_GEN_POWER_T7, + MXT_POWER_IDLEACQINT, + idle_cycle_time); + if (error) + return error; - error = mxt_read_object(data, MXT_GEN_POWER_T7, - MXT_POWER_IDLEACQINT, - &data->idle_cycle_time); + return 0; +} +static int mxt_check_power_cfg_post_reset(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int error; + + error = mxt_read_power_cfg(data, &data->actv_cycle_time, + &data->idle_cycle_time); + if (error) + return error; + + /* Power config is zero, select free run */ + if (data->actv_cycle_time == 0 || data->idle_cycle_time == 0) { + dev_dbg(dev, "Overriding power cfg to free run\n"); + data->actv_cycle_time = 255; + data->idle_cycle_time = 255; + + error = mxt_set_power_cfg(data, MXT_POWER_CFG_RUN); if (error) return error; } - error = mxt_read_object(data, MXT_GEN_POWER_T7, - MXT_POWER_ACTV2IDLETO, - &data->actv2idle_timeout); + return 0; +} - if (error) - return error; +static int mxt_probe_power_cfg(struct mxt_data *data) +{ + int error; - /* On init, power up */ - error = mxt_set_power_cfg(data, 0); + error = mxt_read_power_cfg(data, &data->actv_cycle_time, + &data->idle_cycle_time); if (error) return error; - dev_info(dev, "Initialised power cfg: ACTV %d, IDLE %d", - data->actv_cycle_time, data->idle_cycle_time); + /* If in deep sleep mode, attempt reset */ + if (data->actv_cycle_time == 0 || data->idle_cycle_time == 0) { + error = mxt_soft_reset(data, MXT_RESET_VALUE); + if (error) + return error; + + error = mxt_check_power_cfg_post_reset(data); + if (error) + return error; + } return 0; } -static int mxt_get_info(struct mxt_data *data) +static int mxt_check_reg_init(struct mxt_data *data) { - struct i2c_client *client = data->client; - struct mxt_info *info = &data->info; - int error; - u8 val; + struct device *dev = &data->client->dev; + int timeout_counter = 0; + int ret; + u8 command_register; - /* force send of address pointer on first read during probe */ - data->last_address = -1; + ret = mxt_download_config(data, MXT_CFG_NAME); + if (ret < 0) + return ret; + else if (ret == 0) + /* CRC matched, or no config file, or config parse failure + * - no need to reset */ + return 0; - error = mxt_read_reg(client, MXT_FAMILY_ID, &val); - if (error) - return error; - info->family_id = val; + /* Backup to memory */ + mxt_write_object(data, MXT_GEN_COMMAND_T6, + MXT_COMMAND_BACKUPNV, + MXT_BACKUP_VALUE); + msleep(MXT_BACKUP_TIME); + do { + ret = mxt_read_object(data, MXT_GEN_COMMAND_T6, + MXT_COMMAND_BACKUPNV, + &command_register); + if (ret) + return ret; + msleep(20); + } while ((command_register != 0) && (timeout_counter++ <= 100)); + if (timeout_counter > 100) { + dev_err(dev, "No response after backup!\n"); + return -EIO; + } - error = mxt_read_reg(client, MXT_VARIANT_ID, &val); - if (error) - return error; - info->variant_id = val; + ret = mxt_soft_reset(data, MXT_RESET_VALUE); + if (ret) + return ret; - error = mxt_read_reg(client, MXT_VERSION, &val); - if (error) - return error; - info->version = val; + ret = mxt_check_power_cfg_post_reset(data); + if (ret) + return ret; - error = mxt_read_reg(client, MXT_BUILD, &val); - if (error) - return error; - info->build = val; + return 0; +} - error = mxt_read_reg(client, MXT_OBJECT_NUM, &val); - if (error) - return error; - info->object_num = val; +static int mxt_read_info_block_crc(struct mxt_data *data) +{ + int ret; + u16 offset; + u8 buf[3]; + + offset = MXT_OBJECT_START + MXT_OBJECT_SIZE * data->info.object_num; + + ret = mxt_read_reg(data->client, offset, sizeof(buf), buf); + if (ret) + return ret; + + data->info_block_crc = (buf[2] << 16) | (buf[1] << 8) | buf[0]; return 0; } static int mxt_get_object_table(struct mxt_data *data) { + struct i2c_client *client = data->client; struct device *dev = &data->client->dev; - int error; + int ret; int i; - u16 reg; + u16 end_address; u8 reportid = 0; - u8 buf[MXT_OBJECT_SIZE]; + u8 buf[data->info.object_num][MXT_OBJECT_SIZE]; + data->mem_size = 0; + + data->object_table = kcalloc(data->info.object_num, + sizeof(struct mxt_object), GFP_KERNEL); + if (!data->object_table) { + dev_err(dev, "Failed to allocate object table\n"); + return -ENOMEM; + } + + ret = mxt_read_reg(client, MXT_OBJECT_START, sizeof(buf), buf); + if (ret) + goto free_object_table; for (i = 0; i < data->info.object_num; i++) { struct mxt_object *object = data->object_table + i; - reg = MXT_OBJECT_START + MXT_OBJECT_SIZE * i; - error = mxt_read_object_table(data->client, reg, buf); - if (error) - return error; - - object->type = buf[0]; - object->start_address = (buf[2] << 8) | buf[1]; - object->size = buf[3] + 1; - object->instances = buf[4] + 1; - object->num_report_ids = buf[5]; + object->type = buf[i][0]; + object->start_address = (buf[i][2] << 8) | buf[i][1]; + object->size = buf[i][3] + 1; + object->instances = buf[i][4] + 1; + object->num_report_ids = buf[i][5]; if (object->num_report_ids) { reportid += object->num_report_ids * object->instances; @@ -1073,203 +1589,366 @@ static int mxt_get_object_table(struct mxt_data *data) object->instances * object->num_report_ids + 1; } - /* Store message window address so we don't have to - search the object table every time we read message */ - if (object->type == MXT_GEN_MESSAGE_T5) - data->msg_address = object->start_address; + end_address = object->start_address + + object->size * object->instances - 1; + + if (end_address >= data->mem_size) + data->mem_size = end_address + 1; + + /* save data for objects used when processing interrupts */ + switch (object->type) { + case MXT_TOUCH_MULTI_T9: + data->T9_reportid_max = object->max_reportid; + data->T9_reportid_min = object->min_reportid; + data->num_touchids = object->num_report_ids * object->instances; + break; + case MXT_GEN_COMMAND_T6: + data->T6_reportid = object->max_reportid; + break; + case MXT_GEN_MESSAGE_T5: + if (data->info.family_id == 0x80) { + /* On mXT224 must read and discard CRC byte + * otherwise DMA reads are misaligned */ + data->T5_msg_size = object->size; + } else { + /* CRC not enabled, therefore don't read last byte */ + data->T5_msg_size = object->size - 1; + } + data->T5_address = object->start_address; + break; + case MXT_GEN_POWER_T7: + data->T7_address = object->start_address; + break; + case MXT_TOUCH_KEYARRAY_T15: + data->T15_reportid_max = object->max_reportid; + data->T15_reportid_min = object->min_reportid; + break; + case MXT_PROCI_TOUCHSUPPRESSION_T42: + data->T42_reportid_max = object->max_reportid; + data->T42_reportid_min = object->min_reportid; + break; + case MXT_SPT_MESSAGECOUNT_T44: + data->T44_address = object->start_address; + break; + case MXT_SPT_NOISESUPPRESSION_T48: + data->T48_reportid = object->max_reportid; + break; + case MXT_PROCI_ACTIVE_STYLUS_T63: + data->T63_reportid_max = object->max_reportid; + data->T63_reportid_min = object->min_reportid; + data->num_stylusids = + object->num_report_ids * object->instances; + break; + } - dev_dbg(dev, "T%d, start:%d size:%d instances:%d " - "min_reportid:%d max_reportid:%d\n", + dev_dbg(dev, "T%u, start:%u size:%u instances:%u " + "min_reportid:%u max_reportid:%u\n", object->type, object->start_address, object->size, object->instances, object->min_reportid, object->max_reportid); } + /* Store maximum reportid */ + data->max_reportid = reportid; + + /* If T44 exists, T9 position has to be directly after */ + if (data->T44_address && (data->T5_address != data->T44_address + 1)) { + dev_err(dev, "Invalid T44 position\n"); + ret = -EINVAL; + goto free_object_table; + } + + /* Allocate message buffer */ + data->msg_buf = kcalloc(data->max_reportid, data->T5_msg_size, GFP_KERNEL); + if (!data->msg_buf) { + dev_err(dev, "Failed to allocate message buffer\n"); + ret = -ENOMEM; + goto free_object_table; + } + return 0; + +free_object_table: + kfree(data->object_table); + return ret; } -static int mxt_initialize(struct mxt_data *data) +static int mxt_read_resolution(struct mxt_data *data) { struct i2c_client *client = data->client; - struct mxt_info *info = &data->info; int error; + unsigned int x_range, y_range; + unsigned char orient; + unsigned char val; - error = mxt_get_info(data); + /* Update matrix size in info struct */ + error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, 1, &val); if (error) return error; + data->info.matrix_xsize = val; - data->object_table = kcalloc(info->object_num, - sizeof(struct mxt_object), - GFP_KERNEL); - if (!data->object_table) { - dev_err(&client->dev, "Failed to allocate memory\n"); - return -ENOMEM; + error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, 1, &val); + if (error) + return error; + data->info.matrix_ysize = val; + + /* Read X/Y size of touchscreen */ + error = mxt_read_object(data, MXT_TOUCH_MULTI_T9, + MXT_TOUCH_XRANGE_MSB, &val); + if (error) + return error; + x_range = val << 8; + + error = mxt_read_object(data, MXT_TOUCH_MULTI_T9, + MXT_TOUCH_XRANGE_LSB, &val); + if (error) + return error; + x_range |= val; + + error = mxt_read_object(data, MXT_TOUCH_MULTI_T9, + MXT_TOUCH_YRANGE_MSB, &val); + if (error) + return error; + y_range = val << 8; + + error = mxt_read_object(data, MXT_TOUCH_MULTI_T9, + MXT_TOUCH_YRANGE_LSB, &val); + if (error) + return error; + y_range |= val; + + error = mxt_read_object(data, MXT_TOUCH_MULTI_T9, + MXT_TOUCH_ORIENT, &orient); + if (error) + return error; + + /* Handle default values */ + if (x_range == 0) + x_range = 1023; + + if (y_range == 0) + y_range = 1023; + + if (orient & MXT_XY_SWITCH) { + data->max_x = y_range; + data->max_y = x_range; + } else { + data->max_x = x_range; + data->max_y = y_range; } + dev_info(&client->dev, + "Matrix Size X%uY%u Touchscreen size X%uY%u\n", + data->info.matrix_xsize, data->info.matrix_ysize, + data->max_x, data->max_y); + + return 0; +} + +static int mxt_initialize(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + struct mxt_info *info = &data->info; + int error; + u8 retry_count = 0; + +retry_probe: + /* Read info block */ + error = mxt_read_reg(client, 0, sizeof(*info), info); + if (error) { + error = mxt_probe_bootloader(data); + if (error) { + /* Chip is not in appmode or bootloader mode */ + return error; + } else { + if (++retry_count > 10) { + dev_err(&client->dev, + "Could not recover device from " + "bootloader mode\n"); + data->state = BOOTLOADER; + /* this is not an error state, we can reflash + * from here */ + return 0; + } + + /* Tell bootloader to enter app mode. Ignore errors + * since we're in a retry loop */ + mxt_send_bootloader_cmd(data, false); + msleep(MXT_FWRESET_TIME); + goto retry_probe; + } + } + + dev_info(&client->dev, + "Family ID: %d Variant ID: %d Version: %d.%d.%02X " + "Object Num: %d\n", + info->family_id, info->variant_id, + info->version >> 4, info->version & 0xf, + info->build, info->object_num); + + data->state = APPMODE; + /* Get object table information */ error = mxt_get_object_table(data); if (error) { - dev_err(&client->dev, "Failed to read object table\n"); + dev_err(&client->dev, "Error %d reading object table\n", error); return error; } - /* Load initial touch chip configuration */ - error = mxt_check_reg_init(data); + /* Read information block CRC */ + error = mxt_read_info_block_crc(data); if (error) { - dev_err(&client->dev, "Failed to initialize configuration\n"); - return error; + dev_err(&client->dev, "Error %d reading info block CRC\n", error); } - mxt_handle_pdata(data); - - error = mxt_init_power_cfg(data); + error = mxt_probe_power_cfg(data); if (error) { dev_err(&client->dev, "Failed to initialize power cfg\n"); return error; } - dev_info(&client->dev, - "Family ID: %d Variant ID: %d Version: %d Build: %d\n", - info->family_id, info->variant_id, info->version, - info->build); + /* Check register init values */ + error = mxt_check_reg_init(data); + if (error) { + dev_err(&client->dev, "Failed to initialize config\n"); + return error; + } - dev_info(&client->dev, - "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n", - info->matrix_xsize, info->matrix_ysize, - info->object_num); + error = mxt_read_resolution(data); + if (error) { + dev_err(&client->dev, "Failed to initialize screen size\n"); + return error; + } return 0; } -static void mxt_calc_resolution(struct mxt_data *data) +static int mxt_check_firmware_format(struct device *dev, const struct firmware *fw) { - unsigned int max_x = data->pdata->x_size - 1; - unsigned int max_y = data->pdata->y_size - 1; - - if (data->pdata->orient & MXT_XY_SWITCH) { - data->max_x = max_y; - data->max_y = max_x; - } else { - data->max_x = max_x; - data->max_y = max_y; - } -} - -static ssize_t mxt_object_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mxt_data *data = dev_get_drvdata(dev); - struct mxt_object *object; - int count = 0; - int i, j; - int error; - u8 val; - - for (i = 0; i < data->info.object_num; i++) { - object = data->object_table + i; + unsigned int pos = 0; + char c; - count += snprintf(buf + count, PAGE_SIZE - count, - "Object[%d] (Type %d)\n", - i + 1, object->type); - if (count >= PAGE_SIZE) - return PAGE_SIZE - 1; - - if (!mxt_object_readable(object->type)) { - count += snprintf(buf + count, PAGE_SIZE - count, - "\n"); - if (count >= PAGE_SIZE) - return PAGE_SIZE - 1; - continue; - } + while (pos < fw->size) { + c = *(fw->data + pos); - for (j = 0; j < object->size; j++) { - error = mxt_read_object(data, - object->type, j, &val); - if (error) - return error; + if (c < '0' || (c > '9' && c < 'A') || c > 'F') + return 0; - count += snprintf(buf + count, PAGE_SIZE - count, - "\t[%2d]: %02x (%d)\n", j, val, val); - if (count >= PAGE_SIZE) - return PAGE_SIZE - 1; - } - - count += snprintf(buf + count, PAGE_SIZE - count, "\n"); - if (count >= PAGE_SIZE) - return PAGE_SIZE - 1; + pos++; } - return count; + /* To convert file try + * xxd -r -p mXTXXX__APP_VX-X-XX.enc > maxtouch.fw */ + dev_err(dev, "Aborting: firmware file must be in binary format\n"); + + return -1; } static int mxt_load_fw(struct device *dev, const char *fn) { struct mxt_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; const struct firmware *fw = NULL; unsigned int frame_size; unsigned int pos = 0; + unsigned int retry = 0; + unsigned int frame = 0; int ret; ret = request_firmware(&fw, fn, dev); - if (ret) { + if (ret < 0) { dev_err(dev, "Unable to open firmware %s\n", fn); return ret; } - /* Change to the bootloader mode */ - mxt_write_object(data, MXT_GEN_COMMAND_T6, - MXT_COMMAND_RESET, MXT_BOOT_VALUE); - msleep(MXT_RESET_TIME); + /* Check for incorrect enc file */ + ret = mxt_check_firmware_format(dev, fw); + if (ret) + goto release_firmware; - /* Change to slave address of bootloader */ - if (client->addr == MXT_APP_LOW) - client->addr = MXT_BOOT_LOW; - else - client->addr = MXT_BOOT_HIGH; + if (data->state != BOOTLOADER) { + /* Change to the bootloader mode */ + ret = mxt_soft_reset(data, MXT_BOOT_VALUE); + if (ret) + goto release_firmware; - ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD); - if (ret) - goto out; + ret = mxt_get_bootloader_address(data); + if (ret) + goto release_firmware; + + data->state = BOOTLOADER; + } - /* Unlock bootloader */ - mxt_unlock_bootloader(client); + ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD); + if (ret) { + mxt_wait_for_chg(data); + /* Bootloader may still be unlocked from previous update + * attempt */ + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA); + if (ret) { + data->state = FAILED; + goto release_firmware; + } + } else { + dev_info(dev, "Unlocking bootloader\n"); + + /* Unlock bootloader */ + ret = mxt_send_bootloader_cmd(data, true); + if (ret) { + data->state = FAILED; + goto release_firmware; + } + } while (pos < fw->size) { - ret = mxt_check_bootloader(client, - MXT_WAITING_FRAME_DATA); - if (ret) - goto out; + mxt_wait_for_chg(data); + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA); + if (ret) { + data->state = FAILED; + goto release_firmware; + } frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); - /* We should add 2 at frame size as the the firmware data is not - * included the CRC bytes. - */ + /* Take account of CRC bytes */ frame_size += 2; /* Write one frame to device */ - mxt_fw_write(client, fw->data + pos, frame_size); + ret = mxt_bootloader_write(data, fw->data + pos, frame_size); + if (ret) { + data->state = FAILED; + goto release_firmware; + } - ret = mxt_check_bootloader(client, - MXT_FRAME_CRC_PASS); - if (ret) - goto out; + mxt_wait_for_chg(data); + ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS); + if (ret) { + retry++; - pos += frame_size; + /* Back off by 20ms per retry */ + msleep(retry * 20); + + if (retry > 20) { + data->state = FAILED; + goto release_firmware; + } + } else { + retry = 0; + pos += frame_size; + frame++; + } + + if (frame % 10 == 0) + dev_info(dev, "Updated %d frames, %d/%zd bytes\n", + frame, pos, fw->size); - dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); } -out: - release_firmware(fw); + dev_info(dev, "Finished, sent %d frames, %zd bytes\n", frame, pos); - /* Change to slave address of application */ - if (client->addr == MXT_BOOT_LOW) - client->addr = MXT_APP_LOW; - else - client->addr = MXT_APP_HIGH; + data->state = INIT; +release_firmware: + release_firmware(fw); return ret; } @@ -1287,38 +1966,63 @@ static ssize_t mxt_update_fw_store(struct device *dev, dev_err(dev, "The firmware update failed(%d)\n", error); count = error; } else { - dev_dbg(dev, "The firmware update succeeded\n"); + dev_info(dev, "The firmware update succeeded\n"); /* Wait for reset */ msleep(MXT_FWRESET_TIME); kfree(data->object_table); data->object_table = NULL; + kfree(data->msg_buf); + data->msg_buf = NULL; mxt_initialize(data); } - enable_irq(data->irq); + if (data->state == APPMODE) { + enable_irq(data->irq); + } - error = mxt_make_highchg(data); - if (error) - return error; + return count; +} + +static ssize_t mxt_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int count = 0; + + count += sprintf(buf + count, "%d", data->info.version); + count += sprintf(buf + count, "\n"); return count; } -static ssize_t mxt_pause_show(struct device *dev, +static ssize_t mxt_build_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); int count = 0; - count += sprintf(buf + count, "%d", data->driver_paused); + count += sprintf(buf + count, "%d", data->info.build); count += sprintf(buf + count, "\n"); return count; } +static ssize_t mxt_pause_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + ssize_t count; + char c; + + c = data->driver_paused ? '1' : '0'; + count = sprintf(buf, "%c\n", c); + + return count; +} + static ssize_t mxt_pause_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -1326,23 +2030,24 @@ static ssize_t mxt_pause_store(struct device *dev, int i; if (sscanf(buf, "%u", &i) == 1 && i < 2) { - data->driver_paused = i; - + data->driver_paused = (i == 1); dev_dbg(dev, "%s\n", i ? "paused" : "unpaused"); + return count; } else { dev_dbg(dev, "pause_driver write error\n"); + return -EINVAL; } - return count; } static ssize_t mxt_debug_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); - int count = 0; + int count; + char c; - count += sprintf(buf + count, "%d", data->debug_enabled); - count += sprintf(buf + count, "\n"); + c = data->debug_enabled ? '1' : '0'; + count = sprintf(buf, "%c\n", c); return count; } @@ -1354,109 +2059,34 @@ static ssize_t mxt_debug_enable_store(struct device *dev, int i; if (sscanf(buf, "%u", &i) == 1 && i < 2) { - data->debug_enabled = i; + data->debug_enabled = (i == 1); dev_dbg(dev, "%s\n", i ? "debug enabled" : "debug disabled"); + return count; } else { dev_dbg(dev, "debug_enabled write error\n"); + return -EINVAL; } - return count; -} - -static ssize_t mxt_slowscan_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mxt_data *data = dev_get_drvdata(dev); - int count = 0; - int error; - u8 actv_cycle_time; - u8 idle_cycle_time; - u8 actv2idle_timeout; - dev_info(dev, "Calling mxt_slowscan_show()\n"); - - error = mxt_read_object(data, MXT_GEN_POWER_T7, - MXT_POWER_ACTVACQINT, - &actv_cycle_time); - - if (error) - return error; - - error = mxt_read_object(data, MXT_GEN_POWER_T7, - MXT_POWER_IDLEACQINT, - &idle_cycle_time); - - if (error) - return error; - - error = mxt_read_object(data, MXT_GEN_POWER_T7, - MXT_POWER_ACTV2IDLETO, - &actv2idle_timeout); - - if (error) - return error; - - count += sprintf(buf + count, "SLOW SCAN (enable/disable) = %s.\n", data->slowscan_enabled ? "enabled" : "disabled"); - count += sprintf(buf + count, "SLOW SCAN (actv_cycle_time) = %umS.\n", data->slowscan_actv_cycle_time); - count += sprintf(buf + count, "SLOW SCAN (idle_cycle_time) = %umS.\n", data->slowscan_idle_cycle_time); - count += sprintf(buf + count, "SLOW SCAN (actv2idle_timeout) = %u.%0uS.\n", data->slowscan_actv2idle_timeout / 10, \ - data->slowscan_actv2idle_timeout % 10); - count += sprintf(buf + count, "CURRENT (actv_cycle_time) = %umS.\n", actv_cycle_time); - count += sprintf(buf + count, "CURRENT (idle_cycle_time) = %umS.\n", idle_cycle_time); - count += sprintf(buf + count, "CURRENT (actv2idle_timeout) = %u.%0uS.\n", actv2idle_timeout / 10, \ - actv2idle_timeout % 10); - - return count; } -static ssize_t mxt_slowscan_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static int mxt_check_mem_access_params(struct mxt_data *data, loff_t off, + size_t *count) { - struct mxt_data *data = dev_get_drvdata(dev); - int fn; - int val; - int ret; - - dev_info(dev, "Calling mxt_slowscan_store()\n"); - ret = sscanf(buf, "%u %u", &fn, &val); - if ((ret == 1) || (ret == 2)) { - switch (fn) { - case SLOSCAN_DISABLE: - if (data->slowscan_enabled) { - data->actv_cycle_time = data->slowscan_shad_actv_cycle_time; - data->idle_cycle_time = data->slowscan_shad_idle_cycle_time; - data->actv2idle_timeout = data->slowscan_shad_actv2idle_timeout; - data->slowscan_enabled = 0; - mxt_set_power_cfg(data, 0); - } - break; + if (data->state != APPMODE) { + dev_err(&data->client->dev, "Not in APPMODE\n"); + return -EINVAL; + } - case SLOSCAN_ENABLE: - if (!data->slowscan_enabled) { - data->slowscan_shad_actv_cycle_time = data->actv_cycle_time; - data->slowscan_shad_idle_cycle_time = data->idle_cycle_time; - data->slowscan_shad_actv2idle_timeout = data->actv2idle_timeout; - data->actv_cycle_time = data->slowscan_actv_cycle_time; - data->idle_cycle_time = data->slowscan_idle_cycle_time; - data->actv2idle_timeout = data->slowscan_actv2idle_timeout; - data->slowscan_enabled = 1; - mxt_set_power_cfg(data, 0); - } - break; + if (off >= data->mem_size) + return -EIO; - case SLOSCAN_SET_ACTVACQINT: - data->slowscan_actv_cycle_time = val; - break; + if (off + *count > data->mem_size) + *count = data->mem_size - off; - case SLOSCAN_SET_IDLEACQINT: - data->slowscan_idle_cycle_time = val; - break; + if (*count > MXT_MAX_BLOCK_WRITE) + *count = MXT_MAX_BLOCK_WRITE; - case SLOSCAN_SET_ACTV2IDLETO: - data->slowscan_actv2idle_timeout = val; - break; - } - } - return count; + return 0; } static ssize_t mxt_mem_access_read(struct file *filp, struct kobject *kobj, @@ -1466,62 +2096,27 @@ static ssize_t mxt_mem_access_read(struct file *filp, struct kobject *kobj, struct mxt_data *data = dev_get_drvdata(dev); int ret = 0; - if (off >= 32768) - return -EIO; - - if (off + count > 32768) - count = 32768 - off; - - if (count > 256) - count = 256; + ret = mxt_check_mem_access_params(data, off, &count); + if (ret < 0) + return ret; if (count > 0) - ret = __mxt_read_reg(data->client, off, count, buf); + ret = mxt_read_reg(data->client, off, count, buf); return ret == 0 ? count : ret; } -int mxt_write_block(struct i2c_client *client, u16 addr, u16 length, u8 *value) -{ - int i; - struct { - __le16 le_addr; - u8 data[256]; - } i2c_block_transfer; - - if (length > 256) - return -EINVAL; - - i2c_get_clientdata(client); - - for (i = 0; i < length; i++) - i2c_block_transfer.data[i] = *value++; - - i2c_block_transfer.le_addr = cpu_to_le16(addr); - - i = i2c_master_send(client, (u8 *) &i2c_block_transfer, length + 2); - - if (i == (length + 2)) - return 0; - else - return -EIO; -} - static ssize_t mxt_mem_access_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) { struct device *dev = container_of(kobj, struct device, kobj); struct mxt_data *data = dev_get_drvdata(dev); int ret = 0; - if (off >= 32768) - return -EIO; - - if (off + count > 32768) - count = 32768 - off; - - if (count > 256) - count = 256; + ret = mxt_check_mem_access_params(data, off, &count); + if (ret < 0) + return ret; if (count > 0) ret = mxt_write_block(data->client, off, count, buf); @@ -1529,18 +2124,20 @@ static ssize_t mxt_mem_access_write(struct file *filp, struct kobject *kobj, return ret == 0 ? count : 0; } -static DEVICE_ATTR(object, 0444, mxt_object_show, NULL); -static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store); -static DEVICE_ATTR(pause_driver, 0664, mxt_pause_show, mxt_pause_store); -static DEVICE_ATTR(debug_enable, 0664, mxt_debug_enable_show, mxt_debug_enable_store); -static DEVICE_ATTR(slowscan_enable, 0664, mxt_slowscan_show, mxt_slowscan_store); +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); +static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show, + mxt_debug_enable_store); +static DEVICE_ATTR(pause_driver, S_IWUSR | S_IRUSR, mxt_pause_show, + mxt_pause_store); +static DEVICE_ATTR(version, S_IRUGO, mxt_version_show, NULL); +static DEVICE_ATTR(build, S_IRUGO, mxt_build_show, NULL); static struct attribute *mxt_attrs[] = { - &dev_attr_object.attr, &dev_attr_update_fw.attr, - &dev_attr_pause_driver.attr, &dev_attr_debug_enable.attr, - &dev_attr_slowscan_enable.attr, + &dev_attr_pause_driver.attr, + &dev_attr_version.attr, + &dev_attr_build.attr, NULL }; @@ -1550,54 +2147,39 @@ static const struct attribute_group mxt_attr_group = { static void mxt_start(struct mxt_data *data) { - int error = 0; - int cnt; + int error; struct device *dev = &data->client->dev; - dev_info(dev, "mxt_start: is_stopped = %d\n", data->is_stopped); if (data->is_stopped == 0) return; - /* Touch enable */ - cnt = ARRAY_SIZE(mxt_save); - while (cnt--) - error |= mxt_write_object(data, mxt_save[cnt].suspend_obj, - mxt_save[cnt].suspend_reg, - mxt_save[cnt].restore_val); + error = mxt_set_power_cfg(data, MXT_POWER_CFG_RUN); + if (error) + return; - if (!error) - dev_info(dev, "MXT started\n"); + /* At this point, it may be necessary to clear state + * by disabling/re-enabling the noise suppression object */ - data->is_stopped = 0; + /* Recalibrate since chip has been in deep sleep */ + error = mxt_write_object(data, MXT_GEN_COMMAND_T6, + MXT_COMMAND_CALIBRATE, 1); + + if (!error) + dev_dbg(dev, "MXT started\n"); } static void mxt_stop(struct mxt_data *data) { - int error = 0; - int i, cnt; + int error; struct device *dev = &data->client->dev; - dev_info(dev, "mxt_stop: is_stopped = %d\n", data->is_stopped); if (data->is_stopped) return; - /* Touch disable */ - cnt = ARRAY_SIZE(mxt_save); - for (i = 0; i < cnt; i++) { - if (mxt_save[i].suspend_flags == MXT_SUSPEND_DYNAMIC) - error |= mxt_read_object(data, - mxt_save[i].suspend_obj, - mxt_save[i].suspend_reg, - &mxt_save[i].restore_val); - error |= mxt_write_object(data, mxt_save[i].suspend_obj, - mxt_save[i].suspend_reg, - mxt_save[i].suspend_val); - } + error = mxt_set_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); if (!error) - dev_info(dev, "MXT suspended\n"); - - data->is_stopped = 1; + dev_dbg(dev, "MXT suspended\n"); } static int mxt_input_open(struct input_dev *dev) @@ -1616,39 +2198,31 @@ static void mxt_input_close(struct input_dev *dev) mxt_stop(data); } -static int __devinit mxt_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mxt_initialize_input_device(struct mxt_data *data) { - const struct mxt_platform_data *pdata = client->dev.platform_data; - struct mxt_data *data; + struct device *dev = &data->client->dev; struct input_dev *input_dev; - int error; - - if (!pdata) - return -EINVAL; + int ret; + int key; - data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); + /* Initialize input device */ input_dev = input_allocate_device(); - if (!data || !input_dev) { - dev_err(&client->dev, "Failed to allocate memory\n"); - error = -ENOMEM; - goto err_free_mem; + if (!input_dev) { + dev_err(dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + if (data->pdata->input_name) { + input_dev->name = data->pdata->input_name; + } else { + input_dev->name = "atmel-maxtouch"; } - input_dev->name = "atmel-maxtouch"; input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &client->dev; + input_dev->dev.parent = dev; input_dev->open = mxt_input_open; input_dev->close = mxt_input_close; - data->client = client; - data->input_dev = input_dev; - data->pdata = pdata; - data->irq = client->irq; - data->is_stopped = 0; - - mxt_calc_resolution(data); - __set_bit(EV_ABS, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); @@ -1662,6 +2236,8 @@ static int __devinit mxt_probe(struct i2c_client *client, 0, 255, 0, 0); /* For multi touch */ + input_mt_init_slots(input_dev, + data->num_touchids + data->num_stylusids); input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MXT_MAX_AREA, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_X, @@ -1670,64 +2246,117 @@ static int __devinit mxt_probe(struct i2c_client *client, 0, data->max_y, 0, 0); input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, + 0, 255, 0, 0); + + /* For T63 active stylus */ + if (data->T63_reportid_min) { + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + } + + /* For T15 key array */ + mxt_t15_keystatus = 0; + for (key = 0; key < ARRAY_SIZE(mxt_t15_keys); key++) { + input_set_capability(input_dev, EV_KEY, mxt_t15_keys[key]); + } input_set_drvdata(input_dev, data); - i2c_set_clientdata(client, data); + i2c_set_clientdata(data->client, data); + + ret = input_register_device(input_dev); + if (ret) { + dev_err(dev, "Error %d registering input device\n", ret); + input_free_device(input_dev); + return ret; + } + + data->input_dev = input_dev; + + return 0; +} - mutex_init(&data->access_mutex); +static int __devinit mxt_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct mxt_platform_data *pdata = client->dev.platform_data; + struct mxt_data *data; + int error; + + if (!pdata) + return -EINVAL; + data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); + if (!data) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + data->state = INIT; + + data->client = client; + data->pdata = pdata; + data->irq = client->irq; + + /* Initialize i2c device */ error = mxt_initialize(data); if (error) + goto err_free_data; + + error = mxt_initialize_input_device(data); + if (error) goto err_free_object; error = request_threaded_irq(client->irq, NULL, mxt_interrupt, pdata->irqflags, client->dev.driver->name, data); if (error) { - dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_object; - } - - error = mxt_make_highchg(data); - if (error) { - dev_err(&client->dev, "Failed to make high CHG\n"); - goto err_free_irq; + dev_err(&client->dev, "Error %d registering irq\n", error); + goto err_free_input_device; } - error = input_register_device(input_dev); - if (error) { - dev_err(&client->dev, "Failed to register input device\n"); - goto err_free_irq; - } +#ifdef CONFIG_HAS_EARLYSUSPEND + data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + data->early_suspend.suspend = mxt_early_suspend; + data->early_suspend.resume = mxt_late_resume; + register_early_suspend(&data->early_suspend); +#endif error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); if (error) { - dev_err(&client->dev, "Failed to create sysfs group\n"); - goto err_unregister_device; + dev_err(&client->dev, "Failure %d creating sysfs group\n", + error); + goto err_free_irq; } sysfs_bin_attr_init(&data->mem_access_attr); data->mem_access_attr.attr.name = "mem_access"; - data->mem_access_attr.attr.mode = S_IRUGO | S_IWUSR | S_IWGRP; + data->mem_access_attr.attr.mode = S_IRUGO | S_IWUSR; data->mem_access_attr.read = mxt_mem_access_read; data->mem_access_attr.write = mxt_mem_access_write; - data->mem_access_attr.size = 65535; + data->mem_access_attr.size = data->mem_size; - if (sysfs_create_bin_file(&client->dev.kobj, &data->mem_access_attr) < 0) { - dev_err(&client->dev, "Failed to create %s\n", data->mem_access_attr.attr.name); - goto err_unregister_device; + if (sysfs_create_bin_file(&client->dev.kobj, + &data->mem_access_attr) < 0) { + dev_err(&client->dev, "Failed to create %s\n", + data->mem_access_attr.attr.name); + goto err_remove_sysfs_group; } return 0; -err_unregister_device: - input_unregister_device(input_dev); - input_dev = NULL; +err_remove_sysfs_group: + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); err_free_irq: free_irq(client->irq, data); +err_free_input_device: + input_unregister_device(data->input_dev); err_free_object: + kfree(data->msg_buf); kfree(data->object_table); -err_free_mem: - input_free_device(input_dev); +err_free_data: kfree(data); return error; } @@ -1740,13 +2369,20 @@ static int __devexit mxt_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); free_irq(data->irq, data); input_unregister_device(data->input_dev); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&data->early_suspend); +#endif + kfree(data->msg_buf); + data->msg_buf = NULL; kfree(data->object_table); + data->object_table = NULL; kfree(data); + data = NULL; return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int mxt_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -1769,11 +2405,6 @@ static int mxt_resume(struct device *dev) struct mxt_data *data = i2c_get_clientdata(client); struct input_dev *input_dev = data->input_dev; - /* Soft reset */ - mxt_write_object(data, MXT_GEN_COMMAND_T6, MXT_COMMAND_RESET, 1); - - msleep(MXT_RESET_TIME); - mutex_lock(&input_dev->mutex); if (input_dev->users) @@ -1784,12 +2415,38 @@ static int mxt_resume(struct device *dev) return 0; } -static const struct dev_pm_ops mxt_pm_ops = { - .suspend = mxt_suspend, - .resume = mxt_resume, -}; +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mxt_early_suspend(struct early_suspend *es) +{ + struct mxt_data *mxt; + mxt = container_of(es, struct mxt_data, early_suspend); + + if (mxt_suspend(&mxt->client->dev) != 0) + dev_err(&mxt->client->dev, "%s: failed\n", __func__); +} + +static void mxt_late_resume(struct early_suspend *es) +{ + struct mxt_data *mxt; + mxt = container_of(es, struct mxt_data, early_suspend); + + if (mxt_resume(&mxt->client->dev) != 0) + dev_err(&mxt->client->dev, "%s: failed\n", __func__); +} +#endif + #endif +static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); + +static void mxt_shutdown(struct i2c_client *client) +{ + struct mxt_data *data = i2c_get_clientdata(client); + + disable_irq(data->irq); + data->state = SHUTDOWN; +} + static const struct i2c_device_id mxt_id[] = { { "qt602240_ts", 0 }, { "atmel_mxt_ts", 0 }, @@ -1802,18 +2459,29 @@ static struct i2c_driver mxt_driver = { .driver = { .name = "atmel_mxt_ts", .owner = THIS_MODULE, -#if defined(CONFIG_PM) .pm = &mxt_pm_ops, -#endif }, .probe = mxt_probe, .remove = __devexit_p(mxt_remove), + .shutdown = mxt_shutdown, .id_table = mxt_id, }; -module_i2c_driver(mxt_driver); +static int __init mxt_init(void) +{ + return i2c_add_driver(&mxt_driver); +} + +static void __exit mxt_exit(void) +{ + i2c_del_driver(&mxt_driver); +} + +module_init(mxt_init); +module_exit(mxt_exit); /* Module information */ MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver"); MODULE_LICENSE("GPL"); + |