aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/touchscreen/mxt540s.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen/mxt540s.c')
-rw-r--r--drivers/input/touchscreen/mxt540s.c2131
1 files changed, 2131 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/mxt540s.c b/drivers/input/touchscreen/mxt540s.c
new file mode 100644
index 0000000..6c66adb
--- /dev/null
+++ b/drivers/input/touchscreen/mxt540s.c
@@ -0,0 +1,2131 @@
+/*
+ * Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c/mxt540s.h>
+#include <asm/unaligned.h>
+#include <linux/firmware.h>
+#include <linux/string.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include "mxt540s_dev.h"
+
+int mxt_read_mem(struct mxt_data *data, u16 reg, u8 len, u8 *buf)
+{
+ int ret = 0, i = 0;
+ u16 le_reg = cpu_to_le16(reg);
+ struct i2c_msg msg[2] = {
+ {
+ .addr = data->client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = (u8 *)&le_reg,
+ },
+ {
+ .addr = data->client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ },
+ };
+
+ for (i = 0; i < 3 ; i++) {
+ ret = i2c_transfer(data->client->adapter, msg, 2);
+ if (ret < 0)
+ dev_err(&data->client->dev, "%s fail[%d] address[0x%x]\n",
+ __func__, ret, le_reg);
+ else
+ break;
+ }
+ return ret == 2 ? 0 : -EIO;
+}
+
+int mxt_write_mem(struct mxt_data *data,
+ u16 reg, u8 len, const u8 *buf)
+{
+ int ret = 0, i = 0;
+ u8 tmp[len + 2];
+
+ put_unaligned_le16(cpu_to_le16(reg), tmp);
+ memcpy(tmp + 2, buf, len);
+
+ for (i = 0; i < 3 ; i++) {
+ ret = i2c_master_send(data->client, tmp, sizeof(tmp));
+ if (ret < 0)
+ dev_err(&data->client->dev,
+ "%s %d times write error on address[0x%x,0x%x]\n",
+ __func__, i, tmp[1], tmp[0]);
+ else
+ break;
+ }
+
+ return ret == sizeof(tmp) ? 0 : -EIO;
+}
+
+struct mxt_object *
+ mxt_get_object(struct mxt_data *data, u8 object_type)
+{
+ struct mxt_object *object;
+ int i;
+
+ if (!data->objects)
+ return NULL;
+
+ for (i = 0; i < data->info.object_num; i++) {
+ object = data->objects + i;
+ if (object->object_type == object_type)
+ return object;
+ }
+
+ dev_err(&data->client->dev, "Invalid object type T%d\n",
+ object_type);
+
+ return NULL;
+}
+
+int mxt_read_object(struct mxt_data *data,
+ u8 type, u8 offset, u8 *val)
+{
+ struct mxt_object *object;
+ u16 reg;
+
+ object = mxt_get_object(data, type);
+ if (!object)
+ return -EINVAL;
+
+ reg = object->start_address;
+
+ return mxt_read_mem(data, reg + offset, 1, val);
+}
+
+int mxt_write_object(struct mxt_data *data,
+ u8 type, u8 offset, u8 val)
+{
+ struct mxt_object *object;
+ u16 reg;
+
+ object = mxt_get_object(data, type);
+ if (!object)
+ 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_mem(data, reg + offset, 1, &val);
+}
+
+static int mxt_reset(struct mxt_data *data)
+{
+ u8 buf = 1u;
+ return mxt_write_mem(data, data->cmd_proc + CMD_RESET_OFFSET, 1, &buf);
+}
+
+static int mxt_backup(struct mxt_data *data)
+{
+ u8 buf = 0x55u;
+ return mxt_write_mem(data, data->cmd_proc + CMD_BACKUP_OFFSET, 1, &buf);
+}
+
+static int mxt_start(struct mxt_data *data)
+{
+ int error;
+
+ /* Touch report enable */
+ error = mxt_write_object(data, TOUCH_MULTITOUCHSCREEN_T9,
+ MXT_T9_CTRL, data->tsp_ctrl);
+
+ if (error)
+ dev_err(&data->client->dev, "Fail to start touch\n");
+
+ return error;
+}
+
+static void mxt_stop(struct mxt_data *data)
+{
+ /* Touch report disable */
+ mxt_write_object(data, TOUCH_MULTITOUCHSCREEN_T9, MXT_T9_CTRL, 0);
+}
+
+static int mxt_check_instance(struct mxt_data *data, u8 object_type)
+{
+ int i;
+
+ for (i = 0; i < data->info.object_num; i++) {
+ if (data->objects[i].object_type == object_type)
+ return data->objects[i].instances;
+ }
+ return 0;
+}
+
+static u32 crc24(u32 crc, u8 byte1, u8 byte2)
+{
+ static const u32 crcpoly = 0x80001B;
+ u32 res;
+ u16 data_word;
+
+ data_word = (((u16)byte2) << 8) | byte1;
+ res = (crc << 1) ^ (u32)data_word;
+
+ if (res & 0x1000000)
+ res ^= crcpoly;
+
+ return res;
+}
+
+static int mxt_calculate_infoblock_crc(struct mxt_data *data,
+ u32 *crc_pointer)
+{
+ u32 crc = 0;
+ u8 mem[7 + data->info.object_num * 6];
+ int ret;
+ int i;
+
+ ret = mxt_read_mem(data, 0, sizeof(mem), mem);
+
+ if (ret)
+ return ret;
+
+ for (i = 0; i < sizeof(mem) - 1; i += 2)
+ crc = crc24(crc, mem[i], mem[i + 1]);
+
+ *crc_pointer = crc24(crc, mem[i], 0) & 0x00FFFFFF;
+
+ return 0;
+}
+
+static int mxt_read_info_crc(struct mxt_data *data, u32 *crc_pointer)
+{
+ u16 crc_address;
+ u8 msg[3];
+ int ret;
+
+ /* Read Info block CRC address */
+ crc_address = OBJECT_TABLE_START_ADDRESS +
+ data->info.object_num * OBJECT_TABLE_ELEMENT_SIZE;
+
+ ret = mxt_read_mem(data, crc_address, 3, msg);
+ if (ret)
+ return ret;
+
+ *crc_pointer = msg[0] | (msg[1] << 8) | (msg[2] << 16);
+
+ return 0;
+}
+
+static int mxt_reportid_to_type(struct mxt_data *data,
+ u8 report_id, u8 *instance)
+{
+ if (report_id <= data->max_report_id) {
+ *instance = data->rid_map[report_id].instance;
+ return data->rid_map[report_id].object_type;
+ } else
+ return 0;
+}
+
+static int mxt_read_config_crc(struct mxt_data *data, u32 *crc_pointer)
+{
+ struct device *dev = &data->client->dev;
+ struct mxt_object *object;
+ u8 msg[data->msg_object_size];
+ int error, try = 0;
+ int fail_count = data->max_report_id * 2;
+ u8 object_type, instance;
+
+ object = mxt_get_object(data, GEN_COMMANDPROCESSOR_T6);
+ if (!object)
+ return -EIO;
+
+ /* Try to read the config checksum of the existing cfg */
+ mxt_write_object(data, GEN_COMMANDPROCESSOR_T6,
+ CMD_REPORTATLL_OFFSET, 1);
+
+ /* Read message from command processor, which only has one report ID */
+ while (++try < fail_count) {
+ error = mxt_read_mem(data, data->msg_proc, sizeof(msg), msg);
+ if (error)
+ return error;
+
+ object_type = mxt_reportid_to_type(data, msg[0] , &instance);
+ if (object_type == RESERVED_T0)
+ return -EINVAL;
+
+ if (object_type == GEN_COMMANDPROCESSOR_T6)
+ break;
+ }
+
+ if (error) {
+ dev_err(dev, "Failed to retrieve CRC\n");
+ return error;
+ }
+
+ /* Bytes 1-3 are the checksum. */
+ *crc_pointer = msg[2] | (msg[3] << 8) | (msg[4] << 16);
+
+ return 0;
+}
+
+static int mxt_get_id_info(struct mxt_data *data)
+{
+ int ret = 0;
+ u8 id[ID_BLOCK_SIZE];
+
+ /* Read IC information */
+ ret = mxt_read_mem(data, 0, sizeof(id), id);
+ if (ret) {
+ dev_err(&data->client->dev, "Read fail. IC information\n");
+ goto out;
+ } else {
+ dev_info(&data->client->dev,
+ "family: 0x%x variant: 0x%x version: 0x%x"
+ " build: 0x%x matrix X,Y size: %d,%d"
+ " number of obect: %d\n"
+ , id[0], id[1], id[2], id[3], id[4], id[5], id[6]);
+ data->info.family_id = id[0];
+ data->info.variant_id = id[1];
+ data->info.version = id[2];
+ data->info.build = id[3];
+ data->info.matrix_xsize = id[4];
+ data->info.matrix_ysize = id[5];
+ data->info.object_num = id[6];
+ }
+
+out:
+ return ret;
+}
+
+static int mxt_get_object_table(struct mxt_data *data)
+{
+ int ret = 0;
+ int i;
+ u8 type_count = 0;
+
+ ret = mxt_read_mem(data, OBJECT_TABLE_START_ADDRESS,
+ data->info.object_num * sizeof(*data->objects),
+ (u8 *)data->objects);
+
+ if (ret)
+ goto out;
+
+ data->max_report_id = 0;
+
+ for (i = 0; i < data->info.object_num; i++) {
+ data->objects[i].start_address =
+ le16_to_cpu(data->objects[i].start_address);
+ /* size and instance values are smaller than atual value */
+ data->objects[i].size += 1;
+ data->objects[i].instances += 1;
+ data->max_report_id += data->objects[i].num_report_ids *
+ (data->objects[i].instances);
+
+ switch (data->objects[i].object_type) {
+ case GEN_MESSAGEPROCESSOR_T5:
+ data->msg_object_size = data->objects[i].size;
+ data->msg_proc = data->objects[i].start_address;
+ dev_dbg(&data->client->dev,
+ "mesage object size: %d message address: 0x%x\n",
+ data->msg_object_size, data->msg_proc);
+ break;
+ case GEN_COMMANDPROCESSOR_T6:
+ data->cmd_proc = data->objects[i].start_address;
+ break;
+ case TOUCH_MULTITOUCHSCREEN_T9:
+ data->finger_report_id = type_count + 1;
+ dev_dbg(&data->client->dev, "Finger report id: %d\n",
+ data->finger_report_id);
+ break;
+ }
+
+ if (data->objects[i].num_report_ids) {
+ type_count += data->objects[i].num_report_ids *
+ (data->objects[i].instances);
+ }
+ }
+
+ dev_info(&data->client->dev, "maXTouch: %d Objects\n",
+ data->info.object_num);
+#ifdef DEBUG
+ for (i = 0; i < data->info.object_num; i++) {
+ dev_dbg(&data->client->dev,
+ "Object:T%d\t\t\t Address:0x%x\tSize:%d\tInstance:%d\tReport Id's:%d\n",
+ data->objects[i].object_type,
+ data->objects[i].start_address,
+ data->objects[i].size,
+ data->objects[i].instances,
+ data->objects[i].num_report_ids);
+ }
+#endif
+
+out:
+ return ret;
+}
+
+static void __devinit mxt_make_reportid_table(struct mxt_data *data)
+{
+ struct mxt_object *objects = data->objects;
+ int i, j;
+ int cur_id, sta_id;
+
+ data->rid_map[0].instance = 0;
+ data->rid_map[0].object_type = 0;
+ cur_id = 1;
+
+ for (i = 0; i < data->info.object_num; i++) {
+ if (objects[i].num_report_ids == 0)
+ continue;
+ for (j = 1; j <= objects[i].instances; j++) {
+ for (sta_id = cur_id;
+ cur_id < (sta_id + objects[i].num_report_ids);
+ cur_id++) {
+
+ data->rid_map[cur_id].instance = j;
+ data->rid_map[cur_id].object_type =
+ objects[i].object_type;
+ }
+ }
+ }
+
+ dev_info(&data->client->dev, "maXTouch: %d report ID\n",
+ data->max_report_id);
+
+#ifdef DEBUG
+ for (i = 0; i < data->max_report_id; i++) {
+ dev_dbg(&data->client->dev, "Report_id[%d]:\tT%d\n",
+ i, data->rid_map[i].object_type);
+ }
+#endif
+}
+
+static int mxt_init_write_config(struct mxt_data *data,
+ u8 type, const u8 *cfg)
+{
+ struct mxt_object *object;
+ u8 *temp;
+ int ret;
+
+ object = mxt_get_object(data, type);
+ if (!object)
+ return -EINVAL;
+
+ if ((object->size == 0) || (object->start_address == 0)) {
+ dev_err(&data->client->dev,
+ "%s error object_type T%d\n", __func__, type);
+ return -ENODEV;
+ }
+
+ ret = mxt_write_mem(data, object->start_address,
+ object->size, cfg);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "%s write error T%d address[0x%x]\n",
+ __func__, type, object->start_address);
+ return ret;
+ }
+
+ if (mxt_check_instance(data, type)) {
+ temp = kzalloc(object->size, GFP_KERNEL);
+
+ if (temp == NULL)
+ return -ENOMEM;
+
+ ret |= mxt_write_mem(data, object->start_address + object->size,
+ object->size, temp);
+ kfree(temp);
+ }
+
+ return ret;
+}
+
+static int mxt_write_config_from_pdata(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ u8 **tsp_config = (u8 **)data->pdata->config;
+ u8 i;
+ int ret;
+
+ if (!tsp_config) {
+ dev_info(dev, "No cfg data in pdata\n");
+ return 0;
+ }
+
+ for (i = 0; tsp_config[i][0] != RESERVED_T255; i++) {
+ ret = mxt_init_write_config(data, tsp_config[i][0],
+ tsp_config[i] + 1);
+ if (ret)
+ return ret;
+
+ if (tsp_config[i][0] == TOUCH_MULTITOUCHSCREEN_T9) {
+ /* Are x and y inverted? */
+ if (tsp_config[i][10] & 0x1) {
+ data->x_dropbits =
+ (!(tsp_config[i][22] & 0xC)) << 1;
+ data->y_dropbits =
+ (!(tsp_config[i][20] & 0xC)) << 1;
+ } else {
+ data->x_dropbits =
+ (!(tsp_config[i][20] & 0xC)) << 1;
+ data->y_dropbits =
+ (!(tsp_config[i][22] & 0xC)) << 1;
+ }
+ }
+ }
+ return ret;
+}
+
+#if DUAL_CFG
+static int mxt_write_config(struct mxt_fw_info *fw_info)
+{
+ struct mxt_data *data = fw_info->data;
+ struct device *dev = &data->client->dev;
+ struct mxt_object *object;
+ struct mxt_cfg_data *cfg_data;
+ u32 current_crc;
+ u8 i, val = 0;
+ u16 reg, index;
+ int ret;
+ u32 cfg_length = data->cfg_len = fw_info->cfg_len / 2 ;
+
+ if (!fw_info->ta_cfg_raw_data && !fw_info->batt_cfg_raw_data) {
+ dev_info(dev, "No cfg data in file\n");
+ ret = mxt_write_config_from_pdata(data);
+ return ret;
+ }
+
+ /* Get config CRC from device */
+ ret = mxt_read_config_crc(data, &current_crc);
+ if (ret)
+ return ret;
+
+ /* Check Version information */
+ if (fw_info->fw_ver != data->info.version) {
+ dev_err(dev, "Warning: version mismatch! %s\n", __func__);
+ return 0;
+ }
+ if (fw_info->build_ver != data->info.build) {
+ dev_err(dev, "Warning: build num mismatch! %s\n", __func__);
+ return 0;
+ }
+
+ /* Check config CRC */
+ if (current_crc == fw_info->cfg_crc) {
+ dev_info(dev, "Skip writing Config:[CRC 0x%06X]\n",
+ current_crc);
+ return 0;
+ }
+
+ dev_info(dev, "Writing Config:[CRC 0x%06X!=0x%06X]\n",
+ current_crc, fw_info->cfg_crc);
+
+ /* Get the address of configuration data */
+ data->batt_cfg_raw_data = fw_info->batt_cfg_raw_data;
+ data->ta_cfg_raw_data = fw_info->ta_cfg_raw_data =
+ fw_info->batt_cfg_raw_data + cfg_length;
+
+ /* Write config info */
+ for (index = 0; index < cfg_length;) {
+ if (index + sizeof(struct mxt_cfg_data) >= cfg_length) {
+ dev_err(dev, "index(%d) of cfg_data exceeded total size(%d)!!\n",
+ index + sizeof(struct mxt_cfg_data),
+ cfg_length);
+ return -EINVAL;
+ }
+
+ /* Get the info about each object */
+ if (data->charging_mode)
+ cfg_data = (struct mxt_cfg_data *)
+ (&fw_info->ta_cfg_raw_data[index]);
+ else
+ cfg_data = (struct mxt_cfg_data *)
+ (&fw_info->batt_cfg_raw_data[index]);
+
+ index += sizeof(struct mxt_cfg_data) + cfg_data->size;
+ if (index > cfg_length) {
+ dev_err(dev, "index(%d) of cfg_data exceeded total size(%d) in T%d object!!\n",
+ index, cfg_length, cfg_data->type);
+ return -EINVAL;
+ }
+
+ object = mxt_get_object(data, cfg_data->type);
+ if (!object) {
+ dev_err(dev, "T%d is Invalid object type\n",
+ cfg_data->type);
+ return -EINVAL;
+ }
+
+ /* Check and compare the size, instance of each object */
+ if (cfg_data->size > object->size) {
+ dev_err(dev, "T%d Object length exceeded!\n",
+ cfg_data->type);
+ return -EINVAL;
+ }
+ if (cfg_data->instance >= object->instances) {
+ dev_err(dev, "T%d Object instances exceeded!\n",
+ cfg_data->type);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Writing config for obj %d len %d instance %d (%d/%d)\n",
+ cfg_data->type, object->size,
+ cfg_data->instance, index, cfg_length);
+
+ reg = object->start_address + object->size * cfg_data->instance;
+
+ /* Write register values of each object */
+ ret = mxt_write_mem(data, reg, cfg_data->size,
+ cfg_data->register_val);
+ if (ret) {
+ dev_err(dev, "Write T%d Object failed\n",
+ object->object_type);
+ return ret;
+ }
+
+ /*
+ * 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 a config
+ * download every time until the configuration is updated.
+ */
+ if (cfg_data->size < object->size) {
+ dev_err(dev, "Warning: zeroing %d byte(s) in T%d\n",
+ object->size - cfg_data->size, cfg_data->type);
+
+ for (i = cfg_data->size + 1; i < object->size; i++) {
+ ret = mxt_write_mem(data, reg + i, 1, &val);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+ dev_info(dev, "Updated configuration\n");
+
+ return ret;
+}
+#else
+static int mxt_write_config(struct mxt_fw_info *fw_info)
+{
+ struct mxt_data *data = fw_info->data;
+ struct device *dev = &data->client->dev;
+ struct mxt_object *object;
+ struct mxt_cfg_data *cfg_data;
+ u32 current_crc;
+ u8 i, val = 0;
+ u16 reg, index;
+ int ret;
+
+ if (!fw_info->cfg_raw_data) {
+ dev_info(dev, "No cfg data in file\n");
+ ret = mxt_write_config_from_pdata(data);
+ return ret;
+ }
+
+ /* Get config CRC from device */
+ ret = mxt_read_config_crc(data, &current_crc);
+ if (ret)
+ return ret;
+
+ /* Check Version information */
+ if (fw_info->fw_ver != data->info.version) {
+ dev_err(dev, "Warning: version mismatch! %s\n", __func__);
+ return 0;
+ }
+ if (fw_info->build_ver != data->info.build) {
+ dev_err(dev, "Warning: build num mismatch! %s\n", __func__);
+ return 0;
+ }
+
+ /* Check config CRC */
+ if (current_crc == fw_info->cfg_crc) {
+ dev_info(dev, "Skip writing Config:[CRC 0x%06X]\n",
+ current_crc);
+ return 0;
+ }
+
+ dev_info(dev, "Writing Config:[CRC 0x%06X!=0x%06X]\n",
+ current_crc, fw_info->cfg_crc);
+
+ /* Write config info */
+ for (index = 0; index < fw_info->cfg_len;) {
+
+ if (index + sizeof(struct mxt_cfg_data) >= fw_info->cfg_len) {
+ dev_err(dev, "index(%d) of cfg_data exceeded total size(%d)!!\n",
+ index + sizeof(struct mxt_cfg_data),
+ fw_info->cfg_len);
+ return -EINVAL;
+ }
+
+ /* Get the info about each object */
+ cfg_data = (struct mxt_cfg_data *)
+ (&fw_info->cfg_raw_data[index]);
+
+ index += sizeof(struct mxt_cfg_data) + cfg_data->size;
+ if (index > fw_info->cfg_len) {
+ dev_err(dev, "index(%d) of cfg_data exceeded total size(%d) in T%d object!!\n",
+ index, fw_info->cfg_len, cfg_data->type);
+ return -EINVAL;
+ }
+
+ object = mxt_get_object(data, cfg_data->type);
+ if (!object) {
+ dev_err(dev, "T%d is Invalid object type\n",
+ cfg_data->type);
+ return -EINVAL;
+ }
+
+ /* Check and compare the size, instance of each object */
+ if (cfg_data->size > object->size) {
+ dev_err(dev, "T%d Object length exceeded!\n",
+ cfg_data->type);
+ return -EINVAL;
+ }
+ if (cfg_data->instance >= object->instances) {
+ dev_err(dev, "T%d Object instances exceeded!\n",
+ cfg_data->type);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Writing config for obj %d len %d instance %d (%d/%d)\n",
+ cfg_data->type, object->size,
+ cfg_data->instance, index, fw_info->cfg_len);
+
+ reg = object->start_address + object->size * cfg_data->instance;
+
+ /* Write register values of each object */
+ ret = mxt_write_mem(data, reg, cfg_data->size,
+ cfg_data->register_val);
+ if (ret) {
+ dev_err(dev, "Write T%d Object failed\n",
+ object->object_type);
+ return ret;
+ }
+
+ /*
+ * 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 a config
+ * download every time until the configuration is updated.
+ */
+ if (cfg_data->size < object->size) {
+ dev_err(dev, "Warning: zeroing %d byte(s) in T%d\n",
+ object->size - cfg_data->size, cfg_data->type);
+
+ for (i = cfg_data->size + 1; i < object->size; i++) {
+ ret = mxt_write_mem(data, reg + i, 1, &val);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+ dev_info(dev, "Updated configuration\n");
+
+ return ret;
+}
+#endif
+
+
+static int mxt_calibrate_chip(struct mxt_data *data)
+{
+ u8 cal_data = 1;
+ int ret = 0;
+ /* send calibration command to the chip */
+ ret = mxt_write_mem(data,
+ data->cmd_proc + CMD_CALIBRATE_OFFSET,
+ 1, &cal_data);
+ if (!ret)
+ dev_info(&data->client->dev, "success sending calibration cmd!!!\n");
+ return ret;
+}
+
+#if TSP_INFORM_CHARGER
+static int set_charger_config(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ struct mxt_object *object;
+ struct mxt_cfg_data *cfg_data;
+ u8 i, val = 0;
+ u16 reg, index;
+ int ret;
+
+ dev_dbg(dev, "set_charger_config data->cfg_len = %d\n", data->cfg_len);
+
+ for (index = 0; index < data->cfg_len;) {
+ if (index + sizeof(struct mxt_cfg_data) >= data->cfg_len) {
+ dev_err(dev, "index(%d) of cfg_data exceeded total size(%d)!!\n",
+ index + sizeof(struct mxt_cfg_data),
+ data->cfg_len);
+ return -EINVAL;
+ }
+
+ /* Get the info about each object */
+ if (data->charging_mode)
+ cfg_data = (struct mxt_cfg_data *)
+ (&data->ta_cfg_raw_data[index]);
+ else
+ cfg_data = (struct mxt_cfg_data *)
+ (&data->batt_cfg_raw_data[index]);
+
+ index += sizeof(struct mxt_cfg_data) + cfg_data->size;
+ if (index > data->cfg_len) {
+ dev_err(dev, "index(%d) of cfg_data exceeded total size(%d) in T%d object!!\n",
+ index, data->cfg_len, cfg_data->type);
+ return -EINVAL;
+ }
+
+ object = mxt_get_object(data, cfg_data->type);
+ if (!object) {
+ dev_err(dev, "T%d is Invalid object type\n",
+ cfg_data->type);
+ return -EINVAL;
+ }
+
+ /* Check and compare the size, instance of each object */
+ if (cfg_data->size > object->size) {
+ dev_err(dev, "T%d Object length exceeded!\n",
+ cfg_data->type);
+ return -EINVAL;
+ }
+ if (cfg_data->instance >= object->instances) {
+ dev_err(dev, "T%d Object instances exceeded!\n",
+ cfg_data->type);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Writing config for obj %d len %d instance %d (%d/%d)\n",
+ cfg_data->type, object->size,
+ cfg_data->instance, index, data->cfg_len);
+
+ reg = object->start_address + object->size * cfg_data->instance;
+
+ /* Write register values of each object */
+ ret = mxt_write_mem(data, reg, cfg_data->size,
+ cfg_data->register_val);
+ if (ret) {
+ dev_err(dev, "Write T%d Object failed\n",
+ object->object_type);
+ return ret;
+ }
+
+ /*
+ * 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 a config
+ * download every time until the configuration is updated.
+ */
+ if (cfg_data->size < object->size) {
+ dev_err(dev, "Warning: zeroing %d byte(s) in T%d\n",
+ object->size - cfg_data->size, cfg_data->type);
+
+ for (i = cfg_data->size + 1; i < object->size; i++) {
+ ret = mxt_write_mem(data, reg + i, 1, &val);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void inform_charger(struct mxt_callbacks *cb,
+ bool en)
+{
+ struct mxt_data *data = container_of(cb,
+ struct mxt_data, callbacks);
+
+ cancel_delayed_work_sync(&data->noti_dwork);
+ data->charging_mode = en;
+ schedule_delayed_work(&data->noti_dwork, HZ / 5);
+}
+
+static void charger_noti_dwork(struct work_struct *work)
+{
+ struct mxt_data *data =
+ container_of(work, struct mxt_data,
+ noti_dwork.work);
+
+ if (!data->mxt_enabled) {
+ schedule_delayed_work(&data->noti_dwork, HZ / 5);
+ return ;
+ }
+
+ dev_info(&data->client->dev,
+ "%s mode\n",
+ data->charging_mode ? "charging" : "battery");
+
+ set_charger_config(data);
+}
+
+static void inform_charger_init(struct mxt_data *data)
+{
+ INIT_DELAYED_WORK(&data->noti_dwork, charger_noti_dwork);
+}
+#endif
+
+static void mxt_report_input_data(struct mxt_data *data)
+{
+ int i;
+ int count = 0;
+ int report_count = 0;
+
+ for (i = 0; i < data->num_fingers; i++) {
+ if (data->fingers[i].state == MXT_STATE_INACTIVE)
+ continue;
+
+ if (data->fingers[i].state == MXT_STATE_RELEASE) {
+ input_mt_slot(data->input_dev, i);
+ input_mt_report_slot_state(data->input_dev,
+ MT_TOOL_FINGER, false);
+ } else {
+ input_mt_slot(data->input_dev, i);
+ input_mt_report_slot_state(data->input_dev,
+ MT_TOOL_FINGER, true);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X,
+ data->fingers[i].x);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
+ data->fingers[i].y);
+ input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
+ data->fingers[i].w);
+ input_report_abs(data->input_dev, ABS_MT_PRESSURE,
+ data->fingers[i].z);
+#if TSP_USE_SHAPETOUCH
+ input_report_abs(data->input_dev, ABS_MT_COMPONENT,
+ data->fingers[i].component);
+ input_report_abs(data->input_dev, ABS_MT_SUMSIZE,
+ data->sumsize);
+#endif
+ }
+ report_count++;
+
+#if TSP_DEBUG_INFO
+ if (data->fingers[i].state == MXT_STATE_PRESS)
+ dev_info(&data->client->dev, "P: id[%d] X[%d],Y[%d]\n",
+ i, data->fingers[i].x, data->fingers[i].y);
+#else
+ if (data->fingers[i].state == MXT_STATE_PRESS)
+ dev_info(&data->client->dev, "P: id[%d]\n", i);
+#endif
+ else if (data->fingers[i].state == MXT_STATE_RELEASE)
+ dev_info(&data->client->dev, "R: id[%d] M[%d]\n",
+ i, data->fingers[i].mcount);
+
+ if (data->fingers[i].state == MXT_STATE_RELEASE) {
+ data->fingers[i].state = MXT_STATE_INACTIVE;
+ data->fingers[i].mcount = 0;
+ } else {
+ data->fingers[i].state = MXT_STATE_MOVE;
+ count++;
+ }
+ }
+
+ if (report_count > 0) {
+#if TSP_ITDEV
+ if (!data->driver_paused)
+#endif
+ input_sync(data->input_dev);
+ }
+
+#if (TSP_USE_SHAPETOUCH || TSP_BOOSTER)
+ /* all fingers are released */
+ if (count == 0) {
+#if TSP_USE_SHAPETOUCH
+ data->sumsize = 0;
+#endif
+#if TSP_BOOSTER
+ mxt_set_dvfs_on(data, false);
+#endif
+ }
+#endif
+
+ data->finger_mask = 0;
+}
+
+static void mxt_treat_T6_object(struct mxt_data *data, u8 *msg)
+{
+ /* normal mode */
+ if (msg[1] == 0x00)
+ dev_info(&data->client->dev, "normal mode\n");
+ /* I2C checksum error */
+ if (msg[1] & 0x04)
+ dev_err(&data->client->dev, "I2C checksum error\n");
+ /* config error */
+ if (msg[1] & 0x08)
+ dev_err(&data->client->dev, "config error\n");
+ /* calibration */
+ if (msg[1] & 0x10)
+ dev_info(&data->client->dev, "calibration is on going !!\n");
+ /* signal error */
+ if (msg[1] & 0x20)
+ dev_err(&data->client->dev, "signal error\n");
+ /* overflow */
+ if (msg[1] & 0x40)
+ dev_err(&data->client->dev, "overflow detected\n");
+ /* reset */
+ if (msg[1] & 0x80) {
+ dev_info(&data->client->dev, "reset is ongoing\n");
+#if TSP_INFORM_CHARGER
+ if (data->charging_mode)
+ set_charger_config(data);
+#endif
+#if TSP_SEC_SYSFS
+ data->sysfs_data->current_crc = msg[2]
+ | (msg[3] << 8) | (msg[4] << 16);
+ dev_dbg(&data->client->dev, "CRC [0x%06X]\n",
+ data->sysfs_data->current_crc);
+#endif
+ }
+}
+
+static void mxt_treat_T9_object(struct mxt_data *data, u8 *msg)
+{
+ int id;
+
+ id = msg[0] - data->finger_report_id;
+
+ /* If not a touch event, return */
+ if (id < 0 || id >= data->num_fingers)
+ return;
+
+ if (msg[1] & RELEASE_MSG_MASK) {
+ data->fingers[id].z = 0;
+ data->fingers[id].w = msg[5];
+ data->fingers[id].state = MXT_STATE_RELEASE;
+ mxt_report_input_data(data);
+ } else if ((msg[1] & DETECT_MSG_MASK)
+ && (msg[1] & (PRESS_MSG_MASK | MOVE_MSG_MASK))) {
+ data->fingers[id].z = msg[6];
+ data->fingers[id].w = msg[5];
+ data->fingers[id].x = (((msg[2] << 4) | (msg[4] >> 4))
+ >> data->x_dropbits);
+ data->fingers[id].y = (((msg[3] << 4) | (msg[4] & 0xF))
+ >> data->y_dropbits);
+
+#if TSP_USE_SHAPETOUCH
+ data->fingers[id].component = msg[7];
+#endif
+ data->finger_mask |= 1U << id;
+
+ if (msg[1] & PRESS_MSG_MASK) {
+ data->fingers[id].state = MXT_STATE_PRESS;
+ data->fingers[id].mcount = 0;
+ } else if (msg[1] & MOVE_MSG_MASK) {
+ data->fingers[id].mcount += 1;
+ }
+
+#if TSP_BOOSTER
+ mxt_set_dvfs_on(data, true);
+#endif
+ } else if ((msg[1] & SUPPRESS_MSG_MASK)
+ && (data->fingers[id].state != MXT_STATE_INACTIVE)) {
+ data->fingers[id].z = 0;
+ data->fingers[id].w = msg[5];
+ data->fingers[id].state = MXT_STATE_RELEASE;
+ data->finger_mask |= 1U << id;
+ } else {
+ /* ignore changed amplitude and vector messsage */
+ if (!((msg[1] & DETECT_MSG_MASK)
+ && (msg[1] & AMPLITUDE_MSG_MASK
+ || msg[1] & VECTOR_MSG_MASK)))
+ dev_err(&data->client->dev, "Unknown state %#02x %#02x\n",
+ msg[0], msg[1]);
+ }
+}
+
+static void mxt_treat_T42_object(struct mxt_data *data, u8 *msg)
+{
+ if (msg[1] & 0x01)
+ /* Palm Press */
+ dev_info(&data->client->dev, "palm touch detected\n");
+ else
+ /* Palm release */
+ dev_info(&data->client->dev, "palm touch released\n");
+}
+
+static void mxt_treat_T57_object(struct mxt_data *data, u8 *msg)
+{
+#if TSP_USE_SHAPETOUCH
+ data->sumsize = msg[1] + (msg[2] << 8);
+#endif /* TSP_USE_SHAPETOUCH */
+
+}
+static irqreturn_t mxt_irq_thread(int irq, void *ptr)
+{
+ struct mxt_data *data = ptr;
+ u8 msg[data->msg_object_size];
+ u8 object_type, instance;
+
+ do {
+ if (mxt_read_mem(data, data->msg_proc, sizeof(msg), msg))
+ return IRQ_HANDLED;
+
+#if TSP_ITDEV
+ if (data->debug_enabled)
+ print_hex_dump(KERN_INFO, "MXT MSG:",
+ DUMP_PREFIX_NONE, 16, 1,
+ msg, sizeof(msg), false);
+#endif /* TSP_ITDEV */
+ object_type = mxt_reportid_to_type(data, msg[0] , &instance);
+
+ if (object_type == RESERVED_T0)
+ continue;
+
+ switch (object_type) {
+ case GEN_COMMANDPROCESSOR_T6:
+ mxt_treat_T6_object(data, msg);
+ break;
+ case TOUCH_MULTITOUCHSCREEN_T9:
+ mxt_treat_T9_object(data, msg);
+ break;
+ case PROCI_TOUCHSUPPRESSION_T42:
+ mxt_treat_T42_object(data, msg);
+ break;
+
+ case PROCI_EXTRATOUCHSCREENDATA_T57:
+ mxt_treat_T57_object(data, msg);
+ break;
+ default:
+ dev_err(&data->client->dev,
+ "Untreated Object type[%d]\tmessage[0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x]\n",
+ object_type, msg[0], msg[1], msg[2],
+ msg[3], msg[4], msg[5], msg[6], msg[7], msg[8]);
+ break;
+ }
+ } while (!gpio_get_value(data->pdata->gpio_read_done));
+
+ if (data->finger_mask)
+ mxt_report_input_data(data);
+
+ return IRQ_HANDLED;
+}
+
+/* This API should be deleted later */
+/* dummy API for cypress touchkey */
+int get_tsp_status(void)
+{
+ return 0;
+}
+EXPORT_SYMBOL(get_tsp_status);
+
+static int mxt_internal_suspend(struct mxt_data *data)
+{
+ int i;
+ int count = 0;
+
+ for (i = 0; i < data->num_fingers; i++) {
+ if (data->fingers[i].state == MXT_STATE_INACTIVE)
+ continue;
+ data->fingers[i].z = 0;
+ data->fingers[i].state = MXT_STATE_RELEASE;
+ count++;
+ }
+ if (count)
+ mxt_report_input_data(data);
+
+#if TSP_BOOSTER
+ mxt_set_dvfs_on(data, false);
+#endif
+
+ data->pdata->power_off();
+
+ return 0;
+}
+
+static int mxt_internal_resume(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int ret;
+
+ data->pdata->power_on();
+
+ /* Soft reset */
+ ret = mxt_reset(data);
+ if (ret) {
+ dev_err(dev, "Failed Reset IC\n");
+ goto out;
+ }
+ msleep(MXT_540S_SW_RESET_TIME);
+
+out:
+ return ret;
+}
+
+static int mxt_get_bootloader_version(struct i2c_client *client, u8 val)
+{
+ u8 buf[3];
+
+ if (val & MXT_BOOT_EXTENDED_ID) {
+ if (i2c_master_recv(client, buf, sizeof(buf)) != sizeof(buf)) {
+ dev_err(&client->dev, "%s: i2c recv failed\n",
+ __func__);
+ return -EIO;
+ }
+ dev_info(&client->dev, "Bootloader ID:%d Version:%d",
+ buf[1], buf[2]);
+ } else {
+ dev_info(&client->dev, "Bootloader ID:%d",
+ val & MXT_BOOT_ID_MASK);
+ }
+ return 0;
+}
+
+static int mxt_check_bootloader(struct i2c_client *client,
+ unsigned int state)
+{
+ u8 val;
+
+recheck:
+ if (i2c_master_recv(client, &val, 1) != 1) {
+ dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
+ return -EIO;
+ }
+
+ switch (state) {
+ case MXT_WAITING_BOOTLOAD_CMD:
+ if (mxt_get_bootloader_version(client, val))
+ return -EIO;
+ 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)
+ goto recheck;
+ if (val == MXT_FRAME_CRC_FAIL) {
+ dev_err(&client->dev, "Bootloader CRC fail\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (val != state) {
+ dev_err(&client->dev,
+ "Invalid bootloader mode state 0x%X\n", val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mxt_unlock_bootloader(struct i2c_client *client)
+{
+ u8 buf[2] = {MXT_UNLOCK_CMD_LSB, MXT_UNLOCK_CMD_MSB};
+
+ if (i2c_master_send(client, buf, 2) != 2) {
+ dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mxt_probe_bootloader(struct i2c_client *client)
+{
+ u8 val;
+
+ if (i2c_master_recv(client, &val, 1) != 1) {
+ dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
+ return -EIO;
+ }
+
+ if (val & (~MXT_BOOT_STATUS_MASK)) {
+ if (val & MXT_APP_CRC_FAIL)
+ dev_err(&client->dev, "Application CRC failure\n");
+ else
+ dev_err(&client->dev, "Device in bootloader mode\n");
+ } else {
+ dev_err(&client->dev, "%s: Unknow status\n", __func__);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int mxt_fw_write(struct i2c_client *client,
+ const u8 *frame_data, unsigned int frame_size)
+{
+ if (i2c_master_send(client, frame_data, frame_size) != frame_size) {
+ dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+#if DUAL_CFG
+int mxt_verify_fw(struct mxt_fw_info *fw_info, const struct firmware *fw)
+{
+ struct mxt_data *data = fw_info->data;
+ struct device *dev = &data->client->dev;
+ struct mxt_fw_image *fw_img;
+
+ if (!fw) {
+ dev_err(dev, "could not find firmware file\n");
+ return -ENOENT;
+ }
+
+ fw_img = (struct mxt_fw_image *)fw->data;
+
+ if (le32_to_cpu(fw_img->magic_code) != MXT_FW_MAGIC) {
+ /* In case, firmware file only consist of firmware */
+ dev_info(dev, "Firmware file only consist of raw firmware\n");
+ fw_info->fw_len = fw->size;
+ fw_info->fw_raw_data = fw->data;
+ } else {
+ /*
+ * In case, firmware file consist of header,
+ * configuration, firmware.
+ */
+ dev_info(dev, "Firmware file consist of header, configuration, firmware\n");
+ fw_info->fw_ver = fw_img->fw_ver;
+ fw_info->build_ver = fw_img->build_ver;
+ fw_info->hdr_len = le32_to_cpu(fw_img->hdr_len);
+ fw_info->cfg_len = le32_to_cpu(fw_img->cfg_len);
+ fw_info->fw_len = le32_to_cpu(fw_img->fw_len);
+ fw_info->cfg_crc = le32_to_cpu(fw_img->cfg_crc);
+
+ /* Check the firmware file with header */
+ if (fw_info->hdr_len != sizeof(struct mxt_fw_image)
+ || fw_info->hdr_len + fw_info->cfg_len
+ + fw_info->fw_len != fw->size) {
+ dev_err(dev, "Firmware file is invaild !!hdr size[%d] cfg,fw size[%d,%d] filesize[%d]\n",
+ fw_info->hdr_len, fw_info->cfg_len,
+ fw_info->fw_len, fw->size);
+ return -EINVAL;
+ }
+
+ if (!fw_info->cfg_len) {
+ dev_err(dev, "Firmware file dose not include configuration data\n");
+ return -EINVAL;
+ }
+ if (!fw_info->fw_len) {
+ dev_err(dev, "Firmware file dose not include raw firmware data\n");
+ return -EINVAL;
+ }
+
+ /* Get the address of configuration data */
+ data->cfg_len = fw_info->cfg_len / 2;
+ data->batt_cfg_raw_data = fw_info->batt_cfg_raw_data
+ = fw_img->data;
+ data->ta_cfg_raw_data = fw_info->ta_cfg_raw_data
+ = fw_img->data + (fw_info->cfg_len / 2) ;
+
+ /* Get the address of firmware data */
+ fw_info->fw_raw_data = fw_img->data + fw_info->cfg_len;
+
+#if TSP_SEC_SYSFS
+ data->sysfs_data->fw_ver = fw_info->fw_ver;
+ data->sysfs_data->build_ver = fw_info->build_ver;
+#endif
+ }
+
+ return 0;
+}
+#else
+int mxt_verify_fw(struct mxt_fw_info *fw_info, const struct firmware *fw)
+{
+ struct mxt_data *data = fw_info->data;
+ struct device *dev = &data->client->dev;
+ struct mxt_fw_image *fw_img;
+
+ if (!fw) {
+ dev_err(dev, "could not find firmware file\n");
+ return -ENOENT;
+ }
+
+ fw_img = (struct mxt_fw_image *)fw->data;
+
+ if (le32_to_cpu(fw_img->magic_code) != MXT_FW_MAGIC) {
+ /* In case, firmware file only consist of firmware */
+ dev_dbg(dev, "Firmware file only consist of raw firmware\n");
+ fw_info->fw_len = fw->size;
+ fw_info->fw_raw_data = fw->data;
+ } else {
+ /*
+ * In case, firmware file consist of header,
+ * configuration, firmware.
+ */
+ dev_dbg(dev, "Firmware file consist of header, configuration, firmware\n");
+ fw_info->fw_ver = fw_img->fw_ver;
+ fw_info->build_ver = fw_img->build_ver;
+ fw_info->hdr_len = le32_to_cpu(fw_img->hdr_len);
+ fw_info->cfg_len = le32_to_cpu(fw_img->cfg_len);
+ fw_info->fw_len = le32_to_cpu(fw_img->fw_len);
+ fw_info->cfg_crc = le32_to_cpu(fw_img->cfg_crc);
+
+ /* Check the firmware file with header */
+ if (fw_info->hdr_len != sizeof(struct mxt_fw_image)
+ || fw_info->hdr_len + fw_info->cfg_len
+ + fw_info->fw_len != fw->size) {
+ dev_err(dev, "Firmware file is invaild !!hdr size[%d] cfg,fw size[%d,%d] filesize[%d]\n",
+ fw_info->hdr_len, fw_info->cfg_len,
+ fw_info->fw_len, fw->size);
+ return -EINVAL;
+ }
+
+ if (!fw_info->cfg_len) {
+ dev_err(dev, "Firmware file dose not include configuration data\n");
+ return -EINVAL;
+ }
+ if (!fw_info->fw_len) {
+ dev_err(dev, "Firmware file dose not include raw firmware data\n");
+ return -EINVAL;
+ }
+
+ /* Get the address of configuration data */
+ fw_info->cfg_raw_data = fw_img->data;
+
+ /* Get the address of firmware data */
+ fw_info->fw_raw_data = fw_img->data + fw_info->cfg_len;
+
+#if TSP_SEC_SYSFS
+ data->sysfs_data->fw_ver = fw_info->fw_ver;
+ data->sysfs_data->build_ver = fw_info->build_ver;
+#endif
+ }
+
+ return 0;
+}
+#endif
+
+static int mxt_flash_fw(struct mxt_fw_info *fw_info)
+{
+ struct mxt_data *data = fw_info->data;
+ struct i2c_client *client = data->client_boot;
+ struct device *dev = &data->client->dev;
+ const u8 *fw_data = fw_info->fw_raw_data;
+ size_t fw_size = fw_info->fw_len;
+ unsigned int frame_size;
+ unsigned int pos = 0;
+ int ret;
+
+ if (!fw_data) {
+ dev_err(dev, "firmware data is Null\n");
+ return -ENOMEM;
+ }
+
+ ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD);
+ if (ret) {
+ /*may still be unlocked from previous update attempt */
+ ret = mxt_check_bootloader(client, MXT_WAITING_FRAME_DATA);
+ if (ret)
+ goto out;
+ } else {
+ dev_info(dev, "Unlocking bootloader\n");
+ /* Unlock bootloader */
+ mxt_unlock_bootloader(client);
+ }
+ while (pos < fw_size) {
+ ret = mxt_check_bootloader(client,
+ MXT_WAITING_FRAME_DATA);
+ if (ret) {
+ dev_err(dev, "Fail updating firmware. wating_frame_data err\n");
+ goto out;
+ }
+
+ 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.
+ */
+
+ frame_size += 2;
+
+ /* Write one frame to device */
+ mxt_fw_write(client, fw_data + pos, frame_size);
+
+ ret = mxt_check_bootloader(client,
+ MXT_FRAME_CRC_PASS);
+ if (ret) {
+ dev_err(dev, "Fail updating firmware. frame_crc err\n");
+ goto out;
+ }
+
+ pos += frame_size;
+
+ dev_dbg(dev, "Updated %d bytes / %zd bytes\n",
+ pos, fw_size);
+
+ msleep(20);
+ }
+ dev_info(dev, "success updating firmware\n");
+
+ msleep(MXT_540S_FW_RESET_TIME);
+out:
+ return ret;
+}
+
+int mxt_initialize(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+
+ u32 read_info_crc, calc_info_crc;
+ int ret;
+
+ ret = mxt_get_id_info(data);
+ if (ret)
+ return ret;
+
+ data->objects = kcalloc(data->info.object_num,
+ sizeof(*data->objects),
+ GFP_KERNEL);
+ if (!data->objects) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Get object table */
+ ret = mxt_get_object_table(data);
+ if (ret)
+ goto out;
+
+ data->rid_map = kcalloc(data->max_report_id + 1,
+ sizeof(*data->rid_map),
+ GFP_KERNEL);
+
+ if (!data->rid_map) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Make report id table */
+ mxt_make_reportid_table(data);
+
+ /* Verify the info CRC */
+ ret = mxt_read_info_crc(data, &read_info_crc);
+ if (ret)
+ goto out;
+
+ ret = mxt_calculate_infoblock_crc(data, &calc_info_crc);
+ if (ret)
+ goto out;
+
+ if (read_info_crc != calc_info_crc) {
+ dev_err(&data->client->dev, "CRC error :[CRC 0x%06X!=0x%06X]\n",
+ read_info_crc, calc_info_crc);
+ ret = -EFAULT;
+ goto out;
+ }
+ return 0;
+
+out:
+ return ret;
+}
+
+static void mxt_handle_init_data(struct mxt_data *data)
+{
+ const struct mxt_platform_data *pdata = data->pdata;
+ int ret;
+ u8 val;
+
+ /* Set touchscreen resolution */
+ mxt_write_object(data, TOUCH_MULTITOUCHSCREEN_T9,
+ MXT_T9_XRANGE_LSB, (pdata->max_x) & 0xff);
+ mxt_write_object(data, TOUCH_MULTITOUCHSCREEN_T9,
+ MXT_T9_XRANGE_MSB, (pdata->max_x) >> 8);
+ mxt_write_object(data, TOUCH_MULTITOUCHSCREEN_T9,
+ MXT_T9_YRANGE_LSB, (pdata->max_y) & 0xff);
+ mxt_write_object(data, TOUCH_MULTITOUCHSCREEN_T9,
+ MXT_T9_YRANGE_MSB, (pdata->max_y) >> 8);
+
+ /* Get acquistion time */
+ ret = mxt_read_object(data, TOUCH_MULTITOUCHSCREEN_T9,
+ MXT_T9_CTRL, &val);
+
+ if (ret)
+ data->tsp_ctrl = 0x83;
+ else
+ data->tsp_ctrl = (val > 0) ? val : 0x83;
+
+ dev_info(&data->client->dev, "T9 CTRL : %d", data->tsp_ctrl);
+}
+
+int mxt_rest_initialize(struct mxt_fw_info *fw_info)
+{
+ struct mxt_data *data = fw_info->data;
+ struct device *dev = &data->client->dev;
+ int ret;
+
+ /* Write config */
+ ret = mxt_write_config(fw_info);
+ if (ret) {
+ dev_err(dev, "Failed to write config from file\n");
+ goto out;
+ }
+
+ /* Handle data for init */
+ mxt_handle_init_data(data);
+
+ /* Backup to memory */
+ ret = mxt_backup(data);
+ if (ret) {
+ dev_err(dev, "Failed backup NV data\n");
+ goto out;
+ }
+
+ /* Soft reset */
+ ret = mxt_reset(data);
+ if (ret) {
+ dev_err(dev, "Failed Reset IC\n");
+ goto out;
+ }
+ msleep(MXT_540S_SW_RESET_TIME);
+
+out:
+ return ret;
+}
+
+static int mxt_enter_bootloader(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int error;
+
+ data->objects = kcalloc(data->info.object_num,
+ sizeof(struct mxt_object),
+ GFP_KERNEL);
+ if (!data->objects) {
+ dev_err(dev, "%s Failed to allocate memory\n",
+ __func__);
+ error = -ENOMEM;
+ goto out;
+ }
+
+ /* Get object table information*/
+ error = mxt_get_object_table(data);
+ if (error)
+ goto err_free_mem;
+
+ /* Change to the bootloader mode */
+ mxt_write_object(data, GEN_COMMANDPROCESSOR_T6,
+ CMD_RESET_OFFSET, MXT_BOOT_VALUE);
+
+ msleep(MXT_540S_SW_RESET_TIME);
+
+err_free_mem:
+ kfree(data->objects);
+ data->objects = NULL;
+
+out:
+ return error;
+}
+
+static int mxt_flash_fw_on_probe(struct mxt_fw_info *fw_info)
+{
+ struct mxt_data *data = fw_info->data;
+ struct device *dev = &data->client->dev;
+ int error;
+
+ error = mxt_get_id_info(data);
+ if (error) {
+ /* need to check IC is in boot mode */
+ error = mxt_probe_bootloader(data->client_boot);
+ if (error) {
+ dev_err(dev, "Failed to verify bootloader's status\n");
+ goto out;
+ }
+
+ dev_info(dev, "Updating firmware from boot-mode\n");
+ goto load_fw;
+ }
+
+ /* compare the version to verify necessity of firmware updating */
+ if (data->info.version == fw_info->fw_ver
+ && data->info.build == fw_info->build_ver) {
+ dev_dbg(dev, "Firmware version is same with in IC\n");
+ goto out;
+ }
+
+ dev_info(dev, "Updating firmware from app-mode : IC:0x%x,0x%x =! FW:0x%x,0x%x\n",
+ data->info.version, data->info.build,
+ fw_info->fw_ver, fw_info->build_ver);
+
+ error = mxt_enter_bootloader(data);
+ if (error) {
+ dev_err(dev, "Failed updating firmware\n");
+ goto out;
+ }
+
+load_fw:
+ error = mxt_flash_fw(fw_info);
+ if (error)
+ dev_err(dev, "Failed updating firmware\n");
+ else
+ dev_info(dev, "succeeded updating firmware\n");
+out:
+ return error;
+}
+
+static int mxt_touch_finish_init(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+
+ error = request_threaded_irq(client->irq, NULL, mxt_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT, "mxt_ts", data);
+
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_req_irq;
+ }
+
+#if TSP_BOOSTER
+ error = mxt_init_dvfs(data);
+ if (error < 0) {
+ dev_err(&client->dev, "Fail get dvfs level for touch booster\n");
+ goto err_req_irq;
+ }
+#endif
+
+ /*
+ * to prevent unnecessary report of touch event
+ * it will be enabled in open function
+ */
+ mxt_stop(data);
+
+ dev_info(&client->dev, "Mxt touch controller initialized\n");
+
+ /* for blocking to be excuted open function untile finishing ts init */
+ complete_all(&data->init_done);
+ return 0;
+
+err_req_irq:
+ return error;
+}
+
+static int mxt_touch_rest_init(struct mxt_fw_info *fw_info)
+{
+ struct mxt_data *data = fw_info->data;
+ struct device *dev = &data->client->dev;
+ int error;
+
+ error = mxt_initialize(data);
+ if (error) {
+ dev_err(dev, "Failed to initialize\n");
+ goto err_free_mem;
+ }
+
+ error = mxt_rest_initialize(fw_info);
+ if (error) {
+ dev_err(dev, "Failed to rest initialize\n");
+ goto err_free_mem;
+ }
+
+ error = mxt_touch_finish_init(data);
+ if (error)
+ goto err_free_mem;
+
+ return 0;
+
+err_free_mem:
+ kfree(data->objects);
+ data->objects = NULL;
+ kfree(data->rid_map);
+ data->rid_map = NULL;
+ return error;
+}
+
+
+static void mxt_request_firmware_work(const struct firmware *fw,
+ void *context)
+{
+ struct mxt_data *data = context;
+ struct mxt_fw_info fw_info;
+ int error;
+
+ memset(&fw_info, 0, sizeof(struct mxt_fw_info));
+ fw_info.data = data;
+
+ error = mxt_verify_fw(&fw_info, fw);
+ if (error)
+ goto ts_rest_init;
+
+ /* Skip update on boot up if firmware file does not have a header */
+ if (!fw_info.hdr_len)
+ goto ts_rest_init;
+
+ error = mxt_flash_fw_on_probe(&fw_info);
+ if (error)
+ goto out;
+
+ts_rest_init:
+ error = mxt_touch_rest_init(&fw_info);
+out:
+ if (error)
+ /* complete anyway, so open() doesn't get blocked */
+ complete_all(&data->init_done);
+
+ release_firmware(fw);
+}
+
+static int __devinit mxt_touch_init(struct mxt_data *data, bool nowait)
+{
+ struct i2c_client *client = data->client;
+ const char *firmware_name = data->pdata->firmware_name ?: MXT_FW_NAME;
+ int ret = 0;
+
+ data->pdata->power_on();
+
+ if (nowait) {
+ const struct firmware *fw;
+ ret = request_firmware(&fw, firmware_name, &client->dev);
+ if (ret) {
+ dev_err(&client->dev,
+ "error requesting built-in firmware\n");
+ goto out;
+ }
+ mxt_request_firmware_work(fw, data);
+ } else {
+ ret = request_firmware_nowait(THIS_MODULE, true, firmware_name,
+ &client->dev, GFP_KERNEL,
+ data, mxt_request_firmware_work);
+ if (ret)
+ dev_err(&client->dev,
+ "cannot schedule firmware update (%d)\n", ret);
+ }
+
+out:
+ return ret;
+}
+
+static int mxt_input_open(struct input_dev *dev)
+{
+ struct mxt_data *data = input_get_drvdata(dev);
+ int ret;
+
+ ret = wait_for_completion_interruptible_timeout(&data->init_done,
+ msecs_to_jiffies(90 * MSEC_PER_SEC));
+
+ if (ret < 0) {
+ dev_err(&dev->dev,
+ "error while waiting for device to init (%d)\n", ret);
+ ret = -ENXIO;
+ goto err_open;
+ }
+ if (ret == 0) {
+ dev_err(&dev->dev,
+ "timedout while waiting for device to init\n");
+ ret = -ENXIO;
+ goto err_open;
+ }
+ data->pdata->power_reset();
+
+ ret = mxt_start(data);
+ if (ret)
+ goto err_open;
+
+ return 0;
+
+err_open:
+ return ret;
+}
+
+static void mxt_input_close(struct input_dev *dev)
+{
+ struct mxt_data *data = input_get_drvdata(dev);
+
+ mxt_stop(data);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#define mxt_suspend NULL
+#define mxt_resume NULL
+
+static void mxt_early_suspend(struct early_suspend *h)
+{
+ struct mxt_data *data = container_of(h, struct mxt_data,
+ early_suspend);
+#if TSP_INFORM_CHARGER
+ cancel_delayed_work_sync(&data->noti_dwork);
+#endif
+
+ mutex_lock(&data->lock);
+
+ if (data->mxt_enabled) {
+ disable_irq(data->client->irq);
+ data->mxt_enabled = false;
+ mxt_internal_suspend(data);
+ } else {
+ dev_err(&data->client->dev,
+ "%s. but touch already off\n", __func__);
+ }
+
+ mutex_unlock(&data->lock);
+}
+
+static void mxt_late_resume(struct early_suspend *h)
+{
+ struct mxt_data *data = container_of(h, struct mxt_data,
+ early_suspend);
+ mutex_lock(&data->lock);
+
+ if (data->mxt_enabled) {
+ dev_err(&data->client->dev,
+ "%s. but touch already on\n", __func__);
+ } else {
+ mxt_internal_resume(data);
+ data->mxt_enabled = true;
+ enable_irq(data->client->irq);
+ }
+
+ mutex_unlock(&data->lock);
+}
+#else
+static int mxt_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mxt_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->lock);
+
+ disable_irq(data->client->irq);
+ mxt_internal_suspend(data);
+ data->mxt_enabled = false;
+
+ mutex_unlock(&data->lock);
+ return 0;
+}
+
+static int mxt_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mxt_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->lock);
+
+ mxt_internal_resume(data);
+ data->mxt_enabled = true;
+ enable_irq(data->client->irq);
+
+ mutex_unlock(&data->lock);
+ return 0;
+}
+#endif
+
+/* Added for samsung dependency codes such as Factory test,
+ * Touch booster, Related debug sysfs.
+ */
+#include "mxt540s_sec.c"
+
+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;
+ struct input_dev *input_dev;
+ u16 boot_address;
+ int i, ret = 0;
+
+ if (!pdata) {
+ dev_err(&client->dev, "missing platform data\n");
+ return -ENODEV;
+ }
+
+ if (pdata->max_finger_touches <= 0)
+ return -EINVAL;
+
+ data = kzalloc(sizeof(*data) + pdata->max_finger_touches *
+ sizeof(*data->fingers), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!data || !input_dev) {
+ ret = -ENOMEM;
+ dev_err(&client->dev, "input device allocation failed\n");
+ goto err_alloc_dev;
+ }
+
+ data->client = client;
+ data->input_dev = input_dev;
+ data->pdata = pdata;
+ data->num_fingers = pdata->max_finger_touches;
+ data->mxt_enabled = true;
+ mutex_init(&data->lock);
+ init_completion(&data->init_done);
+
+ input_dev->name = "sec_touchscreen";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+ input_dev->open = mxt_input_open;
+ input_dev->close = mxt_input_close;
+
+ set_bit(EV_ABS, input_dev->evbit);
+ set_bit(EV_KEY, input_dev->evbit);
+ set_bit(MT_TOOL_FINGER, input_dev->keybit);
+ set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+ input_mt_init_slots(input_dev, data->num_fingers);
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->min_x,
+ pdata->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->min_y,
+ pdata->max_y, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, pdata->min_w,
+ pdata->max_w, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE, pdata->min_z,
+ pdata->max_z, 0, 0);
+
+#if TSP_USE_SHAPETOUCH
+ input_set_abs_params(input_dev, ABS_MT_COMPONENT, 0, 255, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_SUMSIZE, 0, 16 * 26, 0, 0);
+#endif
+ for (i = 0; i < data->num_fingers; i++)
+ data->fingers[i].state = MXT_STATE_INACTIVE;
+
+ input_set_drvdata(input_dev, data);
+ i2c_set_clientdata(client, data);
+
+ /* regist dummy device for boot_address */
+ if (data->pdata->boot_address) {
+ boot_address = data->pdata->boot_address;
+ } else {
+ if (client->addr == MXT_APP_LOW)
+ boot_address = MXT_BOOT_LOW;
+ else
+ boot_address = MXT_BOOT_HIGH;
+ }
+ data->client_boot = i2c_new_dummy(client->adapter, boot_address);
+ if (!data->client_boot) {
+ dev_err(&client->dev, "Fail to register sub client[0x%x]\n",
+ boot_address);
+ goto err_alloc_dev;
+ }
+
+ /* regist input device */
+ ret = input_register_device(input_dev);
+ if (ret)
+ goto err_reg_dev;
+
+#if TSP_INFORM_CHARGER
+ /* Register callbacks */
+ /* To inform tsp , charger connection status*/
+ data->callbacks.inform_charger = inform_charger;
+ if (pdata->register_cb) {
+ pdata->register_cb(&data->callbacks);
+ inform_charger_init(data);
+ }
+#endif
+
+ ret = mxt_sysfs_init(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to creat sysfs\n");
+ goto err_init_drv;
+ }
+
+ ret = mxt_touch_init(data, false);
+ if (ret) {
+ dev_err(&client->dev, "Failed to init driver\n");
+ goto err_init_drv;
+ }
+
+#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
+
+ return 0;
+
+err_init_drv:
+ input_unregister_device(input_dev);
+ input_dev = NULL;
+ gpio_free(data->pdata->gpio_read_done);
+ data->pdata->power_off();
+err_reg_dev:
+ input_free_device(input_dev);
+ i2c_unregister_device(data->client_boot);
+err_alloc_dev:
+ kfree(data);
+
+ return ret;
+}
+
+static int __devexit mxt_remove(struct i2c_client *client)
+{
+ struct mxt_data *data = i2c_get_clientdata(client);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&data->early_suspend);
+#endif
+ free_irq(client->irq, data);
+ kfree(data->objects);
+ kfree(data->rid_map);
+ gpio_free(data->pdata->gpio_read_done);
+ data->pdata->power_off();
+ input_unregister_device(data->input_dev);
+ i2c_unregister_device(data->client_boot);
+ kfree(data);
+
+ return 0;
+}
+
+static struct i2c_device_id mxt_idtable[] = {
+ {MXT_DEV_NAME, 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mxt_idtable);
+
+static const struct dev_pm_ops mxt_pm_ops = {
+ .suspend = mxt_suspend,
+ .resume = mxt_resume,
+};
+
+static struct i2c_driver mxt_i2c_driver = {
+ .id_table = mxt_idtable,
+ .probe = mxt_probe,
+ .remove = __devexit_p(mxt_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = MXT_DEV_NAME,
+#ifdef CONFIG_PM
+ .pm = &mxt_pm_ops,
+#endif
+ },
+};
+
+static int __init mxt_init(void)
+{
+ return i2c_add_driver(&mxt_i2c_driver);
+}
+
+static void __exit mxt_exit(void)
+{
+ i2c_del_driver(&mxt_i2c_driver);
+}
+module_init(mxt_init);
+module_exit(mxt_exit);
+
+MODULE_DESCRIPTION("Atmel MaXTouch driver");
+MODULE_AUTHOR("bumwoo.lee<bw365.lee@samsung.com>");
+MODULE_LICENSE("GPL");