diff options
author | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-22 09:48:20 +0200 |
---|---|---|
committer | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-22 14:02:16 +0200 |
commit | 2489007e7d740ccbc3e0a202914e243ad5178787 (patch) | |
tree | b8e6380ea7b1da63474ad68a5dba997e01146043 /drivers/input/touchscreen/mxt540s.c | |
parent | 5f67568eb31e3a813c7c52461dcf66ade15fc2e7 (diff) | |
download | kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.zip kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.tar.gz kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.tar.bz2 |
merge opensource jb u5
Change-Id: I1aaec157aa196f3448eff8636134fce89a814cf2
Diffstat (limited to 'drivers/input/touchscreen/mxt540s.c')
-rw-r--r-- | drivers/input/touchscreen/mxt540s.c | 2131 |
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, ¤t_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, ¤t_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"); |