diff options
Diffstat (limited to 'drivers/input/touchscreen/mxt224s_grande.c')
-rw-r--r-- | drivers/input/touchscreen/mxt224s_grande.c | 4110 |
1 files changed, 4110 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/mxt224s_grande.c b/drivers/input/touchscreen/mxt224s_grande.c new file mode 100644 index 0000000..05ca22f --- /dev/null +++ b/drivers/input/touchscreen/mxt224s_grande.c @@ -0,0 +1,4110 @@ +/* + * Copyright (C) 2010, 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#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/earlysuspend.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/i2c/mxt224s_grande.h> +#include <asm/unaligned.h> +#include <linux/firmware.h> +#include <linux/string.h> + +/* #define CONFIG_READ_FROM_FILE */ +#ifdef CONFIG_READ_FROM_FILE +#define CONFIG_READ_FROM_SDCARD +#define MXT_CFG_MAGIC "OBP_RAW V1" +#define MXT_BATT_CFG_NAME "/sdcard/mxt224s_batt_config.raw" +#define MXT_TA_CFG_NAME "/sdcard/mxt224s_ta_config.raw" + +struct mxt_info { + u8 family_id; + u8 variant_id; + u8 version; + u8 build; + u8 matrix_xsize; + u8 matrix_ysize; + u8 object_num; +}; +#endif + + +#define OBJECT_TABLE_START_ADDRESS 7 +#define OBJECT_TABLE_ELEMENT_SIZE 6 + +#define CMD_RESET_OFFSET 0 +#define CMD_BACKUP_OFFSET 1 +#define CMD_CALIBRATE_OFFSET 2 +#define CMD_REPORTATLL_OFFSET 3 +#define CMD_DEBUG_CTRL_OFFSET 4 +#define CMD_DIAGNOSTIC_OFFSET 5 + + +#define DETECT_MSG_MASK 0x80 +#define PRESS_MSG_MASK 0x40 +#define RELEASE_MSG_MASK 0x20 +#define MOVE_MSG_MASK 0x10 +#define AMPLITUDE_MSG_MASK 0x04 +#define SUPPRESS_MSG_MASK 0x02 + +/* Slave addresses */ +#define MXT_APP_LOW 0x4a +#define MXT_APP_HIGH 0x4b +#define MXT_BOOT_LOW 0x24 +#define MXT_BOOT_HIGH 0x25 + +/* FIRMWARE NAME */ +#define MXT_FW_NAME "tsp_atmel/mxt224s.fw" +#define MXT_BOOT_VALUE 0xa5 +#define MXT_BACKUP_VALUE 0x55 + +#if CHECK_ANTITOUCH +#define MXT_T61_TIMER_ONESHOT 0 +#define MXT_T61_TIMER_REPEAT 1 +#define MXT_T61_TIMER_CMD_START 1 +#define MXT_T61_TIMER_CMD_STOP 2 +#endif + +/* Bootloader mode status */ +#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ +#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ +#define MXT_FRAME_CRC_CHECK 0x02 +#define MXT_FRAME_CRC_FAIL 0x03 +#define MXT_FRAME_CRC_PASS 0x04 +#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ +#define MXT_BOOT_STATUS_MASK 0x3f + +/* Command to unlock bootloader */ +#define MXT_UNLOCK_CMD_MSB 0xaa +#define MXT_UNLOCK_CMD_LSB 0xdc + +#define ID_BLOCK_SIZE 7 + +#define MXT_STATE_INACTIVE -1 +#define MXT_STATE_RELEASE 0 +#define MXT_STATE_PRESS 1 +#define MXT_STATE_MOVE 2 + +#define MAX_USING_FINGER_NUM 10 + +#define MXT_SW_RESET_TIME 300 + +/* Feature */ +/*#######################################*/ +#define TOUCH_BOOSTER 1 +#define SYSFS 1 +#define FOR_BRINGUP 1 +#define UPDATE_ON_PROBE 1 +#define READ_FW_FROM_HEADER 1 +#define FOR_DEBUGGING_TEST_DOWNLOADFW_BIN 0 +#define ITDEV 1 +#define SHOW_COORDINATE 1 +#define DEBUG_INFO 0 +#define HIGH_RESOLUTION 0 +#define TREAT_ERR 0 +#define FORCE_RELEASE 0 +#define DOWNLOAD_CONFIG 0 +/*#######################################*/ + + +/* touch booster */ +#if TOUCH_BOOSTER +#include <mach/cpufreq.h> +#define TOUCH_BOOSTER_TIME 3000 +#define TOUCH_BOOSTER_LIMIT_CLK 500000 + +static bool tsp_press_status; +static bool touch_cpu_lock_status; +static int cpu_lv = -1; +#endif + +/* Firmware */ +#if READ_FW_FROM_HEADER +static u8 firmware_mXT[] = { + #include "mxt224s_V1.1.AA_.h" +}; +#endif + +#if ITDEV +static int driver_paused; +static int debug_enabled; +#endif + +#define DUAL_TSP 1 +#if DUAL_TSP +#define FLIP_NOTINIT -1 +#define FLIP_OPEN 1 +#define FLIP_CLOSE 0 +/* Slave addresses */ +#define MXT224S_ADDR_MAIN 0x4b +#define MXT224S_ADDR_SUB 0x4a +/* TSP_SEL value : GPIO to switch Main tsp or Sub tsp */ +#define TSP_SEL_toMAIN 0 +#define TSP_SEL_toSUB 1 + +static int Flip_status_tsp; +static int Tsp_current_addr; + +static int Tsp_main_initialized; +static int Tsp_sub_initialized; +static int Tsp_probe_passed; +#endif + +/* add for protection code */ +/*#######################################*/ +/*#######################################*/ + +/* variable related protection code */ +/*#######################################*/ +static int treat_median_error_status; +/*#######################################*/ + +struct object_t { + u8 object_type; + u16 i2c_address; + u8 size; + u8 instances; + u8 num_report_ids; +} __packed; + +struct finger_info { + s16 x; + s16 y; + s16 z; + u16 w; + s8 state; + int16_t component; + u16 mcount; /*add for debug*/ +}; + +struct report_id_map_t { + u8 object_type; /*!< Object type. */ + u8 instance; /*!< Instance number. */ +}; + +struct mxt_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct mxt224s_platform_data *pdata; + struct early_suspend early_suspend; + u8 family_id; + u32 finger_mask; + int gpio_read_done; + struct object_t *objects; + u8 objects_len; + u8 tsp_version; + u8 tsp_build; + u8 tsp_variant; +#if DUAL_TSP + const u8 *power_cfg; +#endif + u8 finger_type; + u16 msg_proc; + u16 cmd_proc; + u16 msg_object_size; + u32 x_dropbits:2; + u32 y_dropbits:2; + u8 tchthr_batt; + u8 tchthr_charging; + u8 calcfg_batt; + u8 calcfg_charging; + u8 disable_config_write; + unsigned char Report_touch_number; + u16 distance[10]; +#if TOUCH_BOOSTER + struct delayed_work dvfs_dwork; +#endif + void (*power_on)(void); + void (*power_off)(void); + void (*register_cb)(void *); + void (*read_ta_status)(void *); + int num_fingers; +#if ITDEV + u16 last_read_addr; + u16 msg_proc_addr; +#endif +#ifdef CONFIG_READ_FROM_FILE + struct mxt_info info; +#endif + u8 max_report_id; + struct report_id_map_t *rid_map; + bool rid_map_alloc; + unsigned int touch_area_cnt; + int tcount[10]; + struct finger_info fingers[]; +}; + +static struct mxt_data *copy_data; + +int touch_is_pressed; +EXPORT_SYMBOL(touch_is_pressed); +static int mxt_enabled; +static bool g_debug_switch; +static u8 threshold; +static int firm_status_data; + +static u16 pre_x[][4] = {{0}, }; +static u16 pre_y[][4] = {{0}, }; + +static u8 firmware_latest[] = {0x11, 0xaa}; /* version, build_version */ + +#if DEBUG_INFO +static u8 *object_type_name[63] = { + [5] = "GEN_MESSAGEPROCESSOR_T5", + [6] = "GEN_COMMANDPROCESSOR_T6", + [7] = "GEN_POWERCONFIG_T7", + [8] = "GEN_ACQUIRECONFIG_T8", + [9] = "TOUCH_MULTITOUCHSCREEN_T9", + [15] = "TOUCH_KEYARRAY_T15", + [18] = "SPT_COMCONFIG_T18", + [23] = "TOUCH_PROXIMITY_T23", + [25] = "SPT_SELFTEST_T25", + [37] = "DEBUG_DIAGNOSTICS_T37", + [38] = "USER_DATA_T38", + [40] = "PROCI_GRIPSUPPRESSION_T40", + [42] = "PROCI_TOUCHSUPPRESSION_T42", + [44] = "MESSAGECOUNT_T44", + [46] = "SPT_CTECONFIG_T46", + [47] = "PROCI_STYLUS_T47", + [55] = "PROCI_ADAPTIVETHRESHOLD_T55", + [56] = "PROCI_SHIELDLESS_T56", + [57] = "SPT_GENERICDATA_T57", + [61] = "SPT_TIMER_T61", + [62] = "PROCG_NOISESUPPRESSION_T62", +}; +#endif + +#if TOUCH_BOOSTER +static void mxt_set_dvfs_off(struct work_struct *work) +{ + struct mxt_data *data = + container_of(work, struct mxt_data, dvfs_dwork.work); + + if (mxt_enabled) { + disable_irq(data->client->irq); + if (touch_cpu_lock_status + && !tsp_press_status){ + exynos_cpufreq_lock_free(DVFS_LOCK_ID_TSP); + touch_cpu_lock_status = 0; + } + enable_irq(data->client->irq); + } +} + +static void mxt_set_dvfs_on(struct mxt_data *data) +{ + cancel_delayed_work(&data->dvfs_dwork); + if (cpu_lv < 0) + exynos_cpufreq_get_level(TOUCH_BOOSTER_LIMIT_CLK, &cpu_lv); + exynos_cpufreq_lock(DVFS_LOCK_ID_TSP, cpu_lv); + touch_cpu_lock_status = 1; +} +#endif + +extern struct class *sec_class; + +#ifdef CONFIG_READ_FROM_FILE +int mxt_download_config(struct mxt_data *data, const char *fn); +#endif + +/* declare function proto type */ +/* +static void mxt_ta_probe(int ta_status); +static void report_input_data(struct mxt_data *data); +*/ +static int read_mem(struct mxt_data *data, u16 reg, u8 len, u8 *buf) +{ + int ret; + 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, + }, + }; +#if DUAL_TSP + msg[0].addr=Tsp_current_addr; + msg[1].addr=Tsp_current_addr; +#endif + + ret = i2c_transfer(data->client->adapter, msg, 2); + if (ret < 0) { + pr_err("i2c failed ret = %d, %d %d\n", ret, Tsp_current_addr, data->client->addr); + return ret; + } + return ret == 2 ? 0 : -EIO; +} + +static int write_mem(struct mxt_data *data, u16 reg, u8 len, const u8 *buf) +{ + int ret; + u8 tmp[len + 2]; + + put_unaligned_le16(cpu_to_le16(reg), tmp); + memcpy(tmp + 2, buf, len); + +#if DUAL_TSP + data->client->addr =Tsp_current_addr; +#endif + ret = i2c_master_send(data->client, tmp, sizeof(tmp)); + if (ret < 0){ + pr_err("i2c write failed ret = %d, %d\n", ret, data->client->addr); + return ret; + } + + return ret == sizeof(tmp) ? 0 : -EIO; +} + +static int __devinit mxt_reset(struct mxt_data *data) +{ + u8 buf = 1u; + return write_mem(data, data->cmd_proc + CMD_RESET_OFFSET, 1, &buf); +} + +static int __devinit mxt_backup(struct mxt_data *data) +{ + u8 buf = 0x55u; + return write_mem(data, data->cmd_proc + CMD_BACKUP_OFFSET, 1, &buf); +} + +static int get_object_info(struct mxt_data *data, u8 object_type, u16 *size, + u16 *address) +{ + int i; + + for (i = 0; i < data->objects_len; i++) { + if (data->objects[i].object_type == object_type) { + *size = data->objects[i].size + 1; + *address = data->objects[i].i2c_address; + return 0; + } + } + + return -ENODEV; +} + +#if CHECK_ANTITOUCH +void mxt_t61_timer_set(struct mxt_data *data, u8 mode, u8 cmd, u16 msPeriod) +{ + int ret = 0; + u8 buf[5] = {3, 0, 0, 0, 0}; + u16 size = 0; + u16 obj_address = 0; + + get_object_info(data, SPT_TIMER_T61, + &size, &obj_address); + + buf[1] = cmd; + buf[2] = mode; + buf[3] = msPeriod & 0xFF; + buf[4] = (msPeriod >> 8) & 0xFF; + + ret = write_mem(data, obj_address, 5, buf); + if (ret) + pr_err("%s write error T%d address[0x%x]\n", + __func__, SPT_TIMER_T61, obj_address); + + pr_info("T61 Timer Enabled %d\n", msPeriod); +} + +void mxt_t8_cal_set(struct mxt_data *data, u8 mstime) +{ + int ret = 0; + u16 size = 0; + u16 obj_address = 0; + + if (mstime) { + data->pdata->check_autocal = 1; + pr_info("T8 Autocal Enabled %d\n", mstime); + } else { + data->pdata->check_autocal = 0; + pr_info("T8 Autocal Disabled %d\n", mstime); + } + + get_object_info(data, GEN_ACQUISITIONCONFIG_T8, + &size, &obj_address); + + + ret = write_mem(data, obj_address + 4, 1, &mstime); + if (ret) + pr_err("%s write error T%d address[0x%x]\n", + __func__, SPT_TIMER_T61, obj_address); +} + +/* +static void mxt_check_coordinate(struct mxt_data *data,bool detect, u8 id,u16 *px, u16 *py) +{ + if (detect) { + data->tcount[id] = 0; + data->distance[id] = 0; + } + if (data->tcount[id] >= 1) { // 2rd + data->distance[id] = abs(pre_x[id][0] - *px) + abs(pre_y[id][0] - *py); + pr_info("[TSP]Check Distance ID:%d, %d\n",id, data->distance[id]); + } + pre_x[id][0] = *px; + pre_y[id][0] = *py; + + data->tcount[id]++; +} +*/ +#endif /* CHECK_ANTITOUCH */ + +static int write_config(struct mxt_data *data, u8 type, const u8 *cfg) +{ + int ret; + u16 address = 0; + u16 size = 0; + + ret = get_object_info(data, type, &size, &address); + + if (size == 0 && address == 0) + return 0; + else + return write_mem(data, address, size, cfg); +} + +static int check_instance(struct mxt_data *data, u8 object_type) +{ + int i; + + for (i = 0; i < data->objects_len; i++) { + if (data->objects[i].object_type == object_type) + return data->objects[i].instances; + } + return 0; +} + +static int init_write_config(struct mxt_data *data, u8 type, const u8 *cfg) +{ + int ret; + u16 address = 0; + u16 size = 0; + u8 *temp; + + ret = get_object_info(data, type, &size, &address); + + if ((size == 0) || (address == 0)) { + pr_err("%s error object_type(%d)\n", __func__, type); + return -ENODEV; + } + + ret = write_mem(data, address, size, cfg); + + if (check_instance(data, type)) { +#if DEBUG_INFO + pr_info("exist instance1 object (%d)\n", type); +#endif + temp = kmalloc(size * sizeof(u8), GFP_KERNEL); + memset(temp, 0, size); + ret |= write_mem(data, address+size, size, temp); + kfree(temp); + } + + return ret; +} + +#if TREAT_ERR +static int change_config(struct mxt_data *data, + u16 reg, u8 offeset, u8 change_value) +{ + u8 value = 0; + + value = change_value; + return write_mem(data, reg+offeset, 1, &value); +} +#endif + +static u32 __devinit 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 __devinit calculate_infoblock_crc(struct mxt_data *data, + u32 *crc_pointer) +{ + u32 crc = 0; + u8 mem[7 + data->objects_len * 6]; + int status; + int i; + + status = read_mem(data, 0, sizeof(mem), mem); + + if (status) + return status; + + 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 uint8_t calibrate_chip_e(void) +{ + u8 cal_data = 1; + int ret = 0; + /* send calibration command to the chip */ + ret = write_mem(copy_data, + copy_data->cmd_proc + CMD_CALIBRATE_OFFSET, + 1, &cal_data); + /* set flag for calibration lockup + recovery if cal command was successful */ + if (!ret) + pr_info("calibration success!!!\n"); + return ret; +} + +#if TREAT_ERR +static void treat_error_status(void) +{ + bool ta_status = 0; + u16 size; + u16 obj_address = 0; + int error = 0; + struct mxt_data *data = copy_data; + + data->read_ta_status(&ta_status); + + if (treat_median_error_status) { + pr_err("Error status already treated\n"); + return; + } else + treat_median_error_status = 1; + + pr_info("Error status TA is[%d]\n", ta_status); + + if (ta_status) { +#if !(FOR_BRINGUP) + get_object_info(data, + GEN_POWERCONFIG_T7, &size, &obj_address); + /* 1:ACTVACQINT */ + error = change_config(data, obj_address, 1, 255); + + get_object_info(data, + GEN_ACQUISITIONCONFIG_T8, &size, &obj_address); + /* 0:CHRGTIME */ + error |= change_config(data, obj_address, 0, 64); + + /* 8:ATCHFRCCALTHR*/ + error |= change_config(data, obj_address, 8, 50); + /* 9:ATCHFRCCALRATIO*/ + error |= change_config(data, obj_address, 9, 0); + + get_object_info(data, + PROCI_TOUCHSUPPRESSION_T42, &size, &obj_address); + /* 0:CTRL */ + error |= change_config(data, obj_address, 0, 3); + + get_object_info(data, + SPT_CTECONFIG_T46, &size, &obj_address); + /* 2:IDLESYNCSPERX */ + error |= change_config(data, obj_address, 2, 48); + /* 3:ACTVSYNCSPERX */ + error |= change_config(data, obj_address, 3, 48); + + get_object_info(data, + PROCG_NOISESUPPRESSION_T48, &size, &obj_address); + /* 2:CALCFG */ + error |= change_config(data, obj_address, 2, 114); + /* 3:BASEFREQ */ + error |= change_config(data, obj_address, 3, 15); + /* 8:MFFREQ[0] */ + error |= change_config(data, obj_address, 8, 3); + /* 9:MFFREQ[1] */ + error |= change_config(data, obj_address, 9, 5); + /* 10:NLGAIN*/ + error |= change_config(data, obj_address, 10, 96); + /* 11:NLTHR*/ + error |= change_config(data, obj_address, 11, 30); + /* 17:GCMAXADCSPERX */ + error |= change_config(data, obj_address, 17, 100); + /* 34:BLEN[0] */ + error |= change_config(data, obj_address, 34, 80); + /* 35:TCHTHR[0] */ + error |= change_config(data, obj_address, 35, 40); + /* 36:TCHDI[0] */ + error |= change_config(data, obj_address, 36, 2); + /* 39:MOVFILTER[0] */ + error |= change_config(data, obj_address, 39, 65); + /* 41:MRGHYST[0] */ + error |= change_config(data, obj_address, 41, 40); + /* 42:MRGTHR[0] */ + error |= change_config(data, obj_address, 42, 50); + /* 43:XLOCLIP[0] */ + error |= change_config(data, obj_address, 43, 5); + /* 44:XHICLIP[0] */ + error |= change_config(data, obj_address, 44, 5); + /* 51:JUMPLIMIT[0] */ + error |= change_config(data, obj_address, 51, 25); + /* 52:TCHHYST[0] */ + error |= change_config(data, obj_address, 52, 15); +#endif + if (error < 0) + pr_err("failed to write error status\n"); + } else { +#if !(FOR_BRINGUP) + get_object_info(data, + GEN_POWERCONFIG_T7, &size, &obj_address); + /* 1:ACTVACQINT */ + error = change_config(data, obj_address, 1, 255); + + get_object_info(data, + GEN_ACQUISITIONCONFIG_T8, &size, &obj_address); + /* 0:CHRGTIME */ + error |= change_config(data, obj_address, 0, 64); + + /* 8:ATCHFRCCALTHR*/ + error |= change_config(data, obj_address, 8, 50); + /* 9:ATCHFRCCALRATIO*/ + error |= change_config(data, obj_address, 9, 0); + + get_object_info(data, + TOUCH_MULTITOUCHSCREEN_T9, &size, &obj_address); + /* 31:TCHHYST */ + error |= change_config(data, obj_address, 31, 15); + + get_object_info(data, + PROCI_TOUCHSUPPRESSION_T42, &size, &obj_address); + /* 0:CTRL */ + error |= change_config(data, obj_address, 0, 3); + + get_object_info(data, + SPT_CTECONFIG_T46, &size, &obj_address); + /* 2:IDLESYNCSPERX */ + error |= change_config(data, obj_address, 2, 48); + /* 3:ACTVSYNCSPERX */ + error |= change_config(data, obj_address, 3, 48); + + get_object_info(data, + PROCG_NOISESUPPRESSION_T48, &size, &obj_address); + /* 2:CALCFG */ + error |= change_config(data, obj_address, 2, 242); + /* 3:BASEFREQ */ + error |= change_config(data, obj_address, 3, 15); + /* 8:MFFREQ[0] */ + error |= change_config(data, obj_address, 8, 3); + /* 9:MFFREQ[1] */ + error |= change_config(data, obj_address, 9, 5); + /* 10:NLGAIN*/ + error |= change_config(data, obj_address, 10, 112); + /* 11:NLTHR*/ + error |= change_config(data, obj_address, 11, 25); + /* 17:GCMAXADCSPERX */ + error |= change_config(data, obj_address, 17, 100); + /* 34:BLEN[0] */ + error |= change_config(data, obj_address, 34, 112); + /* 35:TCHTHR[0] */ + error |= change_config(data, obj_address, 35, 40); + /* 41:MRGHYST[0] */ + error |= change_config(data, obj_address, 41, 40); + /* 42:MRGTHR[0] */ + error |= change_config(data, obj_address, 42, 50); + /* 51:JUMPLIMIT[0] */ + error |= change_config(data, obj_address, 51, 25); + /* 52:TCHHYST[0] */ + error |= change_config(data, obj_address, 52, 15); +#endif + if (error < 0) + pr_err("failed to write error status\n"); + } +} +#endif + +static void mxt_ta_probe(int ta_status) +{ +#ifdef CONFIG_READ_FROM_FILE + struct mxt_data *data = copy_data; + if (ta_status) + mxt_download_config(data, MXT_TA_CFG_NAME); + else + mxt_download_config(data, MXT_BATT_CFG_NAME); +#else +#if 1//!(FOR_BRINGUP) + u16 obj_address = 0; + u16 size; + int error = 0; + u8 value = 0; + + struct mxt_data *data = copy_data; + + if (!mxt_enabled) { + pr_err("%s mxt_enabled is 0\n", __func__); + return; + } + + if (ta_status) { + write_config(copy_data, copy_data->pdata->t9_config_chrg[0], copy_data->pdata->t9_config_chrg + 1); + write_config(copy_data, copy_data->pdata->t56_config_chrg[0], copy_data->pdata->t56_config_chrg + 1); + write_config(copy_data, copy_data->pdata->t62_config_chrg[0], copy_data->pdata->t62_config_chrg + 1); +#ifdef CONFIG_READ_FROM_FILE + mxt_download_config(data, MXT_TA_CFG_NAME); +#endif + } else { + write_config(copy_data, copy_data->pdata->t9_config_batt[0], copy_data->pdata->t9_config_batt + 1); + write_config(copy_data, copy_data->pdata->t56_config_batt[0], copy_data->pdata->t56_config_batt + 1); + write_config(copy_data, copy_data->pdata->t62_config_batt[0], copy_data->pdata->t62_config_batt + 1); +#ifdef CONFIG_READ_FROM_FILE + mxt_download_config(data, MXT_BATT_CFG_NAME); +#endif + } +#endif + get_object_info(data, PROCG_NOISESUPPRESSION_T62, + &size, &obj_address); + + //Read CALCFG1 for Setting CHRGON + read_mem(data, obj_address+1, 1, &value); + + + value &= 0xFE; + error = write_mem(data, obj_address+1, 1, &value); + + + value |= 0x01; + error = write_mem(data, obj_address+1, 1, &value); +#endif + pr_info("%s : threshold[%d]\n", __func__, threshold); +}; + +static uint8_t reportid_to_type(struct mxt_data *data, u8 report_id, u8 *instance) +{ + struct report_id_map_t *report_id_map; + report_id_map = data->rid_map; + + if (report_id <= data->max_report_id) { + *instance = report_id_map[report_id].instance; + return report_id_map[report_id].object_type; + } else + return 0; +} + +static int __devinit mxt_init_touch_driver(struct mxt_data *data) +{ + struct object_t *object_table; + struct report_id_map_t *report_id_map_t; + u32 read_crc = 0; + u32 calc_crc; + u16 crc_address; + u16 dummy; + int i, j; + u8 id[ID_BLOCK_SIZE]; + int ret; + u8 type_count = 0; + u8 tmp; + int cur_rep_id, start_report_id; + + ret = read_mem(data, 0, sizeof(id), id); + if (ret) + return ret; + + pr_info("family = %#02x, variant = %#02x, version " + "= %#02x, build = %#02x, " + "matrix X,Y size = %d,%d\n" + , id[0], id[1], id[2], id[3], id[4], id[5]); + + data->family_id = id[0]; + data->tsp_variant = id[1]; + data->tsp_version = id[2]; + data->tsp_build = id[3]; + data->objects_len = id[6]; + + object_table = kmalloc(data->objects_len * sizeof(*object_table), + GFP_KERNEL); + if (!object_table) + return -ENOMEM; + + ret = read_mem(data, OBJECT_TABLE_START_ADDRESS, + data->objects_len * sizeof(*object_table), + (u8 *)object_table); + if (ret) + goto err; + + data->max_report_id = 0; + + for (i = 0; i < data->objects_len; i++) { + object_table[i].i2c_address = + le16_to_cpu(object_table[i].i2c_address); + data->max_report_id += object_table[i].num_report_ids * + (object_table[i].instances + 1); + tmp = 0; + if (object_table[i].num_report_ids) { + tmp = type_count + 1; + type_count += object_table[i].num_report_ids * + (object_table[i].instances + 1); + } + switch (object_table[i].object_type) { + case TOUCH_MULTITOUCHSCREEN_T9: + data->finger_type = tmp; + pr_info("Finger type = %d\n", + data->finger_type); + break; + case GEN_MESSAGEPROCESSOR_T5: +#if ITDEV + data->msg_proc_addr = object_table[i].i2c_address; +#endif + data->msg_object_size = object_table[i].size + 1; + break; + } + } + if (data->rid_map_alloc) { + data->rid_map_alloc = false; + kfree(data->rid_map); + } + data->rid_map = kmalloc((sizeof(report_id_map_t) * data->max_report_id + 1), GFP_KERNEL); + + if (!data->rid_map) { + kfree(object_table); + return -ENOMEM; + } + data->rid_map_alloc = true; + data->rid_map[0].instance = 0; + data->rid_map[0].object_type = 0; + cur_rep_id = 1; + + for (i = 0; i < data->objects_len; i++) { + if (object_table[i].num_report_ids != 0) { + for (j = 0; j <= object_table[i].instances; j++) { + for (start_report_id = cur_rep_id; + cur_rep_id < + (start_report_id + + object_table[i].num_report_ids); + cur_rep_id++) { + data->rid_map[cur_rep_id].instance = j; + data->rid_map[cur_rep_id].object_type = + object_table[i].object_type; + } + } + } + } + data->objects = object_table; + + /* Verify CRC */ + crc_address = OBJECT_TABLE_START_ADDRESS + + data->objects_len * OBJECT_TABLE_ELEMENT_SIZE; + +#ifdef __BIG_ENDIAN +#error The following code will likely break on a big endian machine +#endif + ret = read_mem(data, crc_address, 3, (u8 *)&read_crc); + if (ret) + goto err; + + read_crc = le32_to_cpu(read_crc); + + ret = calculate_infoblock_crc(data, &calc_crc); + if (ret) + goto err; + + if (read_crc != calc_crc) { + pr_err("CRC error\n"); + ret = -EFAULT; + goto err; + } + + ret = get_object_info(data, GEN_MESSAGEPROCESSOR_T5, &dummy, + &data->msg_proc); + if (ret) + goto err; + + ret = get_object_info(data, GEN_COMMANDPROCESSOR_T6, &dummy, + &data->cmd_proc); + if (ret) + goto err; + +#if DEBUG_INFO + pr_info("maXTouch: %d Objects\n", + data->objects_len); + + for (i = 0; i < data->objects_len; i++) { + pr_info("Type:\t\t\t[%d]: %s\n", + object_table[i].object_type, + object_type_name[object_table[i].object_type]); + pr_info("\tAddress:\t0x%04X\n", + object_table[i].i2c_address); + pr_info("\tSize:\t\t%d Bytes\n", + object_table[i].size); + pr_info("\tInstances:\t%d\n", + object_table[i].instances); + pr_info("\tReport Id's:\t%d\n", + object_table[i].num_report_ids); + } +#endif + + return 0; + +err: + kfree(object_table); + return ret; +} + + +static void 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].z); + input_report_abs(data->input_dev, ABS_MT_PRESSURE, + data->fingers[i].w); + } + #ifdef _SUPPORT_SHAPE_TOUCH_ + input_report_abs(data->input_dev, ABS_MT_COMPONENT, + data->fingers[i].component); + #endif + + report_count++; + +#if SHOW_COORDINATE + switch (data->fingers[i].state) { + case MXT_STATE_PRESS: + pr_info("P: " + "id[%d],x=%d,y=%d,w=%d, z=%d\n", + i, data->fingers[i].x, data->fingers[i].y + , data->fingers[i].w, data->fingers[i].z); + break; +/* + case MXT_STATE_MOVE: + pr_info("M: " + "id[%d],x=%d,y=%d,w=%d,mc=%d\n", + i, data->fingers[i].x, data->fingers[i].y + , data->fingers[i].w, data->fingers[i].mcount); + break; +*/ + case MXT_STATE_RELEASE: + pr_info("R: " + "id[%d],mc=%d\n", + i, data->fingers[i].mcount); + break; + default: + break; + } +#else + if (data->fingers[i].state == MXT_STATE_PRESS) + pr_info("P: id[%d],w=%d\n" + , i, data->fingers[i].w); + else if (data->fingers[i].state == MXT_STATE_RELEASE) + pr_info("R: id[%d],mc=%d\n" + , i, data->fingers[i].mcount); +#endif + 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 ITDEV + if (!driver_paused) +#endif + input_sync(data->input_dev); + } + + if (count) + touch_is_pressed = 1; + else + touch_is_pressed = 0; + +#if TOUCH_BOOSTER + if (count == 0) { + if (touch_cpu_lock_status) { + cancel_delayed_work(&data->dvfs_dwork); + schedule_delayed_work(&data->dvfs_dwork, + msecs_to_jiffies(TOUCH_BOOSTER_TIME)); + } + tsp_press_status = 0; + } else + tsp_press_status = 1; +#endif + data->finger_mask = 0; +} + +#if CHECK_ANTITOUCH +static u16 mxt_dist_check(struct mxt_data *data, unsigned char touch_num) +{ + int count; + u16 dist_sum = 0; + + for (count = 0; count < touch_num; count++) { + if (data->distance[count] < 4) { /* touch_num==2À̸é,, data->distance[0],data->distance[1] À» ü’Ú */ + dist_sum++; + } else { + dist_sum = 0; + } + } + pr_info("[TSP] dis0 = %d, dis1 = %d, dis2 = %d, dis3 = %d, dis4 = %d, dis5 = %d\n", data->distance[0], data->distance[1], data->distance[2], data->distance[3], data->distance[4], data->distance[5]); + return dist_sum; +} +#endif + +static irqreturn_t mxt_irq_thread(int irq, void *ptr) +{ + struct mxt_data *data = ptr; + int id; + u8 msg[data->msg_object_size]; + u8 touch_message_flag = 0; + u8 object_type, instance; +#if CHECK_ANTITOUCH + u16 tch_area = 0, atch_area = 0; +#endif + +#if DEBUG_INFO + pr_info("[TSP] mxt_irq_thread()\n"); +#endif + do { + touch_message_flag = 0; + if (read_mem(data, data->msg_proc, sizeof(msg), msg)) { +#if TOUCH_BOOSTER + if (touch_cpu_lock_status) { + exynos_cpufreq_lock_free(DVFS_LOCK_ID_TSP); + touch_cpu_lock_status = 0; + } +#endif + return IRQ_HANDLED; + } +#if ITDEV + if (debug_enabled) + print_hex_dump(KERN_INFO, "MXT MSG:", + DUMP_PREFIX_NONE, 16, 1, msg, sizeof(msg), false); +#endif + object_type = reportid_to_type(data, msg[0] , &instance); + + if (object_type == GEN_COMMANDPROCESSOR_T6) { + if (msg[1] == 0x00) /* normal mode */ + pr_info("normal mode\n"); + if ((msg[1]&0x04) == 0x04) /* I2C checksum error */ + pr_info("I2C checksum error\n"); + if ((msg[1]&0x08) == 0x08) /* config error */ + pr_info("config error\n"); + if ((msg[1]&0x10) == 0x10) { + /* calibration */ + pr_info("calibration is" + " on going !!\n"); + +#if CHECK_ANTITOUCH + /* After Calibration */ + pr_info("[TSP] mxt_check_coordinate Disable autocal = 0\n"); + mxt_t8_cal_set(data, 0); + data->pdata->check_antitouch = 1; + mxt_t61_timer_set(data, + MXT_T61_TIMER_ONESHOT, + MXT_T61_TIMER_CMD_STOP, 0); + data->pdata->check_timer = 0; + data->pdata->check_calgood = 0; +#endif + } + if ((msg[1]&0x20) == 0x20) /* signal error */ + pr_info("signal error\n"); + if ((msg[1]&0x40) == 0x40) /* overflow */ + pr_info("overflow detected\n"); + if ((msg[1]&0x80) == 0x80) /* reset */ + pr_info("[TSP] reset is ongoing\n"); + } + + if (object_type == PROCI_TOUCHSUPPRESSION_T42) { + if ((msg[1] & 0x01) == 0x00) { + /* Palm release */ + pr_info("palm touch released\n"); + touch_is_pressed = 0; + + } else if ((msg[1] & 0x01) == 0x01) { + /* Palm Press */ + pr_info("palm touch detected\n"); + touch_is_pressed = 1; + touch_message_flag = 1; + } + } + if (object_type == PROCI_EXTRATOUCHSCREENDATA_T57) { +#if CHECK_ANTITOUCH + tch_area = msg[3] | (msg[4] << 8); + atch_area = msg[5] | (msg[6] << 8); + + data->Report_touch_number = 0; + + if (data->pdata->check_antitouch) { + if (tch_area) { + pr_info("TCHAREA=%d\n", tch_area); + + /* First Touch After Calibration */ + if (data->pdata->check_timer == 0) { + mxt_t61_timer_set(data, + MXT_T61_TIMER_ONESHOT, + MXT_T61_TIMER_CMD_START, 3000); + data->pdata->check_timer = 1; + } + + if (tch_area <= 2) + mxt_t8_cal_set(data, 5); + } + + if (atch_area >= 3) { + pr_info("ATCHAREA=%d\n", atch_area); + calibrate_chip_e(); + } + } + + if (data->pdata->check_calgood == 1) { + if ((atch_area - tch_area) > 8) { + if (tch_area < 35) { + pr_info("Cal Not Good %d %d\n", atch_area, tch_area); + calibrate_chip_e(); + } + } + if ((tch_area - atch_area) >= 40) { + pr_info("Cal Not Good 2 - %d %d\n", + atch_area, tch_area); + calibrate_chip_e(); + } + } +#endif /* CHECK_ANTITOUCH */ + } + +#if CHECK_ANTITOUCH + if (object_type == SPT_TIMER_T61) { + if ((msg[1] & 0xa0) == 0xa0) { + + if (data->pdata->check_calgood == 1) + data->pdata->check_calgood = 0; + + if (data->pdata->check_antitouch) { + pr_info("SPT_TIMER_T61 Stop\n"); + data->pdata->check_antitouch = 0; + data->pdata->check_timer = 0; + mxt_t8_cal_set(data, 0); + data->pdata->check_calgood = 1; + mxt_t61_timer_set(data, MXT_T61_TIMER_ONESHOT, MXT_T61_TIMER_CMD_START, 10000); + } + } + } +#endif /* CHECK_ANTITOUCH */ + if (object_type == TOUCH_MULTITOUCHSCREEN_T9) { + id = msg[0] - data->finger_type; + + /* If not a touch event, then keep going */ + if (id < 0 || id >= data->num_fingers) + continue; + + if (data->finger_mask & (1U << id)) + report_input_data(data); + + if (msg[1] & RELEASE_MSG_MASK) { + data->fingers[id].z = 0; + data->fingers[id].w = msg[5]; + data->finger_mask |= 1U << id; + data->fingers[id].state = MXT_STATE_RELEASE; + } else if ((msg[1] & DETECT_MSG_MASK) && (msg[1] & + (PRESS_MSG_MASK | MOVE_MSG_MASK))) { +#if TOUCH_BOOSTER + if (!touch_cpu_lock_status) + mxt_set_dvfs_on(data); +#endif + touch_message_flag = 1; + data->fingers[id].z = msg[6]; + data->fingers[id].w = msg[5]; + if (data->fingers[id].w == 0) + data->fingers[id].w = 1; + + 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 HIGH_RESOLUTION + /* high X resolution version*/ + data->fingers[id].x = (u16)((data->fingers[id].x * 480) / 4096); /* 800 -> 480 */ + data->fingers[id].y = (u16)((data->fingers[id].y * 800) / 4096); /* 480 -> 800 */ +#endif + + data->finger_mask |= 1U << id; + + if (msg[1] & PRESS_MSG_MASK) { + data->fingers[id].state = MXT_STATE_PRESS; + data->fingers[id].mcount = 0; +#if CHECK_ANTITOUCH + /* mxt_check_coordinate(data, 1, id, &data->fingers[id].x, &data->fingers[id].y);*/ +#endif + + } else if (msg[1] & MOVE_MSG_MASK) { + data->fingers[id].mcount += 1; +#if CHECK_ANTITOUCH + /* mxt_check_coordinate(data, 0, id, &data->fingers[id].x, &data->fingers[id].y);*/ +#endif + } + +#ifdef _SUPPORT_SHAPE_TOUCH_ + data->fingers[id].component = msg[7]; +#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 message */ + if (!((msg[1] & DETECT_MSG_MASK) + && (msg[1] & AMPLITUDE_MSG_MASK))) + pr_err("Unknown state %#02x %#02x\n", + msg[0], msg[1]); + continue; + } + } + } while (!gpio_get_value(data->gpio_read_done)); + + if (data->finger_mask) + report_input_data(data); + + + return IRQ_HANDLED; +} + +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) + report_input_data(data); + +#if TOUCH_BOOSTER + cancel_delayed_work(&data->dvfs_dwork); + tsp_press_status = 0; + if (touch_cpu_lock_status) { + exynos_cpufreq_lock_free(DVFS_LOCK_ID_TSP); + touch_cpu_lock_status = 0; + } +#endif + data->power_off(); + + return 0; +} + +static int mxt_internal_resume(struct mxt_data *data) +{ + data->power_on(); + + return 0; +} + +#if DUAL_TSP +void samsung_switching_tsp_suspend(void) +{ + static const u8 sleep_power_cfg[3]={0,0,0}; + int ret; + int i=0; + + /******************************************************/ + /* One TSP has to enter suspend mode */ + /******************************************************/ + printk("[TSP] ++++samsung_switching_tsp_suspend()\n"); + if (Flip_status_tsp == FLIP_OPEN) {/* Sub_TSP need to enter suspend-mode*/ + Tsp_current_addr = MXT224S_ADDR_SUB; + gpio_set_value(GPIO_TSP_SEL, TSP_SEL_toSUB); + } else {/* Main_TSP need to enter suspend-mode*/ + Tsp_current_addr = MXT224S_ADDR_MAIN; + gpio_set_value(GPIO_TSP_SEL, TSP_SEL_toMAIN); + } + + do { + ret = write_config(copy_data, GEN_POWERCONFIG_T7, sleep_power_cfg); + msleep(20); + printk(KERN_ERR "[TSP] %s, i=%d, ret=%d \n", __func__, i, ret); + i++; + } while (ret && i < 10); + + + if (Flip_status_tsp == FLIP_OPEN) { /* return to Main_TSP*/ + Tsp_current_addr = MXT224S_ADDR_MAIN; + gpio_set_value(GPIO_TSP_SEL, TSP_SEL_toMAIN); + } else {/* return to Sub_TSP*/ + Tsp_current_addr = MXT224S_ADDR_SUB; + gpio_set_value(GPIO_TSP_SEL, TSP_SEL_toSUB); + } + copy_data->client->addr =Tsp_current_addr; + + return; + +} + +void samsung_switching_tsp_resume(void) +{ + bool ta_status = 0; + int ret; + int i=0; + struct mxt_data *data = copy_data; + + printk("[TSP]%s : addr:%02x, tspsel :%d\n", __FUNCTION__, Tsp_current_addr, gpio_get_value(GPIO_TSP_SEL)); + + if (Tsp_main_initialized == 0) { + /******************************************************/ + /* Main TSP or Sub TSP ini */ + /******************************************************/ + Tsp_main_initialized = 1; + + printk("[TSP] samsung_switching_tsp_resume() : Main TSP init #############\n"); + if (data->read_ta_status) { + data->read_ta_status(&ta_status); + pr_info("ta_status is %d\n", ta_status); + mxt_ta_probe(ta_status); + } + #ifdef CONFIG_READ_FROM_FILE + else{ + mxt_download_config(data, MXT_BATT_CFG_NAME); + } + #endif + calibrate_chip_e(); + } + + if (Tsp_sub_initialized == 0) { + /******************************************************/ + /* Sub TSP init */ + /******************************************************/ + Tsp_sub_initialized = 1; + + printk("[TSP] samsung_switching_tsp_resume() :Sub TSP init #############\n"); + if (data->read_ta_status) { + data->read_ta_status(&ta_status); + pr_info("ta_status is %d\n", ta_status); + mxt_ta_probe(ta_status); + } + #ifdef CONFIG_READ_FROM_FILE + else{ + mxt_download_config(data, MXT_BATT_CFG_NAME); + } + #endif + calibrate_chip_e(); + } + + + do { + ret = write_config(copy_data, GEN_POWERCONFIG_T7, copy_data->power_cfg); + msleep(20); + printk(KERN_ERR "[TSP] %s, i=%d,r=%d \n",__func__, i, ret); + i++; + } while (ret && i < 10); + + + return; +} + +void samsung_switching_tsp(int flip) +{ + if (Tsp_probe_passed == 0) + return; + + printk("[TSP]%s\n", __FUNCTION__); + printk("[TSP] Flip_status_tsp:%s, flip:%d(hallSW:%d)\n", Flip_status_tsp ? "FLIP OPEN" : "FLIP OPEN", flip, gpio_get_value(GPIO_HALL_SW)); + printk( "[TSP] tspsel:%d, addr:%02x, current addr:%02x\n", gpio_get_value(GPIO_TSP_SEL), copy_data->client->addr, Tsp_current_addr); + + if (Flip_status_tsp == FLIP_NOTINIT) { + Flip_status_tsp = flip; + samsung_switching_tsp_suspend(); + return; + } + + disable_irq(copy_data->client->irq); /* do not accept tsp irq before folder open/close complete */ + if (mxt_enabled == 0) { + Flip_status_tsp = flip; + } else { + if (Flip_status_tsp != flip) { + Flip_status_tsp = flip; + samsung_switching_tsp_suspend(); + samsung_switching_tsp_resume(); + } + } + enable_irq(copy_data->client->irq); /* enable tsp irq again */ + + return; +} +EXPORT_SYMBOL(samsung_switching_tsp); + +#endif // DUAL_TSP + +#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 (mxt_enabled == 1) { + pr_info("%s\n", __func__); + mxt_enabled = 0; + touch_is_pressed = 0; + disable_irq(data->client->irq); + mxt_internal_suspend(data); + Tsp_main_initialized = 0; + Tsp_sub_initialized = 0; + } else + pr_err("%s. but touch already off\n", __func__); +} + +static void mxt_late_resume(struct early_suspend *h) +{ + bool ta_status = 0; + struct mxt_data *data = container_of(h, struct mxt_data, + early_suspend); + + if (mxt_enabled == 0) { + pr_info("[TSP] +%s\n", __func__); + mxt_internal_resume(data); + + mxt_enabled = 1; + +#if DUAL_TSP + if (Flip_status_tsp == FLIP_OPEN) { + /******************************************************/ + /* Main TSP or Sub TSP init */ + /******************************************************/ + Tsp_main_initialized = 1; + + Tsp_current_addr = MXT224S_ADDR_MAIN; + gpio_set_value(GPIO_TSP_SEL, TSP_SEL_toMAIN); + printk("[TSP] mxt_late_resume() : Main TSP init #############\n"); + if (data->read_ta_status) { + data->read_ta_status(&ta_status); + pr_info("ta_status is %d\n", ta_status); + mxt_ta_probe(ta_status); + } + #ifdef CONFIG_READ_FROM_FILE + else{ + mxt_download_config(data, MXT_BATT_CFG_NAME); + } + #endif + calibrate_chip_e(); + } else { + /******************************************************/ + /* Sub TSP init */ + /******************************************************/ + Tsp_sub_initialized = 1; + + Tsp_current_addr = MXT224S_ADDR_SUB; + gpio_set_value(GPIO_TSP_SEL, TSP_SEL_toSUB); + printk("[TSP] mxt_late_resume() :Sub TSP init #############\n"); + if (data->read_ta_status) { + data->read_ta_status(&ta_status); + pr_info("ta_status is %d\n", ta_status); + mxt_ta_probe(ta_status); + } + #ifdef CONFIG_READ_FROM_FILE + else{ + mxt_download_config(data, MXT_BATT_CFG_NAME); + } + #endif + calibrate_chip_e(); + } + +/******************************************************/ +/* One TSP has to enter suspend mode */ +/******************************************************/ + samsung_switching_tsp_suspend(); + +/******************************************************/ + treat_median_error_status = 0; + +#else + if (data->read_ta_status) { + data->read_ta_status(&ta_status); + pr_info("ta_status is %d\n", ta_status); + mxt_ta_probe(ta_status); + } +#ifdef CONFIG_READ_FROM_FILE + else{ + mxt_download_config(data, MXT_BATT_CFG_NAME); + } +#endif + treat_median_error_status = 0; + calibrate_chip_e(); +#endif + enable_irq(data->client->irq); + pr_info("[TSP] +%s\n", __func__); + } else + pr_err("%s. but touch already on\n", __func__); +} + +#else +static int mxt_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mxt_data *data = i2c_get_clientdata(client); + + mxt_enabled = 0; + touch_is_pressed = 0; +#if TOUCH_BOOSTER + tsp_press_status = 0; +#endif + return mxt_internal_suspend(data); +} + +static int mxt_resume(struct device *dev) +{ + int ret = 0; + bool ta_status = 0; + struct i2c_client *client = to_i2c_client(dev); + struct mxt_data *data = i2c_get_clientdata(client); + + ret = mxt_internal_resume(data); + + mxt_enabled = 1; + + if (data->read_ta_status) { + data->read_ta_status(&ta_status); + pr_info("ta_status is %d\n", ta_status); + mxt_ta_probe(ta_status); + } +#ifdef CONFIG_READ_FROM_FILE + else{ + mxt_download_config(data, MXT_BATT_CFG_NAME); + } +#endif + return ret; +} +#endif + +#if FORCE_RELEASE +static void Mxt_force_released(void) +{ + struct mxt_data *data = copy_data; + int i; + + if (!mxt_enabled) { + pr_err("mxt_enabled is 0\n"); + return; + } + + 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; + } + report_input_data(data); + + calibrate_chip_e(); +}; +#endif + +#if SYSFS +static ssize_t mxt_debug_setting(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + g_debug_switch = !g_debug_switch; + return 0; +} + +static ssize_t mxt_object_setting(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + unsigned int object_type; + unsigned int object_register; + unsigned int register_value; + u8 value; + u8 val; + int ret; + u16 address; + u16 size; + sscanf(buf, "%u%u%u", &object_type, &object_register, ®ister_value); + pr_info("object type T%d", object_type); + pr_info("object register ->Byte%d\n", object_register); + pr_info("register value %d\n", register_value); + ret = get_object_info(data, (u8)object_type, &size, &address); + if (ret) { + pr_err("fail to get object_info\n"); + return count; + } + + size = 1; + value = (u8)register_value; + write_mem(data, address+(u16)object_register, size, &value); + read_mem(data, address+(u16)object_register, (u8)size, &val); + + pr_info("T%d Byte%d is %d\n", + object_type, object_register, val); + return count; +} + +static ssize_t mxt_object_show(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + unsigned int object_type; + u8 val; + int ret; + u16 address; + u16 size; + u16 i; + sscanf(buf, "%u", &object_type); + pr_info("object type T%d\n", object_type); + ret = get_object_info(data, (u8)object_type, &size, &address); + if (ret) { + pr_err("fail to get object_info\n"); + return count; + } + for (i = 0; i < size; i++) { + read_mem(data, address+i, 1, &val); + pr_info("Byte %u --> %u\n", i, val); + } + return count; +} + +static struct device *sec_touchscreen; +static struct device *mxt_noise_test; +/* + top_left, top_right, center, bottom_left, bottom_right +*/ +static unsigned char test_node[5] = {15, 23, 145, 239, 247}; +uint16_t mxt_delta_node[384] = { 0 }; +uint16_t mxt_refer_node[384] = { 0 }; +static int index_reference; +static int index_delta; + +static void diagnostic_chip(u8 mode) +{ + int error; + u16 t6_address = 0; + u16 size_one; + int ret; + u8 value; + u16 t37_address = 0; + + ret = get_object_info(copy_data, + GEN_COMMANDPROCESSOR_T6, &size_one, &t6_address); + + size_one = 1; + error = write_mem(copy_data, t6_address+5, (u8)size_one, &mode); + /* QT602240_COMMAND_DIAGNOSTIC, mode); */ + if (error < 0) { + pr_err("error %s: write_object\n", __func__); + } else { + get_object_info(copy_data, + DEBUG_DIAGNOSTIC_T37, &size_one, &t37_address); + size_one = 1; + /* pr_info("diagnostic_chip setting success\n"); */ + read_mem(copy_data, t37_address, (u8)size_one, &value); + /* pr_info("dianostic_chip mode is %d\n",value); */ + } +} + +static uint8_t read_uint16_t(struct mxt_data *data, uint16_t address, uint16_t *buf) +{ + uint8_t status; + uint8_t temp[2]; + + status = read_mem(data, address, 2, temp); + *buf = ((uint16_t)temp[1]<<8) + (uint16_t)temp[0]; + + return status; +} + +static void read_dbg_data(uint8_t dbg_mode , uint16_t node, uint16_t *dbg_data) +{ + u8 read_page, read_point; + uint8_t mode, page; + u16 size; + u16 diagnostic_addr = 0; + + if (!mxt_enabled) { + pr_err("read_dbg_data. mxt_enabled is 0\n"); + return; + } + + get_object_info(copy_data, + DEBUG_DIAGNOSTIC_T37, &size, &diagnostic_addr); + + read_page = node / 64; + node %= 64; + read_point = (node * 2) + 2; + + /* Page Num Clear */ + diagnostic_chip(MXT_CTE_MODE); + msleep(20); + + do { + if (read_mem(copy_data, diagnostic_addr, 1, &mode)) { + pr_info("READ_MEM_FAILED\n"); + return; + } + } while (mode != MXT_CTE_MODE); + + diagnostic_chip(dbg_mode); + msleep(20); + + do { + if (read_mem(copy_data, diagnostic_addr, 1, &mode)) { + pr_info("READ_MEM_FAILED\n"); + return; + } + } while (mode != dbg_mode); + + for (page = 1; page <= read_page; page++) { + diagnostic_chip(MXT_PAGE_UP); + msleep(20); + do { + if (read_mem(copy_data, + diagnostic_addr + 1, 1, &mode)) { + pr_info("READ_MEM_FAILED\n"); + return; + } + } while (mode != page); + } + + if (read_uint16_t(copy_data, diagnostic_addr + read_point, dbg_data)) { + pr_info("READ_MEM_FAILED\n"); + return; + } +} + +#define MIN_VALUE 19744 +#define MAX_VALUE 28884 +static u16 max_ref, min_ref; + +static int read_all_data(uint16_t dbg_mode) +{ + u8 read_page, read_point; + u16 max_value = MIN_VALUE, min_value = MAX_VALUE; + u16 object_address = 0; + u8 data_buffer[2] = { 0 }; + u8 node = 0; + int state = 0; + int num = 0; + int ret; + u16 size; + u8 val; + bool dual_x_mode = 0; + + ret = get_object_info(copy_data, PROCG_NOISESUPPRESSION_T62, &size, &object_address); + read_mem(copy_data, object_address + 3, 1, &val); + if (val & 0x01) + dual_x_mode = 1; + + /* Page Num Clear */ + diagnostic_chip(MXT_CTE_MODE); + msleep(30);/* msleep(20); */ + + diagnostic_chip(dbg_mode); + msleep(30);/* msleep(20); */ + + ret = get_object_info(copy_data, + DEBUG_DIAGNOSTIC_T37, &size, &object_address); + /*jerry no need to leave it */ + msleep(50); /* msleep(20); */ + + for (read_page = 0 ; read_page < 4; read_page++) { + for (node = 0; node < 64; node++) { + read_point = (node * 2) + 2; + read_mem(copy_data, object_address + (u16)read_point, 2, data_buffer); + mxt_refer_node[num] = ((u16)data_buffer[1]<<8) + (u16)data_buffer[0]; + + if ((mxt_refer_node[num] > MIN_VALUE) || (mxt_refer_node[num] < MAX_VALUE)) { + state = 1; + printk(KERN_ERR + "[TSP] mxt_refer_node[%3d] = %5d\n", + num, mxt_refer_node[num]); + } + + if (data_buffer[0] != 0) { + if (mxt_refer_node[num] != 0) { + if (mxt_refer_node[num] > max_value) + max_value = mxt_refer_node[num]; + if (mxt_refer_node[num] < min_value) + min_value = mxt_refer_node[num]; + } + } + num++; + /* all node => 19 * 11 = 209 => (3page * 64) + 17 */ + if ((read_page == 3) && (node == 16)) + break; + } + diagnostic_chip(MXT_PAGE_UP); + msleep(20); + } + + if ((max_value - min_value) > 4000) { + printk(KERN_ERR + "[TSP] diff = %d, max_value = %d, min_value = %d\n", + (max_value - min_value), max_value, min_value); + state = 1; + } + + max_ref = max_value; + min_ref = min_value; + + return state; +} + +static int read_all_delta_data(uint16_t dbg_mode) +{ + u8 read_page, read_point; + u16 object_address = 0; + u8 data_buffer[2] = { 0 }; + u8 node = 0; + int state = 0; + int num = 0; + int ret; + u16 size; + + if (!mxt_enabled) { + pr_err("%s : mxt_enabled is 0\n", __func__); + return 1; + } + + /* Page Num Clear */ + diagnostic_chip(MXT_CTE_MODE); + msleep(30);/* msleep(20); */ + + diagnostic_chip(dbg_mode); + msleep(30);/* msleep(20); */ + + ret = get_object_info(copy_data, + DEBUG_DIAGNOSTIC_T37, &size, &object_address); +/*jerry no need to leave it */ +/* + for (i = 0; i < 5; i++) { + if (data_buffer[0] == dbg_mode) + break; + + msleep(20); + } +*/ + msleep(50); /* msleep(20); */ + + + /* 768/64 */ + for (read_page = 0 ; read_page < 6; read_page++) { + for (node = 0; node < 64; node++) { + read_point = (node * 2) + 2; + read_mem(copy_data, + object_address+(u16)read_point, 2, data_buffer); + mxt_delta_node[num] = + ((uint16_t)data_buffer[1]<<8) + + (uint16_t)data_buffer[0]; + + printk(KERN_ERR + "[TSP] mxt_delta_node[%3d] = %5d\n", + num, mxt_delta_node[num]); + + num++; + + /* all node => 24 * 32 = 768 => (12page * 64) */ + /*if ((read_page == 11) && (node == 64)) + break;*/ + } + diagnostic_chip(MXT_PAGE_UP); + msleep(35); + } + + return state; +} + +int find_channel(uint16_t dbg_mode) +{ + u8 read_page, read_point; + u16 object_address = 0; + u8 data_buffer[2] = { 0 }; + u8 node = 0; + int state = 0; + int num = 0; + int ret; + u16 size; + u16 delta_val = 0; + u16 max_val = 0; + + if (!mxt_enabled) { + pr_err("%s : mxt_enabled is 0\n", __func__); + return 1; + } + + /* Page Num Clear */ + diagnostic_chip(MXT_CTE_MODE); + msleep(30);/* msleep(20); */ + + diagnostic_chip(dbg_mode); + msleep(30);/* msleep(20); */ + + ret = get_object_info(copy_data, + DEBUG_DIAGNOSTIC_T37, &size, &object_address); +/*jerry no need to leave it */ +/* + for (i = 0; i < 5; i++) { + if (data_buffer[0] == dbg_mode) + break; + + msleep(20); + } +*/ + msleep(50); /* msleep(20); */ + + + /* 768/64 */ + for (read_page = 0 ; read_page < 12; read_page++) { + for (node = 0; node < 64; node++) { + read_point = (node * 2) + 2; + read_mem(copy_data, + object_address+(u16)read_point, 2, data_buffer); + delta_val = ((uint16_t)data_buffer[1]<<8) + + (uint16_t)data_buffer[0]; + + if (delta_val > 32767) + delta_val = 65535 - delta_val; + if (delta_val > max_val) { + max_val = delta_val; + state = (read_point - 2)/2 + + (read_page * 64); + } + + num++; + + /* all node => 24 * 32 = 768 => (12page * 64) */ + /*if ((read_page == 11) && (node == 64)) + break;*/ + } + diagnostic_chip(MXT_PAGE_UP); + msleep(35); + } + + return state; +} + +static ssize_t find_channel_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int status = 0; + + status = find_channel(MXT_DELTA_MODE); + + return sprintf(buf, "%u\n", status); +} +#endif + +static int mxt_check_bootloader(struct i2c_client *client, + unsigned int state) +{ + u8 val; + u8 temp; + +recheck: + if (i2c_master_recv(client, &val, 1) != 1) + return -EIO; + + if (val & 0x20) { + if (i2c_master_recv(client, &temp, 1) != 1) + return -EIO; + + if (i2c_master_recv(client, &temp, 1) != 1) + return -EIO; + val &= ~0x20; + } + + if ((val & 0xF0) == MXT_APP_CRC_FAIL) { + pr_info("MXT_APP_CRC_FAIL\n"); + if (i2c_master_recv(client, &val, 1) != 1) + return -EIO; + + if (val & 0x20) { + if (i2c_master_recv(client, &temp, 1) != 1) + return -EIO; + + if (i2c_master_recv(client, &temp, 1) != 1) + return -EIO; + val &= ~0x20; + } + } + + switch (state) { + case MXT_WAITING_BOOTLOAD_CMD: + case MXT_WAITING_FRAME_DATA: + val &= ~MXT_BOOT_STATUS_MASK; + break; + case MXT_FRAME_CRC_PASS: + if (val == MXT_FRAME_CRC_CHECK) + goto recheck; + break; + default: + return -EINVAL; + } + + if (val != state) { + pr_err("Unvalid bootloader mode state\n"); + return -EINVAL; + } + + return 0; +} + +static int mxt_unlock_bootloader(struct i2c_client *client) +{ + u8 buf[2]; + + buf[0] = MXT_UNLOCK_CMD_LSB; + buf[1] = MXT_UNLOCK_CMD_MSB; + + if (i2c_master_send(client, buf, 2) != 2) { + pr_err("%s: i2c send failed\n", + __func__); + + return -EIO; + } + + return 0; +} + +static int mxt_fw_write(struct i2c_client *client, + const u8 *data, unsigned int frame_size) +{ + if (i2c_master_send(client, data, frame_size) != frame_size) { + pr_err("%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int mxt_load_fw(struct device *dev, const char *fn) +{ + struct mxt_data *data = copy_data; + struct i2c_client *client = copy_data->client; + unsigned int frame_size; + unsigned int pos = 0; + int ret; + u16 obj_address = 0; + u16 size_one; + u8 value; + unsigned int object_register; + int check_frame_crc_error = 0; + int check_wating_frame_data_error = 0; + +#if READ_FW_FROM_HEADER + struct firmware *fw = NULL; + + pr_info("mxt_load_fw start from header!!!\n"); + fw = kzalloc(sizeof(struct firmware), GFP_KERNEL); + + fw->data = firmware_mXT; + fw->size = sizeof(firmware_mXT); +#else + const struct firmware *fw = NULL; + + pr_info("mxt_load_fw startl!!!\n"); + ret = request_firmware(&fw, fn, &client->dev); + if (ret) { + pr_err("Unable to open firmware %s\n", fn); + return ret; + } +#endif + /* Change to the bootloader mode */ + object_register = 0; + value = (u8)MXT_BOOT_VALUE; + ret = get_object_info(data, + GEN_COMMANDPROCESSOR_T6, &size_one, &obj_address); + if (ret) { + pr_err("fail to get object_info\n"); + release_firmware(fw); + return ret; + } + size_one = 1; + write_mem(data, obj_address+(u16)object_register, (u8)size_one, &value); + msleep(MXT_SW_RESET_TIME); + + /* Change to slave address of bootloader */ + if (client->addr == MXT_APP_LOW) + client->addr = MXT_BOOT_LOW; + else + client->addr = MXT_BOOT_HIGH; + + ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD); + if (ret) + goto out; + + /* Unlock bootloader */ + mxt_unlock_bootloader(client); + + while (pos < fw->size) { + ret = mxt_check_bootloader(client, + MXT_WAITING_FRAME_DATA); + if (ret) { + check_wating_frame_data_error++; + if (check_wating_frame_data_error > 10) { + pr_err("firm update fail. wating_frame_data err\n"); + goto out; + } else { + pr_err("check_wating_frame_data_error = %d, retry\n", + check_wating_frame_data_error); + continue; + } + } + + 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) { + check_frame_crc_error++; + if (check_frame_crc_error > 10) { + pr_err("firm update fail. frame_crc err\n"); + goto out; + } else { + pr_err("check_frame_crc_error = %d, retry\n", + check_frame_crc_error); + continue; + } + } + + pos += frame_size; + + pr_info("Updated %d bytes / %zd bytes\n", + pos, fw->size); + + msleep(20); + } + +out: +#if READ_FW_FROM_HEADER + kfree(fw); +#else + release_firmware(fw); +#endif + /* Change to slave address of application */ + if (client->addr == MXT_BOOT_LOW) + client->addr = MXT_APP_LOW; + else + client->addr = MXT_APP_HIGH; + return ret; +} + +static int mxt_load_fw_bootmode(struct device *dev, const char *fn) +{ + struct i2c_client *client = copy_data->client; + unsigned int frame_size; + unsigned int pos = 0; + int ret; + + int check_frame_crc_error = 0; + int check_wating_frame_data_error = 0; + +#if READ_FW_FROM_HEADER + struct firmware *fw = NULL; + pr_info("mxt_load_fw start from header!!!\n"); + fw = kzalloc(sizeof(struct firmware), GFP_KERNEL); + + fw->data = firmware_mXT; + fw->size = sizeof(firmware_mXT); +#else + const struct firmware *fw = NULL; + pr_info("mxt_load_fw start!!!\n"); + + ret = request_firmware(&fw, fn, &client->dev); + if (ret) { + pr_err("Unable to open firmware %s\n", fn); + return ret; + } +#endif + /* Unlock bootloader */ + mxt_unlock_bootloader(client); + + while (pos < fw->size) { + ret = mxt_check_bootloader(client, + MXT_WAITING_FRAME_DATA); + if (ret) { + check_wating_frame_data_error++; + if (check_wating_frame_data_error > 10) { + pr_err("firm update fail. wating_frame_data err\n"); + goto out; + } else { + pr_err("check_wating_frame_data_error = %d, retry\n", + check_wating_frame_data_error); + continue; + } + } + + 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) { + check_frame_crc_error++; + if (check_frame_crc_error > 10) { + pr_err("firm update fail. frame_crc err\n"); + goto out; + } else { + pr_err("check_frame_crc_error = %d, retry\n", + check_frame_crc_error); + continue; + } + } + + pos += frame_size; + + pr_info("Updated %d bytes / %zd bytes\n", + pos, fw->size); + + msleep(20); + } + +out: +#if READ_FW_FROM_HEADER + kfree(fw); +#else + release_firmware(fw); +#endif + /* Change to slave address of application */ + if (client->addr == MXT_BOOT_LOW) + client->addr = MXT_APP_LOW; + else + client->addr = MXT_APP_HIGH; + return ret; +} + +#if SYSFS +static ssize_t set_refer0_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint16_t mxt_reference = 0; + read_dbg_data(MXT_REFERENCE_MODE, test_node[0], &mxt_reference); + return sprintf(buf, "%u\n", mxt_reference); +} + +static ssize_t set_refer1_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint16_t mxt_reference = 0; + read_dbg_data(MXT_REFERENCE_MODE, test_node[1], &mxt_reference); + return sprintf(buf, "%u\n", mxt_reference); +} + +static ssize_t set_refer2_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint16_t mxt_reference = 0; + read_dbg_data(MXT_REFERENCE_MODE, test_node[2], &mxt_reference); + return sprintf(buf, "%u\n", mxt_reference); +} + +static ssize_t set_refer3_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint16_t mxt_reference = 0; + read_dbg_data(MXT_REFERENCE_MODE, test_node[3], &mxt_reference); + return sprintf(buf, "%u\n", mxt_reference); +} + +static ssize_t set_refer4_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint16_t mxt_reference = 0; + read_dbg_data(MXT_REFERENCE_MODE, test_node[4], &mxt_reference); + return sprintf(buf, "%u\n", mxt_reference); +} + +static ssize_t set_delta0_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint16_t mxt_delta = 0; + read_dbg_data(MXT_DELTA_MODE, test_node[0], &mxt_delta); + if (mxt_delta < 32767) + return sprintf(buf, "%u\n", mxt_delta); + else + mxt_delta = 65535 - mxt_delta; + + if (mxt_delta) + return sprintf(buf, "-%u\n", mxt_delta); + else + return sprintf(buf, "%u\n", mxt_delta); +} + +static ssize_t set_delta1_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint16_t mxt_delta = 0; + read_dbg_data(MXT_DELTA_MODE, test_node[1], &mxt_delta); + if (mxt_delta < 32767) + return sprintf(buf, "%u\n", mxt_delta); + else + mxt_delta = 65535 - mxt_delta; + + if (mxt_delta) + return sprintf(buf, "-%u\n", mxt_delta); + else + return sprintf(buf, "%u\n", mxt_delta); +} + +static ssize_t set_delta2_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint16_t mxt_delta = 0; + read_dbg_data(MXT_DELTA_MODE, test_node[2], &mxt_delta); + if (mxt_delta < 32767) + return sprintf(buf, "%u\n", mxt_delta); + else + mxt_delta = 65535 - mxt_delta; + + if (mxt_delta) + return sprintf(buf, "-%u\n", mxt_delta); + else + return sprintf(buf, "%u\n", mxt_delta); +} + +static ssize_t set_delta3_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint16_t mxt_delta = 0; + read_dbg_data(MXT_DELTA_MODE, test_node[3], &mxt_delta); + if (mxt_delta < 32767) + return sprintf(buf, "%u\n", mxt_delta); + else + mxt_delta = 65535 - mxt_delta; + + if (mxt_delta) + return sprintf(buf, "-%u\n", mxt_delta); + else + return sprintf(buf, "%u\n", mxt_delta); +} + +static ssize_t set_delta4_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint16_t mxt_delta = 0; + read_dbg_data(MXT_DELTA_MODE, test_node[4], &mxt_delta); + if (mxt_delta < 32767) + return sprintf(buf, "%u\n", mxt_delta); + else + mxt_delta = 65535 - mxt_delta; + + if (mxt_delta) + return sprintf(buf, "-%u\n", mxt_delta); + else + return sprintf(buf, "%u\n", mxt_delta); +} + +static ssize_t set_threshold_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", threshold); +} + +static ssize_t set_all_refer_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int status = 0; + + status = read_all_data(MXT_REFERENCE_MODE); + + return snprintf(buf, 20, + "%u, %u, %u\n", status, max_ref, min_ref); +} + +static int atoi(const char *str) +{ + int result = 0; + int count = 0; + if (str == NULL) + return -1; + while (str[count] && str[count] >= '0' && str[count] <= '9') { + result = result * 10 + str[count] - '0'; + ++count; + } + return result; +} + +static ssize_t disp_all_refdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", mxt_refer_node[index_reference]); +} + +static ssize_t disp_all_refdata_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + index_reference = atoi(buf); + return size; +} + +static ssize_t set_all_delta_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int status = 0; + + status = read_all_delta_data(MXT_DELTA_MODE); + + return sprintf(buf, "%u\n", status); +} + +static ssize_t disp_all_deltadata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (mxt_delta_node[index_delta] < 32767) + return sprintf(buf, "%u\n", mxt_delta_node[index_delta]); + else + mxt_delta_node[index_delta] = 65535 - mxt_delta_node[index_delta]; + + return sprintf(buf, "-%u\n", mxt_delta_node[index_delta]); +} + +static ssize_t disp_all_deltadata_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + index_delta = atoi(buf); + return size; +} + +static ssize_t set_firm_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = copy_data; + + u8 id[ID_BLOCK_SIZE]; + u8 value; + int ret; + u8 i; + + if (mxt_enabled == 1) { + disable_irq(data->client->irq); + for (i = 0; i < 4; i++) { + ret = read_mem(copy_data, 0, sizeof(id), id); + if (!ret) + break; + } + enable_irq(data->client->irq); + if (ret < 0) { + pr_err("TSP read fail : %s", __func__); + value = 0; + return sprintf(buf, "%d\n", value); + } else { + pr_info("%s : %#02x\n", + __func__, id[2]); + return sprintf(buf, "%#02x\n", id[2]); + } + } else { + pr_err("TSP power off : %s", __func__); + value = 0; + return sprintf(buf, "%d\n", value); + } +} + +static ssize_t set_module_off_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct mxt_data *data = copy_data; + pr_info("%s!!\n", __func__); + + if (*buf != 'S' && *buf != 'F') { + pr_err("Invalid values\n"); + return -EINVAL; + } + if (mxt_enabled == 1) { + mxt_enabled = 0; + touch_is_pressed = 0; +#if TOUCH_BOOSTER + tsp_press_status = 0; +#endif + disable_irq(data->client->irq); + mxt_internal_suspend(data); + } + return size; +} + +static ssize_t set_module_on_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct mxt_data *data = copy_data; + bool ta_status = 0; + + pr_info("%s!!\n", __func__); + + if (*buf != 'S' && *buf != 'F') { + pr_err("Invalid values\n"); + return -EINVAL; + } + + if (mxt_enabled == 0) { + mxt_internal_resume(data); + enable_irq(data->client->irq); + + mxt_enabled = 1; + + if (data->read_ta_status) { + data->read_ta_status(&ta_status); + pr_info("ta_status is %d\n", ta_status); + mxt_ta_probe(ta_status); + } +#ifdef CONFIG_READ_FROM_FILE + else{ + mxt_download_config(data, MXT_BATT_CFG_NAME); + } +#endif + calibrate_chip_e(); + } + + return size; +} + +#if FOR_DEBUGGING_TEST_DOWNLOADFW_BIN +#include <linux/uaccess.h> + +#define MXT_FW_BIN_NAME "/sdcard/mxt.bin" + +static int mxt_download(const u8 *pBianry, const u32 unLength) +{ + struct mxt_data *data = copy_data; + struct i2c_client *client = copy_data->client; + unsigned int frame_size; + unsigned int pos = 0; + int ret; + u16 obj_address = 0; + u16 size_one; + u8 value; + unsigned int object_register; + int check_frame_crc_error = 0; + int check_wating_frame_data_error = 0; + + pr_info("mxt_download start!!!\n"); + + /* Change to the bootloader mode */ + object_register = 0; + value = (u8)MXT_BOOT_VALUE; + ret = get_object_info(data, + GEN_COMMANDPROCESSOR_T6, &size_one, &obj_address); + if (ret) { + pr_err("fail to get object_info\n"); + return ret; + } + size_one = 1; + + write_mem(data, obj_address+(u16)object_register, (u8)size_one, &value); + msleep(MXT_SW_RESET_TIME); + + /* Change to slave address of bootloader */ + if (client->addr == MXT_APP_LOW) + client->addr = MXT_BOOT_LOW; + else + client->addr = MXT_BOOT_HIGH; + ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD); + + if (ret) + goto out; + + /* Unlock bootloader */ + mxt_unlock_bootloader(client); + + while (pos < unLength) { + ret = mxt_check_bootloader(client, + MXT_WAITING_FRAME_DATA); + if (ret) { + check_wating_frame_data_error++; + if (check_wating_frame_data_error > 10) { + pr_err("firm update fail. wating_frame_data err\n"); + goto out; + } else { + pr_info("check_wating_frame_data_error=%d, retry\n", + check_wating_frame_data_error); + continue; + } + } + + frame_size = ((*(pBianry + pos) << 8) | *(pBianry + 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, pBianry + pos, frame_size); + + ret = mxt_check_bootloader(client, + MXT_FRAME_CRC_PASS); + if (ret) { + check_frame_crc_error++; + if (check_frame_crc_error > 10) { + pr_err("firm update fail. frame_crc err\n"); + goto out; + } else { + pr_info("check_frame_crc_error = %d, retry\n", + check_frame_crc_error); + continue; + } + } + + pos += frame_size; + + pr_info("Updated %d bytes / %zd bytes\n", pos, unLength); + + msleep(20); + } + +out: + /* Change to slave address of application */ + if (client->addr == MXT_BOOT_LOW) + client->addr = MXT_APP_LOW; + else + client->addr = MXT_APP_HIGH; + return ret; +} + +int mxt_binfile_download(void) +{ + int nRet = 0; + int retry_cnt = 0; + long fw1_size = 0; + unsigned char *fw_data1; + struct file *filp; + loff_t pos; + int ret = 0; + mm_segment_t oldfs; + spinlock_t lock; + + oldfs = get_fs(); + set_fs(get_ds()); + + filp = filp_open(MXT_FW_BIN_NAME, O_RDONLY, 0); + if (IS_ERR(filp)) { + pr_err("file open error:%d\n", (s32)filp); + return -1; + } + + fw1_size = filp->f_path.dentry->d_inode->i_size; + pr_info("Size of the file : %ld(bytes)\n", fw1_size); + + fw_data1 = kmalloc(fw1_size, GFP_KERNEL); + memset(fw_data1, 0, fw1_size); + + pos = 0; + memset(fw_data1, 0, fw1_size); + ret = vfs_read(filp, (char __user *)fw_data1, fw1_size, &pos); + + if (ret != fw1_size) { + pr_err("Failed to read file %s (ret = %d)\n", + MXT_FW_BIN_NAME, ret); + kfree(fw_data1); + filp_close(filp, current->files); + return -1; + } + + filp_close(filp, current->files); + + set_fs(oldfs); + + for (retry_cnt = 0; retry_cnt < 3; retry_cnt++) { + pr_info("ADB - MASTER CHIP Firmware update! try : %d", + retry_cnt+1); + nRet = mxt_download((const u8 *)fw_data1, (const u32)fw1_size); + if (nRet) + continue; + break; + } + + kfree(fw_data1); + return nRet; +} +#endif + +static ssize_t set_mxt_firm_update_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct mxt_data *data = dev_get_drvdata(dev); + u8 **tsp_config = (u8 **)data->pdata->config; + int i = 0; + int error = 0; + + pr_info("set_mxt_update_show start!!\n"); + if (*buf != 'S' && *buf != 'F') { + pr_err("Invalid values\n"); + return -EINVAL; + } + + disable_irq(data->client->irq); + firm_status_data = 1; +#if FOR_DEBUGGING_TEST_DOWNLOADFW_BIN + error = mxt_binfile_download(); +#else + if (*buf != 'F' && data->tsp_version >= firmware_latest[0] + && data->tsp_build >= firmware_latest[1]) { + pr_err("latest firmware\n"); + firm_status_data = 2; + enable_irq(data->client->irq); + return size; + } + pr_info("fm_update\n"); + error = mxt_load_fw(dev, MXT_FW_NAME); +#endif + + if (error) { + firm_status_data = 3; + pr_err("The firmware update failed(%d)\n", error); + return error; + } else { + firm_status_data = 2; + pr_info("The firmware update succeeded\n"); + + /* Wait for reset */ + msleep(MXT_SW_RESET_TIME); + + mxt_init_touch_driver(data); + /* mxt224_initialize(data); */ + } + + for (i = 0; tsp_config[i][0] != RESERVED_T255; i++) { + error = init_write_config(data, tsp_config[i][0], + tsp_config[i] + 1); + if (error) { + pr_err("init_write_config error\n"); + firm_status_data = 3; + return error; + } + } + + error = mxt_backup(data); + if (error) { + pr_err("mxt_backup fail!!!\n"); + return error; + } + + /* reset the touch IC. */ + error = mxt_reset(data); + if (error) { + pr_err("mxt_reset fail!!!\n"); + return error; + } + + msleep(MXT_SW_RESET_TIME); + enable_irq(data->client->irq); + + return size; +} + +static ssize_t set_mxt_firm_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + int count; + pr_info("Enter firmware_status_show by Factory command\n"); + + if (firm_status_data == 1) + count = sprintf(buf, "DOWNLOADING\n"); + else if (firm_status_data == 2) + count = sprintf(buf, "PASS\n"); + else if (firm_status_data == 3) + count = sprintf(buf, "FAIL\n"); + else + count = sprintf(buf, "PASS\n"); + + return count; +} + +static ssize_t key_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", threshold); +} + +static ssize_t key_threshold_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + /*TO DO IT*/ + unsigned int object_register = 7; + u8 value; + u8 val; + int ret; + u16 address = 0; + u16 size_one; + int num; + if (sscanf(buf, "%d", &num) == 1) { + threshold = num; + pr_info("threshold value %d\n", threshold); + ret = get_object_info(copy_data, + TOUCH_MULTITOUCHSCREEN_T9, &size_one, &address); + size_one = 1; + value = (u8)threshold; + write_mem(copy_data, + address+(u16)object_register, size_one, &value); + read_mem(copy_data, + address+(u16)object_register, (u8)size_one, &val); + pr_err("T9 Byte%d is %d\n", object_register, val); + } + return size; +} + +static ssize_t set_mxt_firm_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + pr_info("phone's version : %#02x,%#02x\n" + , firmware_latest[0], firmware_latest[1]); + return sprintf(buf, "%#02x\n", firmware_latest[0]); +} + +static ssize_t set_mxt_firm_version_read_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + pr_info("phone's version : %#02x,%#02x\n" + , data->tsp_version, data->tsp_build); + return sprintf(buf, "%#02x\n", data->tsp_version); +} + +static ssize_t set_mxt_config_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 20, + "%s\n", data->pdata->config_fw_version); +} + +static ssize_t mxt_touchtype_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char temp[15]; + + sprintf(temp, "ATMEL,MXT224S\n"); + strcat(buf, temp); + + return strlen(buf); +} + +static ssize_t x_line_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data = 24; + return sprintf(buf, "%d\n", data); +} + +static ssize_t y_line_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data = 32; + return sprintf(buf, "%d\n", data); +} + +#if ITDEV +/* Functions for mem_access interface */ +struct bin_attribute mem_access_attr; + +static int mxt_read_block(struct i2c_client *client, + u16 addr, + u16 length, + u8 *value) +{ + struct i2c_adapter *adapter = client->adapter; + struct i2c_msg msg[2]; + __le16 le_addr; + struct mxt_data *mxt; + + mxt = i2c_get_clientdata(client); + + if (mxt != NULL) { + if ((mxt->last_read_addr == addr) && + (addr == mxt->msg_proc_addr)) { + if (i2c_master_recv(client, value, length) == length) { +#if ITDEV + if (debug_enabled) + print_hex_dump(KERN_INFO, "MXT RX:", + DUMP_PREFIX_NONE, 16, 1, + value, length, false); +#endif + return 0; + } else + return -EIO; + } else { + mxt->last_read_addr = addr; + } + } + + le_addr = cpu_to_le16(addr); + msg[0].addr = client->addr; + msg[0].flags = 0x00; + msg[0].len = 2; + msg[0].buf = (u8 *) &le_addr; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = length; + msg[1].buf = (u8 *) value; + if (i2c_transfer(adapter, msg, 2) == 2) { +#if ITDEV + if (debug_enabled) { + print_hex_dump(KERN_INFO, "MXT TX:", DUMP_PREFIX_NONE, + 16, 1, msg[0].buf, msg[0].len, false); + print_hex_dump(KERN_INFO, "MXT RX:", DUMP_PREFIX_NONE, + 16, 1, msg[1].buf, msg[1].len, false); + } +#endif + return 0; + } else + return -EIO; +} + +/* Writes a block of bytes (max 256) to given address in mXT chip. */ + +int mxt_write_block(struct i2c_client *client, + u16 addr, + u16 length, + u8 *value) +{ + int i; + struct { + __le16 le_addr; + u8 data[256]; + + } i2c_block_transfer; + + struct mxt_data *mxt; + + if (length > 256) + return -EINVAL; + + mxt = i2c_get_clientdata(client); + if (mxt != NULL) + mxt->last_read_addr = -1; + + for (i = 0; i < length; i++) + i2c_block_transfer.data[i] = *value++; + + i2c_block_transfer.le_addr = cpu_to_le16(addr); + + i = i2c_master_send(client, (u8 *) &i2c_block_transfer, length + 2); + + if (i == (length + 2)) { +#if ITDEV + if (debug_enabled) + print_hex_dump(KERN_INFO, "MXT TX:", DUMP_PREFIX_NONE, + 16, 1, &i2c_block_transfer, length+2, false); +#endif + return length; + } else + return -EIO; +} + +static ssize_t mem_access_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) +{ + int ret = 0; + struct i2c_client *client; + + pr_info("mem_access_read p=%p off=%lli c=%zi\n", buf, off, count); + + if (off >= 32768) + return -EIO; + + if (off + count > 32768) + count = 32768 - off; + + if (count > 256) + count = 256; + + if (count > 0) { + client = to_i2c_client(container_of(kobj, struct device, kobj)); + ret = mxt_read_block(client, off, count, buf); + } + + return ret >= 0 ? count : ret; +} + +static ssize_t mem_access_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) +{ + int ret = 0; + struct i2c_client *client; + + pr_info("mem_access_write p=%p off=%lli c=%zi\n", buf, off, count); + + if (off >= 32768) + return -EIO; + + if (off + count > 32768) + count = 32768 - off; + + if (count > 256) + count = 256; + + if (count > 0) { + client = to_i2c_client(container_of(kobj, struct device, kobj)); + ret = mxt_write_block(client, off, count, buf); + } + + return ret >= 0 ? count : 0; +} + +static ssize_t pause_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int count = 0; + + count += sprintf(buf + count, "%d", driver_paused); + count += sprintf(buf + count, "\n"); + + return count; +} + +static ssize_t pause_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int i; + if (sscanf(buf, "%u", &i) == 1 && i < 2) { + driver_paused = i; + + pr_info("%s\n", i ? "paused" : "unpaused"); + } else { + pr_info("pause_driver write error\n"); + } + + return count; +} + +static ssize_t debug_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int count = 0; + + count += sprintf(buf + count, "%d", debug_enabled); + count += sprintf(buf + count, "\n"); + + return count; +} + +static ssize_t debug_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int i; + if (sscanf(buf, "%u", &i) == 1 && i < 2) { + debug_enabled = i; + + pr_info("%s\n", + i ? "debug enabled" : "debug disabled"); + } else { + pr_info("debug_enabled write error\n"); + } + + return count; +} + +static ssize_t command_calibrate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + + ret = calibrate_chip_e(); + + return (ret < 0) ? ret : count; +} + +static ssize_t command_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client; + struct mxt_data *mxt; + int ret; + + client = to_i2c_client(dev); + mxt = i2c_get_clientdata(client); + + ret = mxt_reset(mxt); + + return (ret < 0) ? ret : count; +} + +static ssize_t command_backup_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client; + struct mxt_data *mxt; + int ret; + + client = to_i2c_client(dev); + mxt = i2c_get_clientdata(client); + + ret = mxt_backup(mxt); + + return (ret < 0) ? ret : count; +} +#endif + +static DEVICE_ATTR(set_refer0, S_IRUGO, + set_refer0_mode_show, NULL); +static DEVICE_ATTR(set_delta0, S_IRUGO, + set_delta0_mode_show, NULL); +static DEVICE_ATTR(set_refer1, S_IRUGO, + set_refer1_mode_show, NULL); +static DEVICE_ATTR(set_delta1, S_IRUGO, + set_delta1_mode_show, NULL); +static DEVICE_ATTR(set_refer2, S_IRUGO, + set_refer2_mode_show, NULL); +static DEVICE_ATTR(set_delta2, S_IRUGO, + set_delta2_mode_show, NULL); +static DEVICE_ATTR(set_refer3, S_IRUGO, + set_refer3_mode_show, NULL); +static DEVICE_ATTR(set_delta3, S_IRUGO, + set_delta3_mode_show, NULL); +static DEVICE_ATTR(set_refer4, S_IRUGO, + set_refer4_mode_show, NULL); +static DEVICE_ATTR(set_delta4, S_IRUGO, + set_delta4_mode_show, NULL); +static DEVICE_ATTR(set_all_refer, S_IRUGO, + set_all_refer_mode_show, NULL); +static DEVICE_ATTR(disp_all_refdata, S_IRUGO | S_IWUSR | S_IWGRP, + disp_all_refdata_show, disp_all_refdata_store); +static DEVICE_ATTR(set_all_delta, S_IRUGO, + set_all_delta_mode_show, NULL); +static DEVICE_ATTR(disp_all_deltadata, S_IRUGO | S_IWUSR | S_IWGRP, + disp_all_deltadata_show, disp_all_deltadata_store); +static DEVICE_ATTR(set_firm_version, S_IRUGO | S_IWUSR | S_IWGRP, + set_firm_version_show, NULL); +static DEVICE_ATTR(set_module_off, S_IRUGO | S_IWUSR | S_IWGRP, + NULL, set_module_off_store); +static DEVICE_ATTR(set_module_on, S_IRUGO | S_IWUSR | S_IWGRP, + NULL, set_module_on_store); +static DEVICE_ATTR(mxt_touchtype, S_IRUGO | S_IWUSR | S_IWGRP, + mxt_touchtype_show, NULL); +static DEVICE_ATTR(set_threshold, S_IRUGO, + set_threshold_mode_show, NULL); +/* firmware update */ +static DEVICE_ATTR(tsp_firm_update, S_IWUSR | S_IWGRP, + NULL, set_mxt_firm_update_store); +/* firmware update status return */ +static DEVICE_ATTR(tsp_firm_update_status, S_IRUGO, + set_mxt_firm_status_show, NULL); +/* touch threshold return, store */ +static DEVICE_ATTR(tsp_threshold, S_IRUGO | S_IWUSR | S_IWGRP, + key_threshold_show, key_threshold_store); +/* PHONE*/ /* firmware version resturn in phone driver version */ +static DEVICE_ATTR(tsp_firm_version_phone, S_IRUGO, + set_mxt_firm_version_show, NULL); +/*PART*/ /* firmware version resturn in TSP panel version */ +static DEVICE_ATTR(tsp_firm_version_panel, S_IRUGO, + set_mxt_firm_version_read_show, NULL); +static DEVICE_ATTR(tsp_firm_version_config, S_IRUGO, + set_mxt_config_version_show, NULL); +static DEVICE_ATTR(object_show, S_IWUSR | S_IWGRP, NULL, + mxt_object_show); +static DEVICE_ATTR(object_write, S_IWUSR | S_IWGRP, NULL, + mxt_object_setting); +static DEVICE_ATTR(dbg_switch, S_IWUSR | S_IWGRP, NULL, + mxt_debug_setting); +static DEVICE_ATTR(find_delta_channel, S_IRUGO, + find_channel_show, NULL); +static DEVICE_ATTR(x_line, S_IRUGO, + x_line_show, NULL); +static DEVICE_ATTR(y_line, S_IRUGO, + y_line_show, NULL); +static DEVICE_ATTR(set_tsp_name, S_IRUGO, + mxt_touchtype_show, NULL); + +#if ITDEV +/* Sysfs files for libmaxtouch interface */ +static DEVICE_ATTR(pause_driver, 0666, + pause_show, pause_store); +static DEVICE_ATTR(debug_enable, 0666, + debug_enable_show, debug_enable_store); +static DEVICE_ATTR(command_calibrate, 0666, + NULL, command_calibrate_store); +static DEVICE_ATTR(command_reset, 0666, + NULL, command_reset_store); +static DEVICE_ATTR(command_backup, 0666, + NULL, command_backup_store); + +static struct attribute *libmaxtouch_attributes[] = { + &dev_attr_pause_driver.attr, + &dev_attr_debug_enable.attr, + &dev_attr_command_calibrate.attr, + &dev_attr_command_reset.attr, + &dev_attr_command_backup.attr, + NULL, +}; + +static struct attribute_group libmaxtouch_attr_group = { + .attrs = libmaxtouch_attributes, +}; +#endif + +static struct attribute *mxt_attrs[] = { + &dev_attr_object_show.attr, + &dev_attr_object_write.attr, + &dev_attr_dbg_switch.attr, + NULL +}; + +static const struct attribute_group mxt_attr_group = { + .attrs = mxt_attrs, +}; + +#endif +static int __devinit mxt_init_config(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int ret; + int i; + bool ta_status = 0; + u16 size; + u16 obj_address = 0; + + u8 **tsp_config; +/* + if (client->addr == MXT_APP_LOW) + client->addr = MXT_BOOT_LOW; + else + client->addr = MXT_BOOT_HIGH; + + ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD); + if (ret >= 0) { + pr_info("boot mode. firm update excute\n"); + mxt_load_fw_bootmode(NULL, MXT_FW_NAME); + msleep(MXT_SW_RESET_TIME); + } else { + if (client->addr == MXT_BOOT_LOW) + client->addr = MXT_APP_LOW; + else + client->addr = MXT_APP_HIGH; + } +*/ + ret = mxt_init_touch_driver(data); + + if (ret) { + pr_err("chip initialization failed\n"); + goto err_init_drv; + } + + /* tsp_family_id - 0x82 : MXT224S series */ + if (data->family_id == 0x82) { + tsp_config = (u8 **)data->pdata->config; + +#if DUAL_TSP + for (i = 0; tsp_config[i][0] != RESERVED_T255; i++) { + if (tsp_config[i][0] == GEN_POWERCONFIG_T7) + data->power_cfg = tsp_config[i] + 1; +} +#endif +#if !(FOR_BRINGUP) + data->t48_config_batt = pdata->t48_config_batt; + data->t48_config_chrg = pdata->t48_config_chrg; + data->tchthr_batt = pdata->tchthr_batt; + data->tchthr_charging = pdata->tchthr_charging; + data->calcfg_batt = pdata->calcfg_batt; + data->calcfg_charging = pdata->calcfg_charging; +#endif +#if UPDATE_ON_PROBE +#if !(FOR_DEBUGGING_TEST_DOWNLOADFW_BIN) + if (data->tsp_version < firmware_latest[0] + || (data->tsp_version == firmware_latest[0] + && data->tsp_build != firmware_latest[1])) { + pr_info("force firmware update\n"); + if (mxt_load_fw(NULL, MXT_FW_NAME)) + goto err_config; + else { + msleep(MXT_SW_RESET_TIME); + mxt_init_touch_driver(data); + } + } +#endif +#endif + } else { + pr_err("ERROR : There is no valid TSP ID\n"); + goto err_config; + } + + // Read USER DATA[0] for Enable&Disable to write configuration + get_object_info(data, SPT_USERDATA_T38, &size, &obj_address); + read_mem(data, obj_address + 0, 1, &data->disable_config_write); + + + for (i = 0; tsp_config[i][0] != RESERVED_T255; i++) { +#if FOR_DEBUGGING_TEST_DOWNLOADFW_BIN + if (data->disable_config_write == 0) + ret = init_write_config(data, tsp_config[i][0], + tsp_config[i] + 1); + else + ret = 0; +#else + ret = init_write_config(data, tsp_config[i][0], + tsp_config[i] + 1); +#endif + /*12/03/29 Temporary set as comment*/ + /*if (ret) + goto err_config;*/ + + 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; + } + } + } + ret = mxt_backup(data); + if (ret) + goto err_backup; + + /* reset the touch IC. */ + ret = mxt_reset(data); + if (ret) + goto err_reset; + + msleep(MXT_SW_RESET_TIME); + + if (data->read_ta_status) { + data->read_ta_status(&ta_status); + pr_info("ta_status is %d\n", ta_status); + mxt_ta_probe(ta_status); + } +#ifdef CONFIG_READ_FROM_FILE + else{ + mxt_download_config(data, MXT_BATT_CFG_NAME); + } +#endif + calibrate_chip_e(); + + return 0; + + err_reset: + pr_info("mxt ierr_reset \n"); + err_backup: + pr_info("mxt err_reset \n"); + err_config: + kfree(data->objects); + pr_info("mxt err_config \n"); + err_init_drv: + gpio_free(data->gpio_read_done); + pr_info("mxt err_init_drv \n"); + + return ret; + +} + +static int __devinit mxt_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mxt224s_platform_data *pdata = client->dev.platform_data; + struct mxt_data *data; + struct input_dev *input_dev; + int ret; + int i=0; + + + pr_info("%s +++\n", __func__); + + touch_is_pressed = 0; +#if TOUCH_BOOSTER + tsp_press_status = 0; +#endif +#if DUAL_TSP + Flip_status_tsp = FLIP_NOTINIT; + Tsp_current_addr = MXT224S_ADDR_MAIN; +#endif + + if (!pdata) { + pr_err("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); + if (!data) + return -ENOMEM; + + data->pdata = pdata; + data->num_fingers = pdata->max_finger_touches; + data->power_on = pdata->power_on; + data->power_off = pdata->power_off; +#if 1 //!(FOR_BRINGUP) + data->register_cb = pdata->register_cb; +#endif + data->read_ta_status = pdata->read_ta_status; + + data->client = client; + i2c_set_clientdata(client, data); + + input_dev = input_allocate_device(); + if (!input_dev) { + ret = -ENOMEM; + pr_err("input device allocation failed\n"); + goto err_alloc_dev; + } + data->input_dev = input_dev; + input_set_drvdata(input_dev, data); + input_dev->name = "sec_touchscreen"; + + set_bit(EV_SYN, input_dev->evbit); + 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, MAX_USING_FINGER_NUM); + + 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_z, + pdata->max_z, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, pdata->min_w, + pdata->max_w, 0, 0); + +#ifdef _SUPPORT_SHAPE_TOUCH_ + input_set_abs_params(input_dev, ABS_MT_COMPONENT, 0, 255, 0, 0); +#endif + ret = input_register_device(input_dev); + if (ret) { + input_free_device(input_dev); + goto err_reg_dev; + } + + data->gpio_read_done = pdata->gpio_read_done; + + data->power_on(); + + copy_data = data; +#if 1//!(FOR_BRINGUP) + data->register_cb(mxt_ta_probe); +#endif + +#if DUAL_TSP + Tsp_main_initialized = 0; + Tsp_sub_initialized = 0; + mxt_enabled = 0; + Tsp_probe_passed = 0; +/******************************************************/ +/* Main TSP init */ +/******************************************************/ + Tsp_current_addr = MXT224S_ADDR_MAIN; + gpio_set_value(GPIO_TSP_SEL, TSP_SEL_toMAIN); + copy_data->client->addr = Tsp_current_addr; + printk("[TSP]mxt_probe() : Main TSP init ############# \n"); + ret = mxt_init_config(data); + if (ret) { + pr_err("[TSP] chip config initialization failed\n"); + return ret; + } + Tsp_main_initialized = 1; +/******************************************************/ +/* Sub TSP init */ +/******************************************************/ + Tsp_current_addr = MXT224S_ADDR_SUB; + gpio_set_value(GPIO_TSP_SEL, TSP_SEL_toSUB); + copy_data->client->addr = Tsp_current_addr; + printk("[TSP]mxt_probe() : Sub TSP init ############# \n"); + ret = mxt_init_config(data); + if (ret) { + pr_err("[TSP] chip config initialization failed\n"); + return ret; + } + Tsp_sub_initialized = 1; +/******************************************************/ +/* One TSP has to enter suspend mode */ +/******************************************************/ + /* In flip module, 1st flip-value-scan will be executed precisely.*/ + /* Then, samsung_switching_tsp() will be called... */ + +/******************************************************/ + +#else + ret = mxt_init_config(data); + if (ret) { + pr_err("[TSP] chip config initialization failed\n"); + return ret; + } +#endif + + for (i = 0; i < data->num_fingers; i++) + data->fingers[i].state = MXT_STATE_INACTIVE; + + ret = request_threaded_irq(client->irq, NULL, mxt_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, "mxt_ts", data); + + if (ret < 0) + goto err_irq; + +#if SYSFS + ret = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); + if (ret) + pr_err("sysfs_create_group()is falled\n"); + + +#if ITDEV + ret = sysfs_create_group(&client->dev.kobj, &libmaxtouch_attr_group); + if (ret) { + pr_err("Failed to create libmaxtouch sysfs group\n"); + goto err_irq; + } + + sysfs_bin_attr_init(&mem_access_attr); + mem_access_attr.attr.name = "mem_access"; + mem_access_attr.attr.mode = S_IRUGO | S_IWUGO; + mem_access_attr.read = mem_access_read; + mem_access_attr.write = mem_access_write; + mem_access_attr.size = 65535; + + if (sysfs_create_bin_file(&client->dev.kobj, &mem_access_attr) < 0) { + pr_err("Failed to create device file(%s)!\n", + mem_access_attr.attr.name); + goto err_irq; + } +#endif + pr_info("mxt file sys call!!!\n"); + + sec_touchscreen = device_create(sec_class, + NULL, 0, NULL, "sec_touchscreen"); + dev_set_drvdata(sec_touchscreen, data); + if (IS_ERR(sec_touchscreen)) + pr_err("Failed to create device(sec_touchscreen)!\n"); + + if (device_create_file(sec_touchscreen, + &dev_attr_tsp_firm_update) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_tsp_firm_update.attr.name); + + if (device_create_file(sec_touchscreen, + &dev_attr_tsp_firm_update_status) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_tsp_firm_update_status.attr.name); + + if (device_create_file(sec_touchscreen, + &dev_attr_tsp_threshold) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_tsp_threshold.attr.name); + + if (device_create_file(sec_touchscreen, + &dev_attr_tsp_firm_version_phone) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_tsp_firm_version_phone.attr.name); + + if (device_create_file(sec_touchscreen, + &dev_attr_tsp_firm_version_panel) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_tsp_firm_version_panel.attr.name); + + if (device_create_file(sec_touchscreen, + &dev_attr_tsp_firm_version_config) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_tsp_firm_version_config.attr.name); + + if (device_create_file(sec_touchscreen, + &dev_attr_mxt_touchtype) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_mxt_touchtype.attr.name); + + mxt_noise_test = device_create(sec_class, + NULL, 0, NULL, "tsp_noise_test"); + + if (IS_ERR(mxt_noise_test)) + pr_err("Failed to create device(mxt_noise_test)!\n"); + + if (device_create_file(mxt_noise_test, &dev_attr_set_refer0) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_refer0.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_delta0) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_delta0.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_refer1) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_refer1.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_delta1) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_delta1.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_refer2) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_refer2.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_delta2) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_delta2.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_refer3) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_refer3.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_delta3) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_delta3.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_refer4) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_refer4.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_delta4) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_delta4.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_all_refer) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_all_refer.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_disp_all_refdata) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_disp_all_refdata.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_all_delta) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_all_delta.attr.name); + + if (device_create_file(mxt_noise_test, + &dev_attr_disp_all_deltadata) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_disp_all_deltadata.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_threshold) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_threshold.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_firm_version) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_firm_version.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_module_off) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_module_off.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_set_module_on) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_module_on.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_x_line) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_x_line.attr.name); + + if (device_create_file(mxt_noise_test, &dev_attr_y_line) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_y_line.attr.name); + + if (device_create_file(mxt_noise_test, + &dev_attr_find_delta_channel) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_find_delta_channel.attr.name); + + if (device_create_file(mxt_noise_test, + &dev_attr_set_tsp_name) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_set_tsp_name.attr.name); +#endif +#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 + mxt_enabled = 1; + Tsp_probe_passed = 1; +#if TOUCH_BOOSTER + INIT_DELAYED_WORK(&data->dvfs_dwork, + mxt_set_dvfs_off); +#endif + + + return 0; + +err_irq: + pr_info("mxt err_irq \n"); +err_init_drv: + gpio_free(data->gpio_read_done); + pr_info("mxt err_init_drv \n"); +/* err_gpio_req: + data->power_off(); + input_unregister_device(input_dev); */ +err_reg_dev: + pr_info("mxt err_reg_dev \n"); + +err_alloc_dev: + pr_info("mxt err_alloc_dev \n"); + kfree(data); + return ret; +} + +#ifdef CONFIG_READ_FROM_FILE +#if DOWNLOAD_CONFIG +static struct object_t *mxt_get_object(struct mxt_data *data, u8 type) +{ + struct object_t *object; + int i; + + for (i = 0; i < data->objects_len; i++) { + object = data->objects + i; + if (object->object_type == type) + return object; + } + + dev_err(&data->client->dev, "Invalid object type T%u\n", type); + return NULL; +} + +static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val) +{ + u8 buf[3]; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + buf[2] = val; + + printk("[TSP] mxt_write_reg %d %d\n", reg, val); + + if (i2c_master_send(client, buf, 3) != 3) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +int mxt_download_config(struct mxt_data *data, const char *fn) +{ + struct device *dev = &data->client->dev; + struct mxt_info cfg_info; + struct object_t *object; +#ifdef CONFIG_READ_FROM_SDCARD + struct firmware *cfg = NULL; +#else + const struct firmware *cfg = NULL; +#endif + int ret; + int offset; + loff_t pos; + int i; + unsigned long info_crc, config_crc; + unsigned int type, instance, size, object_size, instance_size; + u8 val; + u16 reg; + +#ifdef CONFIG_READ_FROM_SDCARD + struct file *filp; + long cfg_size = 0; + unsigned char *cfg_data; + mm_segment_t oldfs; + + oldfs = get_fs(); + set_fs(get_ds()); + + printk("[TSP] mxt_download_config %s\n", fn); + + filp = filp_open(fn, O_RDONLY, 0); + if (IS_ERR(filp)) { + pr_err("file open error:%d\n", (s32)filp); + return -1; + } + + cfg_size = filp->f_path.dentry->d_inode->i_size; + pr_info("Size of the Cfg file : %ld(bytes)\n", cfg_size); + + cfg_data = kmalloc(cfg_size, GFP_KERNEL); + memset(cfg_data, 0, cfg_size); + + pos = 0; + ret = vfs_read(filp, (char __user *)cfg_data, cfg_size, &pos); + + if (ret != cfg_size) { + pr_err("Failed to read Cfg file %s (ret = %d)\n", + fn, ret); + kfree(cfg_data); + filp_close(filp, current->files); + return -1; + } + + filp_close(filp, current->files); + + set_fs(oldfs); + + //firmware struct + cfg = kzalloc(sizeof(struct firmware), GFP_KERNEL); + cfg->data = cfg_data; + cfg->size = cfg_size; +#else + ret = request_firmware(&cfg, fn, dev); + if (ret < 0) { + dev_err(dev, "Failure to request config file %s\n", fn); + return 0; + } +#endif + + if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { + dev_err(dev, "Unrecognised config file\n"); + ret = -EINVAL; + goto release; + } + + pos = strlen(MXT_CFG_MAGIC); + + /* Load information block and check */ + for (i = 0; i < sizeof(struct mxt_info); i++) { + ret = sscanf(cfg->data + pos, "%hhx%n", + (unsigned char *)&cfg_info + i, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + } + + pos += offset; + } + + if (cfg_info.family_id != data->family_id) { + dev_err(dev, "Family ID mismatch! %x %x\n", cfg_info.family_id, data->family_id); + ret = -EINVAL; + } + + if (cfg_info.variant_id != data->tsp_variant) { + dev_err(dev, "Variant ID mismatch! %x %x\n", cfg_info.variant_id, data->tsp_variant); + ret = -EINVAL; + } + + if (cfg_info.version != data->tsp_version) + dev_err(dev, "Warning: version mismatch! %x %x\n", cfg_info.version, data->tsp_version); + + if (cfg_info.build != data->tsp_build) + dev_err(dev, "Warning: build num mismatch! %x %x\n", cfg_info.build, data->tsp_build); + + ret = sscanf(cfg->data + pos, "%lx%n", &info_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + } + pos += offset; + + /* Check config CRC */ + ret = sscanf(cfg->data + pos, "%lx%n", &config_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + } + pos += offset; + + while (pos < cfg->size) { + /* Read type, instance, length */ + ret = sscanf(cfg->data + pos, "%x %x %x%n", + &type, &instance, &size, &offset); + if (ret == 0) { + /* EOF */ + ret = 1; + goto release; + } else if (ret < 0) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release; + } + pos += offset; + + object = mxt_get_object(data, type); + if (!object) { + ret = -EINVAL; + goto release; + } + + object_size = object->size+1; + instance_size = object->instances+1; + + if (size > object_size) { + dev_err(dev, "Object length exceeded!\n"); + ret = -EINVAL; + goto release; + } + + if (instance >= instance_size) { + dev_err(dev, "Object instances exceeded!\n"); + ret = -EINVAL; + goto release; + } + + reg = object->i2c_address + object_size * instance; + + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + pos, "%hhx%n", + &val, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release; + } + + ret = mxt_write_reg(data->client, reg + i, val); + if (ret) + goto release; + + pos += offset; + } + + /* 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 (size < object_size) { + dev_info(dev, "Warning: zeroing %d byte(s) in T%d\n", + object->size - size, type); + + for (i = size + 1; i < object_size; i++) { + ret = mxt_write_reg(data->client, reg + i, 0); + if (ret) + goto release; + } + } + } + +release: + +#ifdef CONFIG_READ_FROM_SDCARD + kfree(cfg); + kfree(cfg_data); +#else + release_firmware(cfg); +#endif + return ret; +} + +#endif +#endif + +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); + gpio_free(data->gpio_read_done); + data->power_off(); + input_unregister_device(data->input_dev); + 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, + .pm = &mxt_pm_ops, + }, +}; + +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("ki_won.kim<ki_won.kim@samsung.com>"); +MODULE_LICENSE("GPL"); |