diff options
| author | Meizu OpenSource <patchwork@meizu.com> | 2016-08-15 10:19:42 +0800 |
|---|---|---|
| committer | Meizu OpenSource <patchwork@meizu.com> | 2016-08-15 10:19:42 +0800 |
| commit | d2e1446d81725c351dc73a03b397ce043fb18452 (patch) | |
| tree | 4dbc616b7f92aea39cd697a9084205ddb805e344 /drivers/input/touchscreen/mediatek/GT9XX | |
| download | android_kernel_m2note-d2e1446d81725c351dc73a03b397ce043fb18452.tar.gz | |
first commit
Diffstat (limited to 'drivers/input/touchscreen/mediatek/GT9XX')
| -rwxr-xr-x | drivers/input/touchscreen/mediatek/GT9XX/Makefile | 11 | ||||
| -rw-r--r-- | drivers/input/touchscreen/mediatek/GT9XX/goodix_tool.c | 575 | ||||
| -rw-r--r-- | drivers/input/touchscreen/mediatek/GT9XX/gt9xx_driver.c | 1744 | ||||
| -rw-r--r-- | drivers/input/touchscreen/mediatek/GT9XX/gt9xx_update.c | 1855 |
4 files changed, 4185 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/mediatek/GT9XX/Makefile b/drivers/input/touchscreen/mediatek/GT9XX/Makefile new file mode 100755 index 000000000..135f361be --- /dev/null +++ b/drivers/input/touchscreen/mediatek/GT9XX/Makefile @@ -0,0 +1,11 @@ +include $(srctree)/drivers/misc/mediatek/Makefile.custom + +# Linux driver folder +ccflags-y += -I$(srctree)/arch/arm/mach-$(MTK_PLATFORM)/$(ARCH_MTK_PROJECT)/touchpanel/GT9XX/ +ccflags-y += -I$(srctree)/drivers/input/touchscreen/mediatek/GT9XX/ +ccflags-y += -I$(srctree)/drivers/input/touchscreen/mediatek/ + +obj-y += goodix_tool.o +obj-y += gt9xx_driver.o +obj-y += gt9xx_update.o + diff --git a/drivers/input/touchscreen/mediatek/GT9XX/goodix_tool.c b/drivers/input/touchscreen/mediatek/GT9XX/goodix_tool.c new file mode 100644 index 000000000..db9406bd1 --- /dev/null +++ b/drivers/input/touchscreen/mediatek/GT9XX/goodix_tool.c @@ -0,0 +1,575 @@ +/* drivers/input/touchscreen/goodix_tool.c + * + * 2010 - 2012 Goodix Technology. + * + * 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 a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * 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. + * + * Version:1.2 + * V1.0:2012/05/01,create file. + * V1.2:2012/10/17,reset_guitar etc. + * + */ + +#include "tpd.h" +#include <linux/interrupt.h> +#include <cust_eint.h> +#include <linux/i2c.h> +#include <linux/sched.h> +#include <linux/kthread.h> +#include <linux/rtpm_prio.h> +#include <linux/wait.h> +#include <linux/time.h> +#include <linux/delay.h> +#include "cust_gpio_usage.h" +#include <asm/uaccess.h> + +#include "tpd_custom_gt9xx.h" + +#include <linux/device.h> +#include <linux/proc_fs.h> /*proc */ + +#pragma pack(1) +typedef struct { + u8 wr; /* write read flag٬0:R 1:W 2:PID 3: */ + u8 flag; /* 0:no need flag/int 1: need flag 2:need int */ + u8 flag_addr[2]; /* flag address */ + u8 flag_val; /* flag val */ + u8 flag_relation; /* flag_val:flag 0:not equal 1:equal 2:> 3:< */ + u16 circle; /* polling cycle */ + u8 times; /* plling times */ + u8 retry; /* I2C retry times */ + u16 delay; /* delay befor read or after write */ + u16 data_len; /* data length */ + u8 addr_len; /* address length */ + u8 addr[2]; /* address */ + u8 res[3]; /* reserved */ + u8 *data; /* data pointer */ +} st_cmd_head; +#pragma pack() +st_cmd_head cmd_head; + +#define UPDATE_FUNCTIONS +#define DATA_LENGTH_UINT 512 +#define CMD_HEAD_LENGTH (sizeof(st_cmd_head) - sizeof(u8 *)) +#define GOODIX_ENTRY_NAME "goodix_tool" +static char procname[20] = { 0 }; + +extern struct i2c_client *i2c_client_point; +static struct i2c_client *gt_client; + +#ifdef UPDATE_FUNCTIONS +extern s32 gup_enter_update_mode(struct i2c_client *client); +extern void gup_leave_update_mode(void); +extern s32 gup_update_proc(void *dir); +#endif + +static struct proc_dir_entry *goodix_proc_entry; + +static s32 goodix_tool_write(struct file *filp, const char __user *buff, unsigned long len, + void *data); +static s32 goodix_tool_read(char *page, char **start, off_t off, int count, int *eof, void *data); +static s32(*tool_i2c_read) (u8 *, u16); +static s32(*tool_i2c_write) (u8 *, u16); + +#if GTP_ESD_PROTECT +extern void gtp_esd_switch(struct i2c_client *client, s32 on); +#endif +s32 DATA_LENGTH = 0; +s8 IC_TYPE[16] = "GT9XX"; + +static void tool_set_proc_name(char *procname) +{ + char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", + "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + char date[20] = { 0 }; + char month[4] = { 0 }; + int i = 0, n_month = 1, n_day = 0, n_year = 0; + + sprintf(date, "%s", __DATE__); + + /* GTP_DEBUG("compile date: %s", date); */ + + sscanf(date, "%s %d %d", month, &n_day, &n_year); + + for (i = 0; i < 12; ++i) { + if (!memcmp(months[i], month, 3)) { + n_month = i + 1; + break; + } + } + + sprintf(procname, "gmnode%04d%02d%02d", n_year, n_month, n_day); + + /* GTP_DEBUG("procname = %s", procname); */ +} + +static ssize_t goodix_tool_upper_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + return goodix_tool_read(buffer, NULL, 0, count, NULL, ppos); +} + +static ssize_t goodix_tool_upper_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + return goodix_tool_write(file, buffer, count, ppos); +} + +static const struct file_operations gt_tool_fops = { + .write = goodix_tool_upper_write, + .read = goodix_tool_upper_read +}; + +static s32 tool_i2c_read_no_extra(u8 *buf, u16 len) +{ + s32 ret = -1; + + ret = gtp_i2c_read(gt_client, buf, len + GTP_ADDR_LENGTH); + return ret; +} + +static s32 tool_i2c_write_no_extra(u8 *buf, u16 len) +{ + s32 ret = -1; + + ret = gtp_i2c_write(gt_client, buf, len); + return ret; +} + +static s32 tool_i2c_read_with_extra(u8 *buf, u16 len) +{ + s32 ret = -1; + u8 pre[2] = { 0x0f, 0xff }; + u8 end[2] = { 0x80, 0x00 }; + + tool_i2c_write_no_extra(pre, 2); + ret = tool_i2c_read_no_extra(buf, len); + tool_i2c_write_no_extra(end, 2); + + return ret; +} + +static s32 tool_i2c_write_with_extra(u8 *buf, u16 len) +{ + s32 ret = -1; + u8 pre[2] = { 0x0f, 0xff }; + u8 end[2] = { 0x80, 0x00 }; + + tool_i2c_write_no_extra(pre, 2); + ret = tool_i2c_write_no_extra(buf, len); + tool_i2c_write_no_extra(end, 2); + + return ret; +} + +static void register_i2c_func(void) +{ +/* if (!strncmp(IC_TYPE, "GT818", 5) || !strncmp(IC_TYPE, "GT816", 5) */ +/* || !strncmp(IC_TYPE, "GT811", 5) || !strncmp(IC_TYPE, "GT818F", 6) */ +/* || !strncmp(IC_TYPE, "GT827", 5) || !strncmp(IC_TYPE,"GT828", 5) */ +/* || !strncmp(IC_TYPE, "GT813", 5)) */ + if (strncmp(IC_TYPE, "GT8110", 6) && strncmp(IC_TYPE, "GT8105", 6) + && strncmp(IC_TYPE, "GT801", 5) && strncmp(IC_TYPE, "GT800", 5) + && strncmp(IC_TYPE, "GT801PLUS", 9) && strncmp(IC_TYPE, "GT811", 5) + && strncmp(IC_TYPE, "GTxxx", 5) && strncmp(IC_TYPE, "GT9XX", 5)) { + tool_i2c_read = tool_i2c_read_with_extra; + tool_i2c_write = tool_i2c_write_with_extra; + GTP_DEBUG("I2C function: with pre and end cmd!"); + } else { + tool_i2c_read = tool_i2c_read_no_extra; + tool_i2c_write = tool_i2c_write_no_extra; + GTP_INFO("I2C function: without pre and end cmd!"); + } +} + +static void unregister_i2c_func(void) +{ + tool_i2c_read = NULL; + tool_i2c_write = NULL; + GTP_INFO("I2C function: unregister i2c transfer function!"); +} + + +s32 init_wr_node(struct i2c_client *client) +{ + s32 i; + + gt_client = i2c_client_point; + + memset(&cmd_head, 0, sizeof(cmd_head)); + cmd_head.data = NULL; + + i = 5; + + while ((!cmd_head.data) && i) { + cmd_head.data = kzalloc(i * DATA_LENGTH_UINT, GFP_KERNEL); + + if (NULL != cmd_head.data) { + break; + } + + i--; + } + + if (i) { + DATA_LENGTH = i * DATA_LENGTH_UINT + GTP_ADDR_LENGTH; + GTP_INFO("Applied memory size:%d.", DATA_LENGTH); + } else { + GTP_ERROR("Apply for memory failed."); + return FAIL; + } + + cmd_head.addr_len = 2; + cmd_head.retry = 5; + + register_i2c_func(); + + tool_set_proc_name(procname); +#if 0 /* fix 3.10 */ + goodix_proc_entry = create_proc_entry(gtp_tool_entry, 0664, NULL); + + if (goodix_proc_entry == NULL) { + GTP_ERROR("Couldn't create proc entry!"); + return FAIL; + } else { + GTP_INFO("Create proc entry success!"); + goodix_proc_entry->write_proc = goodix_tool_write; + goodix_proc_entry->read_proc = goodix_tool_read; + } +#else + if (proc_create(procname, 0660, NULL, >_tool_fops) == NULL) { + GTP_ERROR("create_proc_entry %s failed", procname); + return -1; + } +#endif + return SUCCESS; +} + +void uninit_wr_node(void) +{ + kfree(cmd_head.data); + cmd_head.data = NULL; + unregister_i2c_func(); + remove_proc_entry(procname, NULL); +} + +static u8 relation(u8 src, u8 dst, u8 rlt) +{ + u8 ret = 0; + + switch (rlt) { + case 0: + ret = (src != dst) ? true : false; + break; + + case 1: + ret = (src == dst) ? true : false; + GTP_DEBUG("equal:src:0x%02x dst:0x%02x ret:%d.", src, dst, (s32) ret); + break; + + case 2: + ret = (src > dst) ? true : false; + break; + + case 3: + ret = (src < dst) ? true : false; + break; + + case 4: + ret = (src & dst) ? true : false; + break; + + case 5: + ret = (!(src | dst)) ? true : false; + break; + + default: + ret = false; + break; + } + + return ret; +} + +/******************************************************* +Function: + Comfirm function. +Input: + None. +Output: + Return write length. +********************************************************/ +static u8 comfirm(void) +{ + s32 i = 0; + u8 buf[32]; + +/* memcpy(&buf[GTP_ADDR_LENGTH - cmd_head.addr_len], &cmd_head.flag_addr, cmd_head.addr_len); */ +/* memcpy(buf, &cmd_head.flag_addr, cmd_head.addr_len);//Modified by Scott, 2012-02-17 */ + memcpy(buf, cmd_head.flag_addr, cmd_head.addr_len); + + for (i = 0; i < cmd_head.times; i++) { + if (tool_i2c_read(buf, 1) <= 0) { + GTP_ERROR("Read flag data failed!"); + return FAIL; + } + + if (true == + relation(buf[GTP_ADDR_LENGTH], cmd_head.flag_val, cmd_head.flag_relation)) { + GTP_DEBUG("value at flag addr:0x%02x.", buf[GTP_ADDR_LENGTH]); + GTP_DEBUG("flag value:0x%02x.", cmd_head.flag_val); + break; + } + + msleep(cmd_head.circle); + } + + if (i >= cmd_head.times) { + GTP_ERROR("Didn't get the flag to continue!"); + return FAIL; + } + + return SUCCESS; +} + +/******************************************************* +Function: + Goodix tool write function. +Input: + standard proc write function param. +Output: + Return write length. +********************************************************/ +static s32 goodix_tool_write(struct file *filp, const char __user *buff, unsigned long len, + void *data) +{ + u64 ret = 0; + GTP_DEBUG_FUNC(); + GTP_DEBUG_ARRAY((u8 *) buff, len); + + ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH); + + if (ret) { + GTP_ERROR("copy_from_user failed."); + } + + GTP_DEBUG("wr :0x%02x.", cmd_head.wr); + GTP_DEBUG("flag:0x%02x.", cmd_head.flag); + GTP_DEBUG("flag addr:0x%02x%02x.", cmd_head.flag_addr[0], cmd_head.flag_addr[1]); + GTP_DEBUG("flag val:0x%02x.", cmd_head.flag_val); + GTP_DEBUG("flag rel:0x%02x.", cmd_head.flag_relation); + GTP_DEBUG("circle :%d.", (s32) cmd_head.circle); + GTP_DEBUG("times :%d.", (s32) cmd_head.times); + GTP_DEBUG("retry :%d.", (s32) cmd_head.retry); + GTP_DEBUG("delay :%d.", (s32) cmd_head.delay); + GTP_DEBUG("data len:%d.", (s32) cmd_head.data_len); + GTP_DEBUG("addr len:%d.", (s32) cmd_head.addr_len); + GTP_DEBUG("addr:0x%02x%02x.", cmd_head.addr[0], cmd_head.addr[1]); + GTP_DEBUG("len:%d.", (s32) len); + GTP_DEBUG("buf[20]:0x%02x.", buff[CMD_HEAD_LENGTH]); + + if (1 == cmd_head.wr) { + /* copy_from_user(&cmd_head.data[cmd_head.addr_len], &buff[CMD_HEAD_LENGTH], cmd_head.data_len); */ + ret = + copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH], &buff[CMD_HEAD_LENGTH], + cmd_head.data_len); + + if (ret) { + GTP_ERROR("copy_from_user failed."); + } + + memcpy(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len], cmd_head.addr, + cmd_head.addr_len); + + GTP_DEBUG_ARRAY(cmd_head.data, cmd_head.data_len + cmd_head.addr_len); + GTP_DEBUG_ARRAY((u8 *) &buff[CMD_HEAD_LENGTH], cmd_head.data_len); + + if (1 == cmd_head.flag) { + if (FAIL == comfirm()) { + GTP_ERROR("[WRITE]Comfirm fail!"); + return FAIL; + } + } else if (2 == cmd_head.flag) { + /* Need interrupt! */ + } + + if (tool_i2c_write(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len], + cmd_head.data_len + cmd_head.addr_len) <= 0) { + GTP_ERROR("[WRITE]Write data failed!"); + return FAIL; + } + + GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len], + cmd_head.data_len + cmd_head.addr_len); + + if (cmd_head.delay) { + msleep(cmd_head.delay); + } + + return cmd_head.data_len + CMD_HEAD_LENGTH; + } else if (3 == cmd_head.wr) /* Write ic type */ + { + memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len); + register_i2c_func(); + + return cmd_head.data_len + CMD_HEAD_LENGTH; + } else if (5 == cmd_head.wr) { + /* memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len); */ + + return cmd_head.data_len + CMD_HEAD_LENGTH; + } else if (7 == cmd_head.wr) /* disable irq! */ + { + mt_eint_mask(CUST_EINT_TOUCH_PANEL_NUM); +#if GTP_ESD_PROTECT + gtp_esd_switch(i2c_client_point, SWITCH_OFF); +#endif + return CMD_HEAD_LENGTH; + } else if (9 == cmd_head.wr) /* enable irq! */ + { + mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM); +#if GTP_ESD_PROTECT + gtp_esd_switch(i2c_client_point, SWITCH_ON); +#endif + return CMD_HEAD_LENGTH; + } else if (17 == cmd_head.wr) { + /* struct goodix_ts_data *ts = i2c_get_clientdata(gt_client); */ + ret = + copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH], &buff[CMD_HEAD_LENGTH], + cmd_head.data_len); + + if (ret) { + GTP_DEBUG("copy_from_user failed."); + } + + if (cmd_head.data[GTP_ADDR_LENGTH]) { + GTP_DEBUG("gtp enter rawdiff."); + gtp_rawdiff_mode = true; + } else { + gtp_rawdiff_mode = false; + GTP_DEBUG("gtp leave rawdiff."); + } + + return CMD_HEAD_LENGTH; + } +#ifdef UPDATE_FUNCTIONS + else if (11 == cmd_head.wr) /* Enter update mode! */ + { + if (FAIL == gup_enter_update_mode(gt_client)) { + return FAIL; + } + } else if (13 == cmd_head.wr) /* Leave update mode! */ + { + gup_leave_update_mode(); + } else if (15 == cmd_head.wr) /* Update firmware! */ + { + show_len = 0; + total_len = 0; + memset(cmd_head.data, 0, cmd_head.data_len + 1); + memcpy(cmd_head.data, &buff[CMD_HEAD_LENGTH], cmd_head.data_len); + GTP_DEBUG("update firmware, filename: %s", cmd_head.data); + if (FAIL == gup_update_proc((void *)cmd_head.data)) { + return FAIL; + } + } +#endif + + return CMD_HEAD_LENGTH; +} + +/******************************************************* +Function: + Goodix tool read function. +Input: + standard proc read function param. +Output: + Return read length. +********************************************************/ +static s32 goodix_tool_read(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + GTP_DEBUG_FUNC(); + + if (cmd_head.wr % 2) { + return FAIL; + } else if (!cmd_head.wr) { + u16 len = 0; + s16 data_len = 0; + u16 loc = 0; + + if (1 == cmd_head.flag) { + if (FAIL == comfirm()) { + GTP_ERROR("[READ]Comfirm fail!"); + return FAIL; + } + } else if (2 == cmd_head.flag) { + /* Need interrupt! */ + } + + memcpy(cmd_head.data, cmd_head.addr, cmd_head.addr_len); + + GTP_DEBUG("[CMD HEAD DATA] ADDR:0x%02x%02x.", cmd_head.data[0], cmd_head.data[1]); + GTP_DEBUG("[CMD HEAD ADDR] ADDR:0x%02x%02x.", cmd_head.addr[0], cmd_head.addr[1]); + + if (cmd_head.delay) { + msleep(cmd_head.delay); + } + + data_len = cmd_head.data_len; + + while (data_len > 0) { + if (data_len > DATA_LENGTH) { + len = DATA_LENGTH; + } else { + len = data_len; + } + + data_len -= DATA_LENGTH; + + if (tool_i2c_read(cmd_head.data, len) <= 0) { + GTP_ERROR("[READ]Read data failed!"); + return FAIL; + } + + memcpy(&page[loc], &cmd_head.data[GTP_ADDR_LENGTH], len); + loc += len; + + GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH], len); + GTP_DEBUG_ARRAY(page, len); + } + } else if (2 == cmd_head.wr) { + /* memcpy(page, "gt8", cmd_head.data_len); */ + /* memcpy(page, "GT818", 5); */ + /* page[5] = 0; */ + + GTP_DEBUG("Return ic type:%s len:%d.", page, (s32) cmd_head.data_len); + return cmd_head.data_len; + /* return sizeof(IC_TYPE_NAME); */ + } else if (4 == cmd_head.wr) { + page[0] = show_len >> 8; + page[1] = show_len & 0xff; + page[2] = total_len >> 8; + page[3] = total_len & 0xff; + + return cmd_head.data_len; + } else if (6 == cmd_head.wr) { + /* Read error code! */ + } else if (8 == cmd_head.wr) /* Read driver version */ + { + /* memcpy(page, GTP_DRIVER_VERSION, strlen(GTP_DRIVER_VERSION)); */ + s32 tmp_len; + tmp_len = strlen(GTP_DRIVER_VERSION); + memcpy(page, GTP_DRIVER_VERSION, tmp_len); + page[tmp_len] = 0; + } + + return cmd_head.data_len; +} diff --git a/drivers/input/touchscreen/mediatek/GT9XX/gt9xx_driver.c b/drivers/input/touchscreen/mediatek/GT9XX/gt9xx_driver.c new file mode 100644 index 000000000..9e3958fac --- /dev/null +++ b/drivers/input/touchscreen/mediatek/GT9XX/gt9xx_driver.c @@ -0,0 +1,1744 @@ +#include "tpd.h" +#define GUP_FW_INFO +#include "tpd_custom_gt9xx.h" + +#include "cust_gpio_usage.h" + +#ifdef TPD_PROXIMITY +#include <linux/hwmsensor.h> +#include <linux/hwmsen_dev.h> +#include <linux/sensors_io.h> +#endif + +#include <linux/mmprofile.h> +#include <linux/device.h> +#include <linux/proc_fs.h> /*proc */ +extern struct tpd_device *tpd; +#ifdef VELOCITY_CUSTOM +extern int tpd_v_magnify_x; +extern int tpd_v_magnify_y; +#endif +static int tpd_flag; +static int tpd_halt; +static int tpd_eint_mode = 1; +static int tpd_polling_time = 50; +static DECLARE_WAIT_QUEUE_HEAD(waiter); +static DEFINE_MUTEX(i2c_access); + +#ifdef TPD_HAVE_BUTTON +static int tpd_keys_local[TPD_KEY_COUNT] = TPD_KEYS; +static int tpd_keys_dim_local[TPD_KEY_COUNT][4] = TPD_KEYS_DIM; +#endif + +#if GTP_HAVE_TOUCH_KEY +const u16 touch_key_array[] = TPD_KEYS; +/* #define GTP_MAX_KEY_NUM ( sizeof( touch_key_array )/sizeof( touch_key_array[0] ) ) */ +struct touch_vitual_key_map_t { + int point_x; + int point_y; +}; +static struct touch_vitual_key_map_t touch_key_point_maping_array[] = GTP_KEY_MAP_ARRAY; +#endif + +#if (defined(TPD_WARP_START) && defined(TPD_WARP_END)) +static int tpd_wb_start_local[TPD_WARP_CNT] = TPD_WARP_START; +static int tpd_wb_end_local[TPD_WARP_CNT] = TPD_WARP_END; +#endif + +#if (defined(TPD_HAVE_CALIBRATION) && !defined(TPD_CUSTOM_CALIBRATION)) +/* static int tpd_calmat_local[8] = TPD_CALIBRATION_MATRIX; */ +static int tpd_def_calmat_local[8] = TPD_CALIBRATION_MATRIX; +#endif + +s32 gtp_send_cfg(struct i2c_client *client); +static void tpd_eint_interrupt_handler(void); +static int touch_event_handler(void *unused); +static int tpd_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id); +static int tpd_i2c_detect(struct i2c_client *client, struct i2c_board_info *info); +static int tpd_i2c_remove(struct i2c_client *client); +static void tpd_on(void); +static void tpd_off(void); + +#if GTP_CREATE_WR_NODE +extern s32 init_wr_node(struct i2c_client *); +extern void uninit_wr_node(void); +#endif + +#ifdef GTP_CHARGER_DETECT +extern bool upmu_get_pchr_chrdet(void); +#define TPD_CHARGER_CHECK_CIRCLE 50 +static struct delayed_work gtp_charger_check_work; +static struct workqueue_struct *gtp_charger_check_workqueue; +static void gtp_charger_check_func(struct work_struct *); +static u8 gtp_charger_mode; +#endif + +#if GTP_ESD_PROTECT +#define TPD_ESD_CHECK_CIRCLE 2000 +static struct delayed_work gtp_esd_check_work; +static struct workqueue_struct *gtp_esd_check_workqueue; +static void gtp_esd_check_func(struct work_struct *); +#endif + +#ifdef TPD_PROXIMITY +#define TPD_PROXIMITY_VALID_REG 0x814E +#define TPD_PROXIMITY_ENABLE_REG 0x8042 +static u8 tpd_proximity_flag; +static u8 tpd_proximity_detect = 1; /* 0-->close ; 1--> far away */ +#endif + +#ifndef GTP_REG_REFRESH_RATE +#define GTP_REG_REFRESH_RATE 0x8056 +#endif + +struct i2c_client *i2c_client_point = NULL; +static const struct i2c_device_id tpd_i2c_id[] = { {"gt9xx", 0}, {} }; +static unsigned short force[] = { 0, 0xBA, I2C_CLIENT_END, I2C_CLIENT_END }; +static const unsigned short *const forces[] = { force, NULL }; + +/* static struct i2c_client_address_data addr_data = { .forces = forces,}; */ +static struct i2c_board_info i2c_tpd __initdata = { I2C_BOARD_INFO("gt9xx", (0xBA >> 1)) }; + +static struct i2c_driver tpd_i2c_driver = { + .probe = tpd_i2c_probe, + .remove = tpd_i2c_remove, + .detect = tpd_i2c_detect, + .driver.name = "gt9xx", + .id_table = tpd_i2c_id, + .address_list = (const unsigned short *)forces, +}; + +static u8 config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH] += { GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff }; + +#ifdef GTP_CHARGER_DETECT +static u8 config_charger[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH] += { GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff }; +#endif +#pragma pack(1) +typedef struct { + u16 pid; /* product id // */ + u16 vid; /* version id // */ +} st_tpd_info; +#pragma pack() + +st_tpd_info tpd_info; +u8 int_type = 0; +u32 abs_x_max = 0; +u32 abs_y_max = 0; +u8 gtp_rawdiff_mode = 0; +u8 cfg_len = 0; + +/* proc file system */ +s32 i2c_read_bytes(struct i2c_client *client, u16 addr, u8 *rxbuf, int len); +s32 i2c_write_bytes(struct i2c_client *client, u16 addr, u8 *txbuf, int len); +static struct proc_dir_entry *gt91xx_config_proc; + +/******************************************************* +Function: + Write refresh rate + +Input: + rate: refresh rate N (Duration=5+N ms, N=0~15) + +Output: + Executive outcomes.0---succeed. +*******************************************************/ +static u8 gtp_set_refresh_rate(u8 rate) +{ + u8 buf[3] = { GTP_REG_REFRESH_RATE >> 8, GTP_REG_REFRESH_RATE & 0xff, rate }; + + if (rate > 0xf) { + GTP_ERROR("Refresh rate is over range (%d)", rate); + return FAIL; + } + + GTP_INFO("Refresh rate change to %d", rate); + return gtp_i2c_write(i2c_client_point, buf, sizeof(buf)); +} + +/******************************************************* +Function: + Get refresh rate + +Output: + Refresh rate or error code +*******************************************************/ +static u8 gtp_get_refresh_rate(void) +{ + int ret; + + u8 buf[3] = { GTP_REG_REFRESH_RATE >> 8, GTP_REG_REFRESH_RATE & 0xff }; + ret = gtp_i2c_read(i2c_client_point, buf, sizeof(buf)); + if (ret < 0) + return ret; + + GTP_INFO("Refresh rate is %d", buf[GTP_ADDR_LENGTH]); + return buf[GTP_ADDR_LENGTH]; +} + +/* ============================================================= */ +static ssize_t show_refresh_rate(struct device *dev, struct device_attribute *attr, char *buf) +{ + int ret = gtp_get_refresh_rate(); + if (ret < 0) + return 0; + else + return sprintf(buf, "%d\n", ret); +} + +static ssize_t store_refresh_rate(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + /* u32 rate = 0; */ + gtp_set_refresh_rate(simple_strtoul(buf, NULL, 16)); + return size; +} + +static DEVICE_ATTR(tpd_refresh_rate, 0664, show_refresh_rate, store_refresh_rate); + +static struct device_attribute *gt9xx_attrs[] = { + &dev_attr_tpd_refresh_rate, +}; + +/* ============================================================= */ + +static int tpd_i2c_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + strcpy(info->type, "mtk-tpd"); + return 0; +} + +#ifdef TPD_PROXIMITY +static s32 tpd_get_ps_value(void) +{ + return tpd_proximity_detect; +} + +static s32 tpd_enable_ps(s32 enable) +{ + u8 state; + s32 ret = -1; + + if (enable) { + state = 1; + tpd_proximity_flag = 1; + GTP_INFO("TPD proximity function to be on."); + } else { + state = 0; + tpd_proximity_flag = 0; + GTP_INFO("TPD proximity function to be off."); + } + + ret = i2c_write_bytes(i2c_client_point, TPD_PROXIMITY_ENABLE_REG, &state, 1); + + if (ret < 0) { + GTP_ERROR("TPD %s proximity cmd failed.", state ? "enable" : "disable"); + return ret; + } + + GTP_INFO("TPD proximity function %s success.", state ? "enable" : "disable"); + return 0; +} + +s32 tpd_ps_operate(void *self, u32 command, void *buff_in, s32 size_in, + void *buff_out, s32 size_out, s32 *actualout) +{ + s32 err = 0; + s32 value; + hwm_sensor_data *sensor_data; + + switch (command) { + case SENSOR_DELAY: + if ((buff_in == NULL) || (size_in < sizeof(int))) { + GTP_ERROR("Set delay parameter error!"); + err = -EINVAL; + } + /* Do nothing */ + break; + + case SENSOR_ENABLE: + if ((buff_in == NULL) || (size_in < sizeof(int))) { + GTP_ERROR("Enable sensor parameter error!"); + err = -EINVAL; + } else { + value = *(int *)buff_in; + err = tpd_enable_ps(value); + } + + break; + + case SENSOR_GET_DATA: + if ((buff_out == NULL) || (size_out < sizeof(hwm_sensor_data))) { + GTP_ERROR("Get sensor data parameter error!"); + err = -EINVAL; + } else { + sensor_data = (hwm_sensor_data *) buff_out; + sensor_data->values[0] = tpd_get_ps_value(); + sensor_data->value_divide = 1; + sensor_data->status = SENSOR_STATUS_ACCURACY_MEDIUM; + } + + break; + + default: + GTP_ERROR("proxmy sensor operate function no this parameter %d!", command); + err = -1; + break; + } + + return err; +} +#endif + +static int gt91xx_config_read_proc(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + char *page = NULL; + char *ptr = NULL; + char temp_data[GTP_CONFIG_MAX_LENGTH + 2] = { 0 }; + int i, len, err = -1; + + page = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!page) { + kfree(page); + return -ENOMEM; + } + + ptr = page; + ptr += sprintf(ptr, "==== GT9XX config init value====\n"); + + for (i = 0; i < GTP_CONFIG_MAX_LENGTH; i++) { + ptr += sprintf(ptr, "0x%02X ", config[i + 2]); + + if (i % 8 == 7) + ptr += sprintf(ptr, "\n"); + } + + ptr += sprintf(ptr, "\n"); + + ptr += sprintf(ptr, "==== GT9XX config real value====\n"); + i2c_read_bytes(i2c_client_point, GTP_REG_CONFIG_DATA, temp_data, GTP_CONFIG_MAX_LENGTH); + + for (i = 0; i < GTP_CONFIG_MAX_LENGTH; i++) { + ptr += sprintf(ptr, "0x%02X ", temp_data[i]); + + if (i % 8 == 7) + ptr += sprintf(ptr, "\n"); + } + /* Touch PID & VID */ + ptr += sprintf(ptr, "\n"); + ptr += sprintf(ptr, "==== GT9XX Version ID ====\n"); + i2c_read_bytes(i2c_client_point, GTP_REG_VERSION, temp_data, 6); + ptr += + sprintf(ptr, "Chip PID: %c%c%c VID: 0x%02X%02X\n", temp_data[0], temp_data[1], + temp_data[2], temp_data[5], temp_data[4]); +#if GTP_COMPATIBLE_MODE + ptr += + sprintf(ptr, "Driver VID: 0x%02X%02X\n", gtp_default_FW_fl[12], gtp_default_FW_fl[13]); +#else + ptr += sprintf(ptr, "Driver VID: 0x%02X%02X\n", gtp_default_FW[12], gtp_default_FW[13]); +#endif + i2c_read_bytes(i2c_client_point, 0x41E4, temp_data, 1); + ptr += sprintf(ptr, "Boot status 0x%X\n", temp_data[0]); + + /* Touch Status and Clock Gate */ + ptr += sprintf(ptr, "\n"); + ptr += sprintf(ptr, "==== Touch Status and Clock Gate ====\n"); + ptr += sprintf(ptr, "status: 1: on, 0 :off\n"); + ptr += sprintf(ptr, "status:%d\n", (tpd_halt + 1) & 0x1); + + + len = ptr - page; + if (*ppos >= len) { + kfree(page); + return 0; + } + err = copy_to_user(buffer, (char *)page, len); + *ppos += len; + if (err) { + kfree(page); + return err; + } + kfree(page); + return len; + + /* return (ptr - page); */ +} + +static int gt91xx_config_write_proc(struct file *file, const char *buffer, size_t count, + loff_t *ppos) +{ + s32 ret = 0; + char temp[25] = { 0 }; /* for store special format cmd */ + char mode_str[15] = { 0 }; + unsigned int mode; + u8 buf[1]; + + GTP_DEBUG("write count %ld\n", (unsigned long)count); + + if (count > GTP_CONFIG_MAX_LENGTH) { + //GTP_ERROR("size not match [%d:%ld]", GTP_CONFIG_MAX_LENGTH, count); + return -EFAULT; + } + + /**********************************************/ + /* for store special format cmd */ + if (copy_from_user(temp, buffer, sizeof(temp))) { + GTP_ERROR("copy from user fail 2"); + return -EFAULT; + } + sscanf(temp, "%s %d", (char *)&mode_str, &mode); + + /***********POLLING/EINT MODE switch****************/ + if (strcmp(mode_str, "polling") == 0) { + if (mode >= 10 && mode <= 200) { + GTP_INFO("Switch to polling mode, polling time is %d", mode); + tpd_eint_mode = 0; + tpd_polling_time = mode; + tpd_flag = 1; + wake_up_interruptible(&waiter); + } else { + GTP_INFO("Wrong polling time, please set between 10~200ms"); + } + return count; + } + if (strcmp(mode_str, "eint") == 0) { + GTP_INFO("Switch to eint mode"); + tpd_eint_mode = 1; + return count; + } + /**********************************************/ + if (strcmp(mode_str, "switch") == 0) { + if (mode == 0) /* turn off */ + tpd_off(); + else if (mode == 1) /* turn on */ + tpd_on(); + else + GTP_ERROR("error mode :%d", mode); + return count; + } + /* force clear config */ + if (strcmp(mode_str, "clear_config") == 0) { + GTP_INFO("Force clear config"); + buf[0] = 0x10; + ret = i2c_write_bytes(i2c_client_point, GTP_REG_SLEEP, buf, 1); + return count; + } + + if (copy_from_user(&config[2], buffer, count)) { + GTP_ERROR("copy from user fail"); + return -EFAULT; + } + + /***********clk operate reseved****************/ + /**********************************************/ + ret = gtp_send_cfg(i2c_client_point); + abs_x_max = (config[RESOLUTION_LOC + 1] << 8) + config[RESOLUTION_LOC]; + abs_y_max = (config[RESOLUTION_LOC + 3] << 8) + config[RESOLUTION_LOC + 2]; + int_type = (config[TRIGGER_LOC]) & 0x03; + + if (ret < 0) { + GTP_ERROR("send config failed."); + } + + return count; +} + +int i2c_read_bytes(struct i2c_client *client, u16 addr, u8 *rxbuf, int len) +{ + u8 buffer[GTP_ADDR_LENGTH]; + u16 left = len; + u16 offset = 0; + + struct i2c_msg msg[2] = { + { + .addr = ((client->addr & I2C_MASK_FLAG) | (I2C_ENEXT_FLAG)), + /* .addr = (client->addr &I2C_MASK_FLAG), */ + /* .ext_flag = I2C_ENEXT_FLAG, */ + /* .addr = ((client->addr &I2C_MASK_FLAG) | (I2C_PUSHPULL_FLAG)), */ + .flags = 0, + .buf = buffer, + .len = GTP_ADDR_LENGTH, + .timing = I2C_MASTER_CLOCK}, + { + .addr = ((client->addr & I2C_MASK_FLAG) | (I2C_ENEXT_FLAG)), + /* .addr = (client->addr &I2C_MASK_FLAG), */ + /* .ext_flag = I2C_ENEXT_FLAG, */ + /* .addr = ((client->addr &I2C_MASK_FLAG) | (I2C_PUSHPULL_FLAG)), */ + .flags = I2C_M_RD, + .timing = I2C_MASTER_CLOCK}, + }; + + if (rxbuf == NULL) + return -1; + + GTP_DEBUG("i2c_read_bytes to device %02X address %04X len %d", client->addr, addr, len); + + while (left > 0) { + buffer[0] = ((addr + offset) >> 8) & 0xFF; + buffer[1] = (addr + offset) & 0xFF; + + msg[1].buf = &rxbuf[offset]; + + if (left > MAX_TRANSACTION_LENGTH) { + msg[1].len = MAX_TRANSACTION_LENGTH; + left -= MAX_TRANSACTION_LENGTH; + offset += MAX_TRANSACTION_LENGTH; + } else { + msg[1].len = left; + left = 0; + } + + if (i2c_transfer(client->adapter, &msg[0], 2) != 2) { + GTP_ERROR("I2C read 0x%X length=%d failed", addr + offset, len); + return -1; + } + } + + return 0; +} + +s32 gtp_i2c_read(struct i2c_client *client, u8 *buf, s32 len) +{ + s32 ret = -1; + u16 addr = (buf[0] << 8) + buf[1]; + + ret = i2c_read_bytes(client, addr, &buf[2], len - 2); + + if (!ret) { + return 2; + } else { + gtp_reset_guitar(client, 20); + return ret; + } +} + +int i2c_write_bytes(struct i2c_client *client, u16 addr, u8 *txbuf, int len) +{ + u8 buffer[MAX_TRANSACTION_LENGTH]; + u16 left = len; + u16 offset = 0; + + struct i2c_msg msg = { + .addr = ((client->addr & I2C_MASK_FLAG) | (I2C_ENEXT_FLAG)), + /* .addr = (client->addr &I2C_MASK_FLAG), */ + /* .ext_flag = I2C_ENEXT_FLAG, */ + /* .addr = ((client->addr &I2C_MASK_FLAG) | (I2C_PUSHPULL_FLAG)), */ + .flags = 0, + .buf = buffer, + .timing = I2C_MASTER_CLOCK, + }; + + + if (txbuf == NULL) + return -1; + + GTP_DEBUG("i2c_write_bytes to device %02X address %04X len %d", client->addr, addr, len); + + while (left > 0) { + buffer[0] = ((addr + offset) >> 8) & 0xFF; + buffer[1] = (addr + offset) & 0xFF; + + if (left > MAX_I2C_TRANSFER_SIZE) { + memcpy(&buffer[GTP_ADDR_LENGTH], &txbuf[offset], MAX_I2C_TRANSFER_SIZE); + msg.len = MAX_TRANSACTION_LENGTH; + left -= MAX_I2C_TRANSFER_SIZE; + offset += MAX_I2C_TRANSFER_SIZE; + } else { + memcpy(&buffer[GTP_ADDR_LENGTH], &txbuf[offset], left); + msg.len = left + GTP_ADDR_LENGTH; + left = 0; + } + + /* GTP_DEBUG("byte left %d offset %d", left, offset); */ + + if (i2c_transfer(client->adapter, &msg, 1) != 1) { + GTP_ERROR("I2C write 0x%X%X length=%d failed", buffer[0], buffer[1], len); + return -1; + } + } + + return 0; +} + +s32 gtp_i2c_write(struct i2c_client *client, u8 *buf, s32 len) +{ + s32 ret = -1; + u16 addr = (buf[0] << 8) + buf[1]; + + ret = i2c_write_bytes(client, addr, &buf[2], len - 2); + + if (!ret) { + return 1; + } else { + gtp_reset_guitar(client, 20); + return ret; + } +} + + + +/******************************************************* +Function: + Send config Function. + +Input: + client: i2c client. + +Output: + Executive outcomes.0--success,non-0--fail. +*******************************************************/ +s32 gtp_send_cfg(struct i2c_client *client) +{ + s32 ret = 0; +#if GTP_DRIVER_SEND_CFG + s32 retry = 0; + + for (retry = 0; retry < 5; retry++) { +#ifdef GTP_CHARGER_DETECT + + if (gtp_charger_mode == 1) { + GTP_DEBUG("Write charger config"); + ret = + gtp_i2c_write(client, config_charger, + GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH); + } else { + GTP_DEBUG("Write normal config"); + ret = + gtp_i2c_write(client, config, GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH); + } + +#else + ret = gtp_i2c_write(client, config, GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH); +#endif + + + if (ret > 0) { + break; + } + } + +#endif + + return ret; +} + +/******************************************************* +Function: + Read goodix touchscreen version function. + +Input: + client: i2c client struct. + version:address to store version info + +Output: + Executive outcomes.0---succeed. +*******************************************************/ +s32 gtp_read_version(struct i2c_client *client, u16 *version) +{ + s32 ret = -1; + s32 i; + u8 buf[8] = { GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff }; + + GTP_DEBUG_FUNC(); + + ret = gtp_i2c_read(client, buf, sizeof(buf)); + + if (ret < 0) { + GTP_ERROR("GTP read version failed"); + return ret; + } + + if (version) { + *version = (buf[7] << 8) | buf[6]; + } + + tpd_info.vid = *version; + tpd_info.pid = 0x00; + + /* for gt9xx series */ + for (i = 0; i < 3; i++) { + if (buf[i + 2] < 0x30) + break; + + tpd_info.pid |= ((buf[i + 2] - 0x30) << ((2 - i) * 4)); + } + + GTP_INFO("IC VERSION:%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[7], buf[6]); + + return ret; +} + +/******************************************************* +Function: + GTP initialize function. + +Input: + client: i2c client private struct. + +Output: + Executive outcomes.0---succeed. +*******************************************************/ +static s32 gtp_init_panel(struct i2c_client *client) +{ + s32 ret = -1; + +#if GTP_DRIVER_SEND_CFG + s32 i; + u8 check_sum = 0; + u8 rd_cfg_buf[16]; + + u8 cfg_info_group1[] = CTP_CFG_GROUP1; + u8 cfg_info_group2[] = CTP_CFG_GROUP2; + u8 cfg_info_group3[] = CTP_CFG_GROUP3; + u8 *send_cfg_buf[3] = { cfg_info_group1, cfg_info_group2, cfg_info_group3 }; +#ifdef GTP_CHARGER_DETECT + u8 cfg_info_group1_charger[] = CTP_CFG_GROUP1_CHARGER; + u8 cfg_info_group2_charger[] = CTP_CFG_GROUP2_CHARGER; + u8 cfg_info_group3_charger[] = CTP_CFG_GROUP3_CHARGER; + u8 *send_cfg_buf_charger[3] = + { cfg_info_group1_charger, cfg_info_group2_charger, cfg_info_group3_charger }; +#endif + u8 cfg_info_len[3] = { sizeof(cfg_info_group1) / sizeof(cfg_info_group1[0]), + sizeof(cfg_info_group2) / sizeof(cfg_info_group2[0]), + sizeof(cfg_info_group3) / sizeof(cfg_info_group3[0]) + }; + + for (i = 0; i < 3; i++) { + if (cfg_info_len[i] > cfg_len) { + cfg_len = cfg_info_len[i]; + } + } + + GTP_DEBUG("len1=%d,len2=%d,len3=%d,get_len=%d", cfg_info_len[0], cfg_info_len[1], + cfg_info_len[2], cfg_len); + + if ((!cfg_info_len[1]) && (!cfg_info_len[2])) { + rd_cfg_buf[GTP_ADDR_LENGTH] = 0; + } else { + rd_cfg_buf[0] = GTP_REG_SENSOR_ID >> 8; + rd_cfg_buf[1] = GTP_REG_SENSOR_ID & 0xff; + ret = gtp_i2c_read(client, rd_cfg_buf, 3); + + if (ret < 0) { + GTP_ERROR("Read SENSOR ID failed,default use group1 config!"); + rd_cfg_buf[GTP_ADDR_LENGTH] = 0; + goto out; + } + + rd_cfg_buf[GTP_ADDR_LENGTH] &= 0x03; + } + + GTP_INFO("SENSOR ID:%d", rd_cfg_buf[GTP_ADDR_LENGTH]); + memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH); + memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf[rd_cfg_buf[GTP_ADDR_LENGTH]], cfg_len); +#ifdef GTP_CHARGER_DETECT + memset(&config_charger[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH); + memcpy(&config_charger[GTP_ADDR_LENGTH], send_cfg_buf_charger[rd_cfg_buf[GTP_ADDR_LENGTH]], + cfg_len); +#endif + +#if GTP_CUSTOM_CFG + config[RESOLUTION_LOC] = (u8) GTP_MAX_WIDTH; + config[RESOLUTION_LOC + 1] = (u8) (GTP_MAX_WIDTH >> 8); + config[RESOLUTION_LOC + 2] = (u8) GTP_MAX_HEIGHT; + config[RESOLUTION_LOC + 3] = (u8) (GTP_MAX_HEIGHT >> 8); + + if (GTP_INT_TRIGGER == 0) /* RISING */ + { + config[TRIGGER_LOC] &= 0xfe; + } else if (GTP_INT_TRIGGER == 1) /* FALLING */ + { + config[TRIGGER_LOC] |= 0x01; + } +#endif /* endif GTP_CUSTOM_CFG */ + + check_sum = 0; + + for (i = GTP_ADDR_LENGTH; i < cfg_len; i++) { + check_sum += config[i]; + } + + config[cfg_len] = (~check_sum) + 1; +#ifdef GTP_CHARGER_DETECT + check_sum = 0; + + for (i = GTP_ADDR_LENGTH; i < cfg_len; i++) { + check_sum += config_charger[i]; + } + + config_charger[cfg_len] = (~check_sum) + 1; +#endif +#else /* else DRIVER NEED NOT SEND CONFIG */ + + if (cfg_len == 0) { + cfg_len = GTP_CONFIG_MAX_LENGTH; + } + + ret = gtp_i2c_read(client, config, cfg_len + GTP_ADDR_LENGTH); + + if (ret < 0) { + GTP_ERROR("GTP read resolution & max_touch_num failed, use default value!"); + abs_x_max = GTP_MAX_WIDTH; + abs_y_max = GTP_MAX_HEIGHT; + int_type = GTP_INT_TRIGGER; + goto out; + } +#endif /* endif GTP_DRIVER_SEND_CFG */ + + abs_x_max = (config[RESOLUTION_LOC + 1] << 8) + config[RESOLUTION_LOC]; + abs_y_max = (config[RESOLUTION_LOC + 3] << 8) + config[RESOLUTION_LOC + 2]; + int_type = (config[TRIGGER_LOC]) & 0x03; + + if ((!abs_x_max) || (!abs_y_max)) { + GTP_ERROR("GTP resolution & max_touch_num invalid, use default value!"); + abs_x_max = GTP_MAX_WIDTH; + abs_y_max = GTP_MAX_HEIGHT; + } + + ret = gtp_send_cfg(client); + + if (ret < 0) { + GTP_ERROR("Send config error."); + goto out; + } + + GTP_DEBUG("X_MAX = %d,Y_MAX = %d,TRIGGER = 0x%02x", abs_x_max, abs_y_max, int_type); + + msleep(10); + out: + return ret; +} + +static s8 gtp_i2c_test(struct i2c_client *client) +{ + + u8 retry = 0; + s8 ret = -1; + u32 hw_info = 0; + + GTP_DEBUG_FUNC(); + + while (retry++ < 5) { + ret = i2c_read_bytes(client, GTP_REG_HW_INFO, (u8 *) &hw_info, sizeof(hw_info)); + + if ((!ret) && (hw_info == 0x00900600)) /* 20121212 */ + { + return ret; + } + + GTP_ERROR("GTP_REG_HW_INFO : %08X", hw_info); + GTP_ERROR("GTP i2c test failed time %d.", retry); + msleep(10); + } + + return -1; +} + +/******************************************************* +Function: + Set INT pin as input for FW sync. + +Note: + If the INT is high, It means there is pull up resistor attached on the INT pin. + Pull low the INT pin manaully for FW sync. +*******************************************************/ +void gtp_int_sync(void) +{ + GTP_DEBUG("There is pull up resisitor attached on the INT pin~!"); + GTP_GPIO_OUTPUT(GTP_INT_PORT, 0); + msleep(50); + GTP_GPIO_AS_INT(GTP_INT_PORT); +} + +void gtp_reset_guitar(struct i2c_client *client, s32 ms) +{ + GTP_INFO("GTP RESET!"); + GTP_GPIO_OUTPUT(GTP_RST_PORT, 0); + msleep(ms); + GTP_GPIO_OUTPUT(GTP_INT_PORT, client->addr == 0x14); + msleep(2); + GTP_GPIO_OUTPUT(GTP_RST_PORT, 1); + msleep(6); + gtp_int_sync(); + + return; +} + +static int tpd_power_on(struct i2c_client *client) +{ + int ret = 0; + int reset_count = 0; + + reset_proc: + GTP_GPIO_OUTPUT(GTP_INT_PORT, 0); + GTP_GPIO_OUTPUT(GTP_RST_PORT, 0); + msleep(10); + /* power on, need confirm with SA */ +#ifdef TPD_POWER_SOURCE_CUSTOM + hwPowerOn(TPD_POWER_SOURCE_CUSTOM, VOL_2800, "TP"); +#else + hwPowerOn(MT65XX_POWER_LDO_VGP2, VOL_2800, "TP"); +#endif +#ifdef TPD_POWER_SOURCE_1800 + hwPowerOn(TPD_POWER_SOURCE_1800, VOL_1800, "TP"); +#endif + + gtp_reset_guitar(client, 20); + GTP_ERROR("GTP_INT_PORT:0x%x, GTP_RST_PORT:0x%x", GTP_INT_PORT, GTP_RST_PORT); + ret = gtp_i2c_test(client); + + if (ret < 0) { + GTP_ERROR("I2C communication ERROR!"); + + if (reset_count < TPD_MAX_RESET_COUNT) { + reset_count++; + goto reset_proc; + } else { + goto out; + } + } +#if GTP_FW_DOWNLOAD + ret = gup_init_fw_proc(client); + + if (ret < 0) { + GTP_ERROR("Create fw download thread error."); + } +#endif + out: + return ret; +} + +#ifdef CONFIG_MTK_CTP_RESET_CONFIG +static int tpd_clear_config(void *unused) +{ + int ret = 0, check_sum = 0; + u8 temp_data = 0, i = 0; + u8 config_1st[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH] + = { GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff }; + + GTP_INFO("Clear Config Begin......"); + msleep(10000); /* wait main thread to be completed */ + + ret = i2c_read_bytes(i2c_client_point, GTP_REG_CONFIG_DATA, &temp_data, 1); + if (ret < 0) { + GTP_ERROR("GTP read config failed!"); + return -1; + } + + GTP_INFO("IC config version: 0x%x; Driver config version: 0x%x", temp_data, + config[GTP_ADDR_LENGTH]); + if ((temp_data < (u8) 0x5A) && (temp_data > config[GTP_ADDR_LENGTH])) { + memset(&config_1st[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH); + memcpy(&config_1st[GTP_ADDR_LENGTH], &config[GTP_ADDR_LENGTH], cfg_len); + config_1st[GTP_ADDR_LENGTH] = 0; + check_sum = 0; + + for (i = GTP_ADDR_LENGTH; i < cfg_len; i++) { + check_sum += config_1st[i]; + } + + config_1st[cfg_len] = (~check_sum) + 1; + ret = + gtp_i2c_write(i2c_client_point, config_1st, + GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH); + if (ret < 0) { + GTP_ERROR("GTP write 00 config failed!"); + } else { + GTP_INFO("Force clear cfg done"); + } + } else { + GTP_INFO("No need clear cfg"); + } + return 0; +} +#endif + +static const struct file_operations gt_upgrade_proc_fops = { + .write = gt91xx_config_write_proc, + .read = gt91xx_config_read_proc +}; + + +static s32 tpd_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + s32 err = 0; + s32 ret = 0; + u16 version_info; + struct task_struct *thread = NULL; +#if 0 /* GTP_HAVE_TOUCH_KEY */ + s32 idx = 0; +#endif +#ifdef TPD_PROXIMITY + struct hwmsen_object obj_ps; +#endif + + i2c_client_point = client; + ret = tpd_power_on(client); + + if (ret < 0) { + GTP_ERROR("I2C communication ERROR!"); + goto out; + } +#ifdef CONFIG_MTK_CTP_RESET_CONFIG + thread = kthread_run(tpd_clear_config, 0, "mtk-tpd-clear-config"); + if (IS_ERR(thread)) { + err = PTR_ERR(thread); + GTP_INFO(TPD_DEVICE " failed to create kernel thread for clearing config: %d", err); + } + thread = NULL; +#endif + +#if GTP_AUTO_UPDATE + ret = gup_init_update_proc(client); + + if (ret < 0) { + GTP_ERROR("Create update thread error."); + goto out; + } +#endif + + + +#ifdef VELOCITY_CUSTOM + tpd_v_magnify_x = TPD_VELOCITY_CUSTOM_X; + tpd_v_magnify_y = TPD_VELOCITY_CUSTOM_Y; + +#endif + + ret = gtp_read_version(client, &version_info); + + if (ret < 0) { + GTP_ERROR("Read version failed."); + goto out; + } + + ret = gtp_init_panel(client); + + if (ret < 0) { + GTP_ERROR("GTP init panel failed."); + goto out; + } + GTP_DEBUG("gtp_init_panel success"); + /* Create proc file system */ + gt91xx_config_proc = + proc_create(GT91XX_CONFIG_PROC_FILE, 0660, NULL, >_upgrade_proc_fops); + + if (gt91xx_config_proc == NULL) { + GTP_ERROR("create_proc_entry %s failed", GT91XX_CONFIG_PROC_FILE); + goto out; + } +#if GTP_CREATE_WR_NODE + init_wr_node(client); +#endif + + thread = kthread_run(touch_event_handler, 0, TPD_DEVICE); + + if (IS_ERR(thread)) { + err = PTR_ERR(thread); + GTP_ERROR(TPD_DEVICE " failed to create kernel thread: %d", err); + goto out; + } +#if 0 /* GTP_HAVE_TOUCH_KEY */ + + for (idx = 0; idx < TPD_KEY_COUNT; idx++) { + input_set_capability(tpd->dev, EV_KEY, touch_key_array[idx]); + } + +#endif + + if (!int_type) /* EINTF_TRIGGER */ + { + mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, EINTF_TRIGGER_RISING, + tpd_eint_interrupt_handler, 0); + } else { + mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, EINTF_TRIGGER_FALLING, tpd_eint_interrupt_handler, 0); /* disable auto-unmask */ + } + + mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM); + +#ifdef TPD_PROXIMITY + /* obj_ps.self = cm3623_obj; */ + obj_ps.polling = 0; /* 0--interrupt mode;1--polling mode; */ + obj_ps.sensor_operate = tpd_ps_operate; + + if ((err = hwmsen_attach(ID_PROXIMITY, &obj_ps))) { + GTP_ERROR("hwmsen attach fail, return:%d.", err); + } +#endif + +#if GTP_ESD_PROTECT + INIT_DELAYED_WORK(>p_esd_check_work, gtp_esd_check_func); + gtp_esd_check_workqueue = create_workqueue("gtp_esd_check"); + queue_delayed_work(gtp_esd_check_workqueue, >p_esd_check_work, TPD_ESD_CHECK_CIRCLE); +#endif + +#ifdef GTP_CHARGER_DETECT + INIT_DELAYED_WORK(>p_charger_check_work, gtp_charger_check_func); + gtp_charger_check_workqueue = create_workqueue("gtp_charger_check"); + queue_delayed_work(gtp_charger_check_workqueue, >p_charger_check_work, + TPD_CHARGER_CHECK_CIRCLE); +#endif + tpd_load_status = 1; + + GTP_INFO("%s, success run Done", __func__); + return 0; + out: + return -1; +} + +static void tpd_eint_interrupt_handler(void) +{ + TPD_DEBUG_PRINT_INT; + tpd_flag = 1; + wake_up_interruptible(&waiter); +} + +static int tpd_i2c_remove(struct i2c_client *client) +{ +#if GTP_CREATE_WR_NODE + uninit_wr_node(); +#endif + +#if GTP_ESD_PROTECT + destroy_workqueue(gtp_esd_check_workqueue); +#endif + +#if GTP_ESD_PROTECT + destroy_workqueue(gtp_charger_check_workqueue); +#endif + return 0; +} + +#ifdef GTP_CHARGER_DETECT +static void gtp_charger_check_func(struct work_struct *work) +{ + int cur_charger_state; + cur_charger_state = upmu_get_pchr_chrdet(); + + GTP_DEBUG("Charger mode = %d", cur_charger_state); + + if (gtp_charger_mode != cur_charger_state) { + GTP_DEBUG("Charger state change detected~!"); + GTP_DEBUG("Charger mode = %d", cur_charger_state); + gtp_charger_mode = cur_charger_state; + gtp_send_cfg(i2c_client_point); + } + + if (!tpd_halt) { + queue_delayed_work(gtp_charger_check_workqueue, >p_charger_check_work, + TPD_CHARGER_CHECK_CIRCLE); + } + + return; +} +#endif + +#if GTP_ESD_PROTECT +static void force_reset_guitar(void) +{ + s32 i; + s32 ret; + + GTP_INFO("force_reset_guitar"); + + /* Power off TP */ +#ifdef TPD_POWER_SOURCE_CUSTOM + hwPowerDown(TPD_POWER_SOURCE_CUSTOM, "TP"); +#else + hwPowerDown(MT65XX_POWER_LDO_VGP2, "TP"); +#endif +#ifdef TPD_POWER_SOURCE_1800 + hwPowerDown(TPD_POWER_SOURCE_1800, "TP"); +#endif + msleep(30); + /* Power on TP */ +#ifdef TPD_POWER_SOURCE_CUSTOM + hwPowerOn(TPD_POWER_SOURCE_CUSTOM, VOL_2800, "TP"); +#else + hwPowerOn(MT65XX_POWER_LDO_VGP2, VOL_2800, "TP"); +#endif +#ifdef TPD_POWER_SOURCE_1800 + hwPowerOn(TPD_POWER_SOURCE_1800, VOL_1800, "TP"); +#endif + msleep(30); + for (i = 0; i < 5; i++) { + /* Reset Guitar */ + gtp_reset_guitar(i2c_client_point, 20); + + /* Send config */ + ret = gtp_send_cfg(i2c_client_point); + + if (ret < 0) { + continue; + } + + break; + } + +} + +static void gtp_esd_check_func(struct work_struct *work) +{ + int i; + int ret = -1; + u8 test[3] = { GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff }; + + if (tpd_halt) { + return; + } + + for (i = 0; i < 3; i++) { + ret = gtp_i2c_read(i2c_client_point, test, 3); + + if (ret > 0) { + break; + } + } + + if (i >= 3) { + force_reset_guitar(); + } + + if (!tpd_halt) { + queue_delayed_work(gtp_esd_check_workqueue, >p_esd_check_work, + TPD_ESD_CHECK_CIRCLE); + } + + return; +} +#endif +static int tpd_history_x = 0, tpd_history_y; +static void tpd_down(s32 x, s32 y, s32 size, s32 id) +{ + if ((!size) && (!id)) { + input_report_abs(tpd->dev, ABS_MT_PRESSURE, 100); + input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, 100); + } else { + input_report_abs(tpd->dev, ABS_MT_PRESSURE, size); + input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, size); + /* track id Start 0 */ + input_report_abs(tpd->dev, ABS_MT_TRACKING_ID, id); + } + + input_report_key(tpd->dev, BTN_TOUCH, 1); + input_report_abs(tpd->dev, ABS_MT_POSITION_X, x); + input_report_abs(tpd->dev, ABS_MT_POSITION_Y, y); + input_mt_sync(tpd->dev); + TPD_DEBUG_SET_TIME; + TPD_EM_PRINT(x, y, x, y, id, 1); + tpd_history_x = x; + tpd_history_y = y; + + MMProfileLogEx(MMP_TouchPanelEvent, MMProfileFlagPulse, 1, x + y); +#ifdef TPD_HAVE_BUTTON + + if (FACTORY_BOOT == get_boot_mode() || RECOVERY_BOOT == get_boot_mode()) { + tpd_button(x, y, 1); + } +#endif +} + +static void tpd_up(s32 x, s32 y, s32 id) +{ + /* input_report_abs(tpd->dev, ABS_MT_PRESSURE, 0); */ + input_report_key(tpd->dev, BTN_TOUCH, 0); + /* input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, 0); */ + input_mt_sync(tpd->dev); + TPD_DEBUG_SET_TIME; + TPD_EM_PRINT(tpd_history_x, tpd_history_y, tpd_history_x, tpd_history_y, id, 0); + tpd_history_x = 0; + tpd_history_y = 0; + MMProfileLogEx(MMP_TouchPanelEvent, MMProfileFlagPulse, 0, x + y); + +#ifdef TPD_HAVE_BUTTON + + if (FACTORY_BOOT == get_boot_mode() || RECOVERY_BOOT == get_boot_mode()) { + tpd_button(x, y, 0); + } +#endif +} + +static int touch_event_handler(void *unused) +{ + struct sched_param param = {.sched_priority = RTPM_PRIO_TPD }; + u8 end_cmd[3] = { GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0 }; + u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1] = + { GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF }; + u8 touch_num = 0; + u8 finger = 0; + static u8 pre_touch; + static u8 pre_key; + u8 key_value = 0; + u8 *coor_data = NULL; + s32 input_x = 0; + s32 input_y = 0; + s32 input_w = 0; + s32 id = 0; + s32 i = 0; + s32 ret = -1; +#ifdef TPD_PROXIMITY + s32 err = 0; + hwm_sensor_data sensor_data; + u8 proximity_status; +#endif +#if GTP_CHANGE_X2Y + s32 temp; +#endif + + sched_setscheduler(current, SCHED_RR, ¶m); + + do { + set_current_state(TASK_INTERRUPTIBLE); + if (tpd_eint_mode) { + wait_event_interruptible(waiter, tpd_flag != 0); + tpd_flag = 0; + } else { + msleep(tpd_polling_time); + } + set_current_state(TASK_RUNNING); + + mutex_lock(&i2c_access); + + if (tpd_halt) { + mutex_unlock(&i2c_access); + GTP_DEBUG("return for interrupt after suspend... "); + continue; + } + + ret = gtp_i2c_read(i2c_client_point, point_data, 12); + + if (ret < 0) { + GTP_ERROR("I2C transfer error. errno:%d ", ret); + goto exit_work_func; + } + + finger = point_data[GTP_ADDR_LENGTH]; + + if ((finger & 0x80) == 0) { + mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM); + mutex_unlock(&i2c_access); + GTP_ERROR("buffer not ready"); + continue; + } +#ifdef TPD_PROXIMITY + + if (tpd_proximity_flag == 1) { + proximity_status = point_data[GTP_ADDR_LENGTH]; + GTP_DEBUG("REG INDEX[0x814E]:0x%02X", proximity_status); + + if (proximity_status & 0x60) /* proximity or large touch detect,enable hwm_sensor. */ + { + tpd_proximity_detect = 0; + /* sensor_data.values[0] = 0; */ + } else { + tpd_proximity_detect = 1; + /* sensor_data.values[0] = 1; */ + } + + /* get raw data */ + GTP_DEBUG(" ps change"); + GTP_DEBUG("PROXIMITY STATUS:0x%02X", tpd_proximity_detect); + /* map and store data to hwm_sensor_data */ + sensor_data.values[0] = tpd_get_ps_value(); + sensor_data.value_divide = 1; + sensor_data.status = SENSOR_STATUS_ACCURACY_MEDIUM; + /* report to the up-layer */ + ret = hwmsen_get_interrupt_data(ID_PROXIMITY, &sensor_data); + + if (ret) { + GTP_ERROR("Call hwmsen_get_interrupt_data fail = %d", err); + } + } +#endif + + touch_num = finger & 0x0f; + + if (touch_num > GTP_MAX_TOUCH) { + GTP_ERROR("Bad number of fingers!"); + goto exit_work_func; + } + + if (touch_num > 1) { + u8 buf[8 * GTP_MAX_TOUCH] = + { (GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff }; + + ret = gtp_i2c_read(i2c_client_point, buf, 2 + 8 * (touch_num - 1)); + memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1)); + } +#if GTP_HAVE_TOUCH_KEY + key_value = point_data[3 + 8 * touch_num]; + + if (key_value || pre_key) { + for (i = 0; i < TPD_KEY_COUNT; i++) { + /* input_report_key(tpd->dev, touch_key_array[i], key_value & (0x01 << i)); */ + if (key_value & (0x01 << i)) /* key=1 menu ;key=2 home; key =4 back; */ + { + input_x = touch_key_point_maping_array[i].point_x; + input_y = touch_key_point_maping_array[i].point_y; + GTP_DEBUG("button =%d %d", input_x, input_y); + + tpd_down(input_x, input_y, 0, 0); + } + } + + if ((pre_key != 0) && (key_value == 0)) { + tpd_up(0, 0, 0); + } + + touch_num = 0; + pre_touch = 0; + } +#endif + pre_key = key_value; + + GTP_DEBUG("pre_touch:%02x, finger:%02x.", pre_touch, finger); + + if (touch_num) { + for (i = 0; i < touch_num; i++) { + coor_data = &point_data[i * 8 + 3]; + + id = coor_data[0] & 0x0F; + input_x = coor_data[1] | coor_data[2] << 8; + input_y = coor_data[3] | coor_data[4] << 8; + input_w = coor_data[5] | coor_data[6] << 8; + + input_x = TPD_WARP_X(abs_x_max, input_x); + input_y = TPD_WARP_Y(abs_y_max, input_y); + +#if GTP_CHANGE_X2Y + temp = input_x; + input_x = input_y; + input_y = temp; +#endif + + tpd_down(input_x, input_y, input_w, id); + } + } else if (pre_touch) { + GTP_DEBUG("Touch Release!"); + tpd_up(0, 0, 0); + } else { + GTP_DEBUG("Additional Eint!"); + } + pre_touch = touch_num; + /* input_report_key(tpd->dev, BTN_TOUCH, (touch_num || key_value)); */ + + if (tpd != NULL && tpd->dev != NULL) { + input_sync(tpd->dev); + } + + exit_work_func: + + if (!gtp_rawdiff_mode) { + ret = gtp_i2c_write(i2c_client_point, end_cmd, 3); + + if (ret < 0) { + GTP_INFO("I2C write end_cmd error!"); + } + } + mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM); + mutex_unlock(&i2c_access); + + } + while (!kthread_should_stop()); + + return 0; +} + +static int tpd_local_init(void) +{ + + if (i2c_add_driver(&tpd_i2c_driver) != 0) { + GTP_INFO("unable to add i2c driver."); + return -1; + } + + if (tpd_load_status == 0) /* if(tpd_load_status == 0) // disable auto load touch driver for linux3.0 porting */ + { + GTP_INFO("add error touch panel driver."); + i2c_del_driver(&tpd_i2c_driver); + return -1; + } + input_set_abs_params(tpd->dev, ABS_MT_TRACKING_ID, 0, (GTP_MAX_TOUCH - 1), 0, 0); +#ifdef TPD_HAVE_BUTTON + tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local); /* initialize tpd button data */ +#endif + +#if (defined(TPD_WARP_START) && defined(TPD_WARP_END)) + TPD_DO_WARP = 1; + memcpy(tpd_wb_start, tpd_wb_start_local, TPD_WARP_CNT * 4); + memcpy(tpd_wb_end, tpd_wb_start_local, TPD_WARP_CNT * 4); +#endif + +#if (defined(TPD_HAVE_CALIBRATION) && !defined(TPD_CUSTOM_CALIBRATION)) + memcpy(tpd_calmat, tpd_def_calmat_local, 8 * 4); + memcpy(tpd_def_calmat, tpd_def_calmat_local, 8 * 4); +#endif + + /* set vendor string */ + tpd->dev->id.vendor = 0x00; + tpd->dev->id.product = tpd_info.pid; + tpd->dev->id.version = tpd_info.vid; + + GTP_INFO("end %s, %d", __func__, __LINE__); + tpd_type_cap = 1; + + return 0; +} + + +/******************************************************* +Function: + Eter sleep function. + +Input: + client:i2c_client. + +Output: + Executive outcomes.0--success,non-0--fail. +*******************************************************/ +static s8 gtp_enter_sleep(struct i2c_client *client) +{ + s8 ret = -1; +#if !GTP_POWER_CTRL_SLEEP + s8 retry = 0; + u8 i2c_control_buf[3] = { (u8) (GTP_REG_SLEEP >> 8), (u8) GTP_REG_SLEEP, 5 }; + + GTP_GPIO_OUTPUT(GTP_INT_PORT, 0); + msleep(5); + + while (retry++ < 5) { + ret = gtp_i2c_write(client, i2c_control_buf, 3); + + if (ret > 0) { + GTP_INFO("GTP enter sleep!"); + return ret; + } + + msleep(10); + } + +#else + + GTP_GPIO_OUTPUT(GTP_RST_PORT, 0); + msleep(5); + +#ifdef TPD_POWER_SOURCE_CUSTOM + hwPowerDown(TPD_POWER_SOURCE_CUSTOM, "TP"); +#else + hwPowerDown(MT65XX_POWER_LDO_VGP2, "TP"); +#endif +#ifdef TPD_POWER_SOURCE_1800 + hwPowerDown(TPD_POWER_SOURCE_1800, "TP"); +#endif + + GTP_INFO("GTP enter sleep!"); + return 0; + +#endif + GTP_ERROR("GTP send sleep cmd failed."); + return ret; +} + +/******************************************************* +Function: + Wakeup from sleep mode Function. + +Input: + client:i2c_client. + +Output: + Executive outcomes.0--success,non-0--fail. +*******************************************************/ +static s8 gtp_wakeup_sleep(struct i2c_client *client) +{ + u8 retry = 0; + s8 ret = -1; + + + GTP_INFO("GTP wakeup begin."); +#if GTP_POWER_CTRL_SLEEP + + while (retry++ < 5) { + ret = tpd_power_on(client); + + if (ret < 0) { + GTP_ERROR("I2C Power on ERROR!"); + } + + ret = gtp_send_cfg(client); + + if (ret > 0) { + GTP_DEBUG("Wakeup sleep send config success."); + return ret; + } + } + +#else + + while (retry++ < 10) { + GTP_GPIO_OUTPUT(GTP_INT_PORT, 1); + msleep(5); + GTP_GPIO_OUTPUT(GTP_INT_PORT, 0); + msleep(5); + ret = gtp_i2c_test(client); + + if (ret >= 0) { + gtp_int_sync(); + return ret; + } + + gtp_reset_guitar(client, 20); + } + +#endif + + GTP_ERROR("GTP wakeup sleep failed."); + return ret; +} + +/* Function to manage low power suspend */ +static void tpd_suspend(struct early_suspend *h) +{ + s32 ret = -1; + mutex_lock(&i2c_access); + mt_eint_mask(CUST_EINT_TOUCH_PANEL_NUM); + tpd_halt = 1; + mutex_unlock(&i2c_access); + + ret = gtp_enter_sleep(i2c_client_point); + if (ret < 0) { + GTP_ERROR("GTP early suspend failed."); + } +#if GTP_ESD_PROTECT + cancel_delayed_work_sync(>p_esd_check_work); +#endif + +#ifdef GTP_CHARGER_DETECT + cancel_delayed_work_sync(>p_charger_check_work); +#endif +#ifdef TPD_PROXIMITY + + if (tpd_proximity_flag == 1) { + return; + } +#endif +} + +/* Function to manage power-on resume */ +static void tpd_resume(struct early_suspend *h) +{ + s32 ret = -1; + + ret = gtp_wakeup_sleep(i2c_client_point); + + if (ret < 0) { + GTP_ERROR("GTP later resume failed."); + } + + GTP_INFO("GTP wakeup sleep."); + + mutex_lock(&i2c_access); + tpd_halt = 0; + mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM); + mutex_unlock(&i2c_access); + +#ifdef TPD_PROXIMITY + if (tpd_proximity_flag == 1) { + return; + } +#endif + +#if GTP_ESD_PROTECT + queue_delayed_work(gtp_esd_check_workqueue, >p_esd_check_work, TPD_ESD_CHECK_CIRCLE); +#endif + +#ifdef GTP_CHARGER_DETECT + queue_delayed_work(gtp_charger_check_workqueue, >p_charger_check_work, + TPD_CHARGER_CHECK_CIRCLE); +#endif + +} + +static void tpd_off(void) +{ + +#ifdef TPD_POWER_SOURCE_CUSTOM + hwPowerDown(TPD_POWER_SOURCE_CUSTOM, "TP"); +#else + hwPowerDown(MT65XX_POWER_LDO_VGP2, "TP"); +#endif +#ifdef TPD_POWER_SOURCE_1800 + hwPowerDown(TPD_POWER_SOURCE_1800, "TP"); +#endif + GTP_INFO("GTP enter sleep!"); + + tpd_halt = 1; + mt_eint_mask(CUST_EINT_TOUCH_PANEL_NUM); +} + +static void tpd_on(void) +{ + s32 ret = -1, retry = 0; + + while (retry++ < 5) { + ret = tpd_power_on(i2c_client_point); + + if (ret < 0) { + GTP_ERROR("I2C Power on ERROR!"); + } + + ret = gtp_send_cfg(i2c_client_point); + + if (ret > 0) { + GTP_DEBUG("Wakeup sleep send config success."); + } + } + if (ret < 0) { + GTP_ERROR("GTP later resume failed."); + } + + mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM); + tpd_halt = 0; +} + +static struct tpd_driver_t tpd_device_driver = { + .tpd_device_name = "gt9xx", + .tpd_local_init = tpd_local_init, + .suspend = tpd_suspend, + .resume = tpd_resume, +#ifdef TPD_HAVE_BUTTON + .tpd_have_button = 1, +#else + .tpd_have_button = 0, +#endif + .attrs = { + .attr = gt9xx_attrs, + .num = ARRAY_SIZE(gt9xx_attrs), + }, +}; + +/* called when loaded into kernel */ +static int __init tpd_driver_init(void) +{ + GTP_INFO("MediaTek gt91xx touch panel driver init"); +#if defined(TPD_I2C_NUMBER) + i2c_register_board_info(TPD_I2C_NUMBER, &i2c_tpd, 1); +#else + i2c_register_board_info(0, &i2c_tpd, 1); +#endif + if (tpd_driver_add(&tpd_device_driver) < 0) + GTP_INFO("add generic driver failed"); + + return 0; +} + +/* should never be called */ +static void __exit tpd_driver_exit(void) +{ + GTP_INFO("MediaTek gt91xx touch panel driver exit"); + /* input_unregister_device(tpd->dev); */ + tpd_driver_remove(&tpd_device_driver); +} +module_init(tpd_driver_init); +module_exit(tpd_driver_exit); diff --git a/drivers/input/touchscreen/mediatek/GT9XX/gt9xx_update.c b/drivers/input/touchscreen/mediatek/GT9XX/gt9xx_update.c new file mode 100644 index 000000000..cf49f909c --- /dev/null +++ b/drivers/input/touchscreen/mediatek/GT9XX/gt9xx_update.c @@ -0,0 +1,1855 @@ +/* drivers/input/touchscreen/gt813_827_828_update.c + * + * 2010 - 2012 Goodix Technology. + * + * 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 a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * 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. + * + * Version:1.2 + * V1.0:2012/08/31,first release. + * V1.2:2012/10/15,add force update,GT9110P pid map + */ +#include "tpd.h" +#include <linux/interrupt.h> +#include <cust_eint.h> +#include <linux/i2c.h> +#include <linux/sched.h> +#include <linux/kthread.h> +#include <linux/rtpm_prio.h> +#include <linux/wait.h> +#include <linux/time.h> +#include <linux/delay.h> +#include <linux/namei.h> +#include <linux/mount.h> +#include "cust_gpio_usage.h" +#include <asm/uaccess.h> + +#define GUP_FW_INFO +#include "tpd_custom_gt9xx.h" + +#define GUP_REG_HW_INFO 0x4220 +#define GUP_REG_FW_MSG 0x41E4 +#define GUP_REG_PID_VID 0x8140 + +/* #define GTP_BOOT_FW_CONFIG_SD_UPDATE */ +#define GUP_SEARCH_FILE_TIMES 50 +#define UPDATE_FILE_PATH_2 "/data/goodix/_goodix_update_.bin" +#define UPDATE_FILE_PATH_1 "/sdcard/goodix/_goodix_update_.bin" + +#define CONFIG_FILE_PATH_2 "/data/goodix/_goodix_config_.cfg" +#define CONFIG_FILE_PATH_1 "/sdcard/goodix/_goodix_config_.cfg" + +#define FW_HEAD_LENGTH 14 +#define FW_SECTION_LENGTH 0x2000 +#define FW_DSP_ISP_LENGTH 0x1000 +#define FW_DSP_LENGTH 0x1000 +#define FW_BOOT_LENGTH 0x800 +#define FW_DOWNLOAD_LENGTH 0x4000 +#define FW_LENGTH (4*FW_SECTION_LENGTH+FW_DSP_ISP_LENGTH+FW_DSP_LENGTH+FW_BOOT_LENGTH) + +#define PACK_SIZE 256 +#define MAX_FRAME_CHECK_TIME 5 + +#define _bRW_MISCTL__SRAM_BANK 0x4048 +#define _bRW_MISCTL__MEM_CD_EN 0x4049 +#define _bRW_MISCTL__CACHE_EN 0x404B +#define _bRW_MISCTL__TMR0_EN 0x40B0 +#define _rRW_MISCTL__SWRST_B0_ 0x4180 +#define _bWO_MISCTL__CPU_SWRST_PULSE 0x4184 +#define _rRW_MISCTL__BOOTCTL_B0_ 0x4190 +#define _rRW_MISCTL__BOOT_OPT_B0_ 0x4218 +#define _rRW_MISCTL__BOOT_CTL_ 0x5094 + +#define FAIL 0 +#define SUCCESS 1 + +#pragma pack(1) +typedef struct { + u8 hw_info[4]; /* hardware info// */ + u8 pid[8]; /* product id // */ + u16 vid; /* version id // */ +} st_fw_head; +#pragma pack() + +typedef struct { + u8 force_update; + u8 fw_flag; + struct file *file; + struct file *cfg_file; + st_fw_head ic_fw_msg; + mm_segment_t old_fs; +} st_update_msg; + +st_update_msg update_msg; +struct i2c_client *guitar_client = NULL; +extern struct i2c_client *i2c_client_point; +u16 show_len; +u16 total_len; +u8 *fw_buf; + +extern s32 gtp_i2c_read(struct i2c_client *client, uint8_t *buf, s32 len); +extern s32 gtp_i2c_write(struct i2c_client *client, uint8_t *data, s32 len); +extern void gtp_reset_guitar(struct i2c_client *client, s32 ms); +extern s32 gtp_send_cfg(struct i2c_client *client); + +static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8 *msg, s32 len) +{ + s32 i = 0; + + msg[0] = (addr >> 8) & 0xff; + msg[1] = addr & 0xff; + + for (i = 0; i < 5; i++) { + if (gtp_i2c_read(client, msg, GTP_ADDR_LENGTH + len) > 0) { + break; + } + } + + if (i >= 5) { + GTP_ERROR("Read data from 0x%02x%02x failed!", msg[0], msg[1]); + return FAIL; + } + + return SUCCESS; +} + +static u8 gup_set_ic_msg(struct i2c_client *client, u16 addr, u8 val) +{ + s32 i = 0; + u8 msg[3]; + + msg[0] = (addr >> 8) & 0xff; + msg[1] = addr & 0xff; + msg[2] = val; + + for (i = 0; i < 5; i++) { + if (gtp_i2c_write(client, msg, GTP_ADDR_LENGTH + 1) > 0) { + break; + } + } + + if (i >= 5) { + GTP_ERROR("Set data to 0x%02x%02x failed!", msg[0], msg[1]); + return FAIL; + } + + return SUCCESS; +} + +static u8 gup_get_ic_fw_msg(struct i2c_client *client) +{ + s32 ret = -1; + u8 retry = 0; + u8 buf[16]; + u8 i; + + /* step1:get hardware info */ + ret = gup_get_ic_msg(client, GUP_REG_HW_INFO, buf, 4); + + if (FAIL == ret) { + GTP_ERROR("Read hardware info fail."); + return ret; + } + + memcpy(update_msg.ic_fw_msg.hw_info, &buf[GTP_ADDR_LENGTH], 4); + + for (i = 0; i < 4; i++) { + update_msg.ic_fw_msg.hw_info[i] = buf[GTP_ADDR_LENGTH + 3 - i]; + } + + GTP_DEBUG("IC Hardware info:%02x%02x%02x%02x", update_msg.ic_fw_msg.hw_info[0], + update_msg.ic_fw_msg.hw_info[1], update_msg.ic_fw_msg.hw_info[2], + update_msg.ic_fw_msg.hw_info[3]); + + /* step2:get firmware message */ + for (retry = 0; retry < 2; retry++) { + ret = gup_get_ic_msg(client, GUP_REG_FW_MSG, buf, 1); + + if (FAIL == ret) { + GTP_ERROR("Read firmware message fail."); + return ret; + } + + update_msg.force_update = buf[GTP_ADDR_LENGTH]; + + if ((0xBE != update_msg.force_update) && (!retry)) { + GTP_INFO("The check sum in ic is error."); + GTP_INFO("The IC will be updated by force."); + continue; + } + + break; + } + + GTP_DEBUG("IC force update flag:0x%x", update_msg.force_update); + + /* step3:get pid & vid */ + ret = gup_get_ic_msg(client, GUP_REG_PID_VID, buf, 6); + + if (FAIL == ret) { + GTP_ERROR("Read product id & version id fail."); + return ret; + } + + memset(update_msg.ic_fw_msg.pid, 0, sizeof(update_msg.ic_fw_msg.pid)); + memcpy(update_msg.ic_fw_msg.pid, &buf[GTP_ADDR_LENGTH], 4); + GTP_DEBUG("IC Product id:%s", update_msg.ic_fw_msg.pid); + + /* GT9XX PID MAPPING */ + /*|-----FLASH-----RAM-----| + |------918------918-----| + |------968------968-----| + |------913------913-----| + |------913P-----913P----| + |------927------927-----| + |------927P-----927P----| + |------9110-----9110----| + |------9110P----9111----| */ + if (update_msg.ic_fw_msg.pid[0] != 0) { + if (!memcmp(update_msg.ic_fw_msg.pid, "9111", 4)) { + GTP_DEBUG("IC Mapping Product id:%s", update_msg.ic_fw_msg.pid); + memcpy(update_msg.ic_fw_msg.pid, "9110P", 5); + } + } + + update_msg.ic_fw_msg.vid = buf[GTP_ADDR_LENGTH + 4] + (buf[GTP_ADDR_LENGTH + 5] << 8); + GTP_DEBUG("IC version id:%04x", update_msg.ic_fw_msg.vid); + + return SUCCESS; +} + +s32 gup_enter_update_mode(struct i2c_client *client) +{ + s32 ret = -1; + s32 retry = 0; + u8 rd_buf[3]; + + /* step1:RST output low last at least 2ms */ + GTP_GPIO_OUTPUT(GTP_RST_PORT, 0); + msleep(2); + + /* step2:select I2C slave addr,INT:0--0xBA;1--0x28. */ + GTP_GPIO_OUTPUT(GTP_INT_PORT, (client->addr == 0x14)); + msleep(2); + + /* step3:RST output high reset guitar */ + GTP_GPIO_OUTPUT(GTP_RST_PORT, 1); + + /* 20121211 modify start */ + msleep(5); + + while (retry++ < 200) { + /* step4:Hold ss51 & dsp */ + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + + if (ret <= 0) { + GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry); + continue; + } + /* step5:Confirm hold */ + ret = gup_get_ic_msg(client, _rRW_MISCTL__SWRST_B0_, rd_buf, 1); + + if (ret <= 0) { + GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry); + continue; + } + + if (0x0C == rd_buf[GTP_ADDR_LENGTH]) { + GTP_DEBUG("Hold ss51 & dsp confirm SUCCESS"); + break; + } + + GTP_DEBUG("Hold ss51 & dsp confirm 0x4180 failed,value:%d", + rd_buf[GTP_ADDR_LENGTH]); + } + + if (retry >= 200) { + GTP_ERROR("Enter update Hold ss51 failed."); + return FAIL; + } + /* step6:DSP_CK and DSP_ALU_CK PowerOn */ + ret = gup_set_ic_msg(client, 0x4010, 0x00); + + /* 20121211 modify end */ + + return ret; +} + +void gup_leave_update_mode(void) +{ + s32 ret = 0; + /* GTP_GPIO_AS_INT(GTP_INT_PORT); */ + + GTP_DEBUG("[leave_update_mode]reset chip."); + gtp_reset_guitar(guitar_client, 20); + + msleep(100); + GTP_DEBUG("[update_proc]send config."); + ret = gtp_send_cfg(guitar_client); + + if (ret < 0) { + GTP_ERROR("[update_proc]send config fail."); + } +} + +static u8 gup_enter_upadte_judge(st_fw_head *fw_head) +{ + u16 u16_tmp; + /* Get the correct nvram data */ + /* The correct conditions: */ + /* 1. the hardware info is the same */ + /* 2. the product id is the same */ + /* 3. the firmware version in update file is greater than the firmware version in ic */ + /* or the check sum in ic is wrong */ + + u16_tmp = fw_head->vid; + fw_head->vid = (u16) (u16_tmp >> 8) + (u16) (u16_tmp << 8); + + GTP_DEBUG("FILE HARDWARE INFO:%02x%02x%02x%02x", fw_head->hw_info[0], fw_head->hw_info[1], + fw_head->hw_info[2], fw_head->hw_info[3]); + GTP_DEBUG("FILE PID:%s", fw_head->pid); + GTP_DEBUG("FILE VID:%04x", fw_head->vid); + + GTP_DEBUG("IC HARDWARE INFO:%02x%02x%02x%02x", update_msg.ic_fw_msg.hw_info[0], + update_msg.ic_fw_msg.hw_info[1], update_msg.ic_fw_msg.hw_info[2], + update_msg.ic_fw_msg.hw_info[3]); + GTP_DEBUG("IC PID:%s", update_msg.ic_fw_msg.pid); + GTP_DEBUG("IC VID:%04x", update_msg.ic_fw_msg.vid); + + /* First two conditions */ + if (!memcmp + (fw_head->hw_info, update_msg.ic_fw_msg.hw_info, + sizeof(update_msg.ic_fw_msg.hw_info))) { + GTP_DEBUG("Get the same hardware info."); + + if (update_msg.force_update != 0xBE) { + GTP_INFO("FW chksum error,need enter update."); + return SUCCESS; + } + + if ((!memcmp + (fw_head->pid, update_msg.ic_fw_msg.pid, strlen(update_msg.ic_fw_msg.pid))) + || (!memcmp(update_msg.ic_fw_msg.pid, "91XX", 4)) + || (!memcmp(fw_head->pid, "91XX", 4))) { + if (!memcmp(fw_head->pid, "91XX", 4)) { + GTP_DEBUG("Force none same pid update mode."); + } else { + GTP_DEBUG("Get the same pid."); + } + + /* The third condition */ + if (fw_head->vid > update_msg.ic_fw_msg.vid) { + + GTP_INFO("Need enter update."); + return SUCCESS; + } + + GTP_ERROR("Don't meet the third condition."); + } + } + + return FAIL; +} + + +#ifdef GTP_BOOT_FW_CONFIG_SD_UPDATE +static u8 ascii2hex(u8 a) +{ + s8 value = 0; + + if (a >= '0' && a <= '9') { + value = a - '0'; + } else if (a >= 'A' && a <= 'F') { + value = a - 'A' + 0x0A; + } else if (a >= 'a' && a <= 'f') { + value = a - 'a' + 0x0A; + } else { + value = 0xff; + } + + return value; +} + +static s8 gup_update_config(struct i2c_client *client) +{ + s32 file_len = 0; + s32 ret = 0; + s32 i = 0; + s32 file_cfg_len = 0; + s32 chip_cfg_len = 0; + s32 count = 0; + u8 *buf; + u8 *pre_buf; + u8 *file_config; + /* u8 checksum = 0; */ + + if (IS_ERR(update_msg.cfg_file)) { + GTP_ERROR("[update_cfg]No need to upgrade config!"); + return FAIL; + } + + file_len = update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, 0, SEEK_END); + + chip_cfg_len = 186; + + if (!memcmp(update_msg.ic_fw_msg.pid, "968", 3)) { + chip_cfg_len = 228; + } + + GTP_DEBUG("[update_cfg]config file len:%d", file_len); + GTP_DEBUG("[update_cfg]need config len:%d", chip_cfg_len); + + if ((file_len + 5) < chip_cfg_len * 5) { + GTP_ERROR("Config length error"); + return -1; + } + + buf = (u8 *) kzalloc(file_len, GFP_KERNEL); + pre_buf = (u8 *) kzalloc(file_len, GFP_KERNEL); + file_config = (u8 *) kzalloc(chip_cfg_len + GTP_ADDR_LENGTH, GFP_KERNEL); + update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, 0, SEEK_SET); + + GTP_DEBUG("[update_cfg]Read config from file."); + ret = + update_msg.cfg_file->f_op->read(update_msg.cfg_file, (char *)pre_buf, file_len, + &update_msg.cfg_file->f_pos); + + if (ret < 0) { + GTP_ERROR("[update_cfg]Read config file failed."); + goto update_cfg_file_failed; + } + + GTP_DEBUG("[update_cfg]Delete illgal charactor."); + + for (i = 0, count = 0; i < file_len; i++) { + if (pre_buf[i] == ' ' || pre_buf[i] == '\r' || pre_buf[i] == '\n') { + continue; + } + + buf[count++] = pre_buf[i]; + } + + GTP_DEBUG("[update_cfg]Ascii to hex."); + file_config[0] = GTP_REG_CONFIG_DATA >> 8; + file_config[1] = GTP_REG_CONFIG_DATA & 0xff; + + for (i = 0, file_cfg_len = GTP_ADDR_LENGTH; i < count; i += 5) { + if ((buf[i] == '0') && ((buf[i + 1] == 'x') || (buf[i + 1] == 'X'))) { + u8 high, low; + high = ascii2hex(buf[i + 2]); + low = ascii2hex(buf[i + 3]); + + if ((high == 0xFF) || (low == 0xFF)) { + ret = 0; + GTP_ERROR("[update_cfg]Illegal config file."); + goto update_cfg_file_failed; + } + + file_config[file_cfg_len++] = (high << 4) + low; + } else { + ret = 0; + GTP_ERROR("[update_cfg]Illegal config file."); + goto update_cfg_file_failed; + } + } + + GTP_DEBUG("config:"); + GTP_DEBUG_ARRAY(file_config, file_cfg_len); + + /* cal checksum */ +#if 0 + + for (i = GTP_ADDR_LENGTH; i < chip_cfg_len; i++) { + checksum += file_config[i]; + } + + file_config[chip_cfg_len] = (~checksum) + 1; +#endif + + i = 0; + + while (i++ < 5) { + ret = gtp_i2c_write(client, file_config, file_cfg_len); + + if (ret > 0) { + GTP_INFO("[update_cfg]Send config SUCCESS."); + break; + } + + GTP_ERROR("[update_cfg]Send config i2c error."); + } + + update_cfg_file_failed: + kfree(pre_buf); + kfree(buf); + kfree(file_config); + return ret; +} +#endif + +static u8 gup_load_update_file(st_fw_head *fw_head) +{ + s32 ret = 0; + + mm_segment_t fs = get_fs(); + set_fs(KERNEL_DS); + + ret = update_msg.file->f_op->llseek(update_msg.file, 0, SEEK_SET); + /* update_msg.file->f_pos = 0; */ + ret = + update_msg.file->f_op->read(update_msg.file, (char *)fw_head, FW_HEAD_LENGTH, + &update_msg.file->f_pos); + + if (ret < 0) { + GTP_ERROR("Read firmware head in update file error."); + + return FAIL; + } + /* memcpy(fw_head, buf, FW_HEAD_LENGTH); */ + update_msg.file->f_pos = FW_HEAD_LENGTH + 0; + ret = + update_msg.file->f_op->read(update_msg.file, (char *)fw_buf, FW_LENGTH, + &update_msg.file->f_pos); + + if (ret < 0) { + GTP_ERROR("Read firmware file fail."); + + return FAIL; + } + + set_fs(fs); + + return SUCCESS; +} + +static u8 gup_check_update_file(struct i2c_client *client, st_fw_head *fw_head, u8 *path) +{ + s32 ret = 0; + s32 i = 0; + s32 fw_checksum = 0; + u8 empty_test = 0; + + update_msg.file = NULL; + + if (path) { + GTP_DEBUG("Update File path:%s, %d", path, strlen(path)); + update_msg.file = filp_open(path, O_RDONLY, 0644); + + if (IS_ERR(update_msg.file)) { + GTP_ERROR("Open update file(%s) error!", path); + return FAIL; + } + } +#ifdef GTP_BOOT_FW_CONFIG_SD_UPDATE + + else { + /* Begin to search update file,the config file & firmware file must be in the same path,single or double. */ + for (i = 0; i < GUP_SEARCH_FILE_TIMES; i++) { + update_msg.file = filp_open(UPDATE_FILE_PATH_1, O_RDWR, 0444); + + if (IS_ERR(update_msg.file)) { + update_msg.file = filp_open(UPDATE_FILE_PATH_2, O_RDWR, 0664); + + if (IS_ERR(update_msg.file)) { + GTP_DEBUG("%3d:Searching file...", i); + msleep(1000); + continue; + } else { + GTP_DEBUG("Find UPDATE_FILE_PATH_2"); + break; + } + } else { + GTP_DEBUG("Find UPDATE_FILE_PATH_1"); + + i = GUP_SEARCH_FILE_TIMES; + break; + } + } + + /* make sure check config file at least once */ + if (i == GUP_SEARCH_FILE_TIMES) { + i--; + } + + for (; i < GUP_SEARCH_FILE_TIMES; i++) { + update_msg.cfg_file = filp_open(CONFIG_FILE_PATH_1, O_RDWR, 0444); + + if (IS_ERR(update_msg.cfg_file)) { + update_msg.cfg_file = filp_open(CONFIG_FILE_PATH_2, O_RDWR, 0664); + + if (IS_ERR(update_msg.cfg_file)) { + GTP_DEBUG("%3d:Searching file...", i); + msleep(1000); + continue; + } else { + GTP_DEBUG("Find CONFIG_FILE_PATH_2"); + break; + } + } else { + GTP_DEBUG("Find CONFIG_FILE_PATH_1"); + break; + } + } + + + if (!IS_ERR(update_msg.cfg_file)) { + GTP_DEBUG("Got the update config file."); + ret = gup_update_config(client); + + if (ret <= 0) { + GTP_ERROR("Update config failed."); + } + + filp_close(update_msg.cfg_file, NULL); + } + } + + + +#endif /* end BOOT_FW_CONFIG_SD_UPDATE */ + + if ((!IS_ERR(update_msg.file)) && (update_msg.file != NULL)) { + ret = gup_load_update_file(fw_head); + filp_close(update_msg.file, NULL); + + if (ret == FAIL) { + return FAIL; + } + } + + + + else /* default FW */ + { + GTP_DEBUG("Load Default FW."); + memcpy(fw_head, gtp_default_FW, FW_HEAD_LENGTH); + memcpy(fw_buf, gtp_default_FW + FW_HEAD_LENGTH, FW_LENGTH); + } + + /* check firmware legality */ + fw_checksum = 0; + + for (i = 0; i < FW_SECTION_LENGTH * 4 + FW_DSP_ISP_LENGTH + FW_DSP_LENGTH + FW_BOOT_LENGTH; + i += 2) { + u16 temp; + /* GTP_DEBUG("BUF[0]:%x", buf[0]); */ + empty_test |= fw_buf[i]; + empty_test |= fw_buf[i + 1]; + temp = (fw_buf[i] << 8) + fw_buf[i + 1]; + fw_checksum += temp; + } + + GTP_DEBUG("firmware checksum:%x empty_test:%x", (fw_checksum & 0xFFFF), empty_test); + + if ((fw_checksum & 0xFFFF) || (empty_test == 0)) { + GTP_ERROR("Illegal or empty firmware file."); + GTP_ERROR("Load Default FW."); + memcpy(fw_head, gtp_default_FW, FW_HEAD_LENGTH); + memcpy(fw_buf, gtp_default_FW + FW_HEAD_LENGTH, FW_LENGTH); + + } + + return SUCCESS; + +} + + +static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf, u16 start_addr, u16 total_length) +{ + s32 ret = 0; + u16 burn_addr = start_addr; + u16 frame_length = 0; + u16 burn_length = 0; + u8 wr_buf[PACK_SIZE + GTP_ADDR_LENGTH]; + u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH]; + u8 retry = 0; + + GTP_DEBUG("Begin burn %dk data to addr 0x%x", (total_length / 1024), start_addr); + + while (burn_length < total_length) { + GTP_DEBUG("B/T:%04d/%04d", burn_length, total_length); + frame_length = + ((total_length - burn_length) > + PACK_SIZE) ? PACK_SIZE : (total_length - burn_length); + wr_buf[0] = (u8) (burn_addr >> 8); + rd_buf[0] = wr_buf[0]; + wr_buf[1] = (u8) burn_addr; + rd_buf[1] = wr_buf[1]; + memcpy(&wr_buf[GTP_ADDR_LENGTH], &burn_buf[burn_length], frame_length); + + for (retry = 0; retry < MAX_FRAME_CHECK_TIME; retry++) { + ret = gtp_i2c_write(client, wr_buf, GTP_ADDR_LENGTH + frame_length); + + if (ret <= 0) { + GTP_ERROR("Write frame data i2c error."); + continue; + } + + ret = gtp_i2c_read(client, rd_buf, GTP_ADDR_LENGTH + frame_length); + + if (ret <= 0) { + GTP_ERROR("Read back frame data i2c error."); + continue; + } + + if (memcmp + (&wr_buf[GTP_ADDR_LENGTH], &rd_buf[GTP_ADDR_LENGTH], frame_length)) { + GTP_ERROR("Check frame data fail,not equal."); + GTP_DEBUG("write array:"); + GTP_DEBUG_ARRAY(&wr_buf[GTP_ADDR_LENGTH], frame_length); + GTP_DEBUG("read array:"); + GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], frame_length); + continue; + } else { + /* GTP_DEBUG("Check frame data success."); */ + break; + } + } + + if (retry >= MAX_FRAME_CHECK_TIME) { + GTP_ERROR("Burn frame data time out,exit."); + return FAIL; + } + + burn_length += frame_length; + burn_addr += frame_length; + } + + return SUCCESS; +} + +static u8 gup_load_section_file(u8 *buf, u16 offset, u16 length) +{ + + memcpy(buf, fw_buf + offset, length); + return SUCCESS; +} + +static u8 gup_recall_check(struct i2c_client *client, u8 *chk_src, u16 start_rd_addr, + u16 chk_length) +{ + u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH]; + s32 ret = 0; + u16 recall_addr = start_rd_addr; + u16 recall_length = 0; + u16 frame_length = 0; + + while (recall_length < chk_length) { + frame_length = + ((chk_length - recall_length) > + PACK_SIZE) ? PACK_SIZE : (chk_length - recall_length); + ret = gup_get_ic_msg(client, recall_addr, rd_buf, frame_length); + + if (ret <= 0) { + GTP_ERROR("recall i2c error,exit"); + return FAIL; + } + + if (memcmp(&rd_buf[GTP_ADDR_LENGTH], &chk_src[recall_length], frame_length)) { + GTP_ERROR("Recall frame data fail,not equal."); + GTP_DEBUG("chk_src array:"); + GTP_DEBUG_ARRAY(&chk_src[recall_length], frame_length); + GTP_DEBUG("recall array:"); + GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], frame_length); + return FAIL; + } + + recall_length += frame_length; + recall_addr += frame_length; + } + + GTP_DEBUG("Recall check %dk firmware success.", (chk_length / 1024)); + + return SUCCESS; +} + +static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section, u16 start_addr, + u8 bank_cmd) +{ + s32 ret = 0; + u8 rd_buf[5]; + + /* step1:hold ss51 & dsp */ + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]hold ss51 & dsp fail."); + return FAIL; + } + /* step2:set scramble */ + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]set scramble fail."); + return FAIL; + } + /* step3:select bank */ + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, (bank_cmd >> 4) & 0x0F); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]select bank %d fail.", (bank_cmd >> 4) & 0x0F); + return FAIL; + } + /* step4:enable accessing code */ + ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]enable accessing code fail."); + return FAIL; + } + /* step5:burn 8k fw section */ + ret = gup_burn_proc(client, fw_section, start_addr, FW_SECTION_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_section]burn fw_section fail."); + return FAIL; + } + /* step6:hold ss51 & release dsp */ + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]hold ss51 & release dsp fail."); + return FAIL; + } + /* must delay */ + msleep(1); + + /* step7:send burn cmd to move data to flash from sram */ + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, bank_cmd & 0x0f); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]send burn cmd fail."); + return FAIL; + } + + GTP_DEBUG("[burn_fw_section]Wait for the burn is complete......"); + + do { + ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]Get burn state fail"); + return FAIL; + } + + msleep(10); + /* GTP_DEBUG("[burn_fw_section]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]); */ + } + while (rd_buf[GTP_ADDR_LENGTH]); + + /* step8:select bank */ + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, (bank_cmd >> 4) & 0x0F); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]select bank %d fail.", (bank_cmd >> 4) & 0x0F); + return FAIL; + } + /* step9:enable accessing code */ + ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]enable accessing code fail."); + return FAIL; + } + /* step10:recall 8k fw section */ + ret = gup_recall_check(client, fw_section, start_addr, FW_SECTION_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_section]recall check 8k firmware fail."); + return FAIL; + } + /* step11:disable accessing code */ + ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x00); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]disable accessing code fail."); + return FAIL; + } + + return SUCCESS; +} + +static u8 gup_burn_dsp_isp(struct i2c_client *client) +{ + s32 ret = 0; + u8 *fw_dsp_isp = NULL; + u8 retry = 0; + + GTP_DEBUG("[burn_dsp_isp]Begin burn dsp isp---->>"); + + /* step1:alloc memory */ + GTP_DEBUG("[burn_dsp_isp]step1:alloc memory"); + + while (retry++ < 5) { + fw_dsp_isp = (u8 *) kzalloc(FW_DSP_ISP_LENGTH, GFP_KERNEL); + + if (fw_dsp_isp == NULL) { + continue; + } else { + GTP_INFO("[burn_dsp_isp]Alloc %dk byte memory success.", + (FW_DSP_ISP_LENGTH / 1024)); + break; + } + } + + if (retry >= 5) { + GTP_ERROR("[burn_dsp_isp]Alloc memory fail,exit."); + return FAIL; + } + /* step2:load dsp isp file data */ + GTP_DEBUG("[burn_dsp_isp]step2:load dsp isp file data"); + ret = + gup_load_section_file(fw_dsp_isp, + (4 * FW_SECTION_LENGTH + FW_DSP_LENGTH + FW_BOOT_LENGTH), + FW_DSP_ISP_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_dsp_isp]load firmware dsp_isp fail."); + goto exit_burn_dsp_isp; + } + /* step3:disable wdt,clear cache enable */ + GTP_DEBUG("[burn_dsp_isp]step3:disable wdt,clear cache enable"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00); + + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]disable wdt fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + ret = gup_set_ic_msg(client, _bRW_MISCTL__CACHE_EN, 0x00); + + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]clear cache enable fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + /* step4:hold ss51 & dsp */ + GTP_DEBUG("[burn_dsp_isp]step4:hold ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]hold ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + /* step5:set boot from sram */ + GTP_DEBUG("[burn_dsp_isp]step5:set boot from sram"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02); + + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]set boot from sram fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + /* step6:software reboot */ + GTP_DEBUG("[burn_dsp_isp]step6:software reboot"); + ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01); + + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]software reboot fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + /* step7:select bank2 */ + GTP_DEBUG("[burn_dsp_isp]step7:select bank2"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02); + + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]select bank2 fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + /* step8:enable accessing code */ + GTP_DEBUG("[burn_dsp_isp]step8:enable accessing code"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); + + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]enable accessing code fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + /* step9:burn 4k dsp_isp */ + GTP_DEBUG("[burn_dsp_isp]step9:burn 4k dsp_isp"); + ret = gup_burn_proc(client, fw_dsp_isp, 0xC000, FW_DSP_ISP_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_dsp_isp]burn dsp_isp fail."); + goto exit_burn_dsp_isp; + } + /* step10:set scramble */ + GTP_DEBUG("[burn_dsp_isp]step10:set scramble"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); + + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]set scramble fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + ret = SUCCESS; + + exit_burn_dsp_isp: + kfree(fw_dsp_isp); + return ret; +} + +static u8 gup_burn_fw_ss51(struct i2c_client *client) +{ + u8 *fw_ss51 = NULL; + u8 retry = 0; + s32 ret = 0; + + GTP_DEBUG("[burn_fw_ss51]Begin burn ss51 firmware---->>"); + + /* step1:alloc memory */ + GTP_DEBUG("[burn_fw_ss51]step1:alloc memory"); + + while (retry++ < 5) { + fw_ss51 = (u8 *) kzalloc(FW_SECTION_LENGTH, GFP_KERNEL); + + if (fw_ss51 == NULL) { + continue; + } else { + GTP_INFO("[burn_fw_ss51]Alloc %dk byte memory success.", + (FW_SECTION_LENGTH / 1024)); + break; + } + } + + if (retry >= 5) { + GTP_ERROR("[burn_fw_ss51]Alloc memory fail,exit."); + return FAIL; + } + /* step2:load ss51 firmware section 1 file data */ + GTP_DEBUG("[burn_fw_ss51]step2:load ss51 firmware section 1 file data"); + ret = gup_load_section_file(fw_ss51, 0, FW_SECTION_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 1 fail."); + goto exit_burn_fw_ss51; + } + /* step3:clear control flag */ + GTP_DEBUG("[burn_fw_ss51]step3:clear control flag"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_ss51]clear control flag fail."); + ret = FAIL; + goto exit_burn_fw_ss51; + } + /* step4:burn ss51 firmware section 1 */ + GTP_DEBUG("[burn_fw_ss51]step4:burn ss51 firmware section 1"); + ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 1 fail."); + goto exit_burn_fw_ss51; + } + /* step5:load ss51 firmware section 2 file data */ + GTP_DEBUG("[burn_fw_ss51]step5:load ss51 firmware section 2 file data"); + ret = gup_load_section_file(fw_ss51, FW_SECTION_LENGTH, FW_SECTION_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 2 fail."); + goto exit_burn_fw_ss51; + } + /* step6:burn ss51 firmware section 2 */ + GTP_DEBUG("[burn_fw_ss51]step6:burn ss51 firmware section 2"); + ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x02); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 2 fail."); + goto exit_burn_fw_ss51; + } + /* step7:load ss51 firmware section 3 file data */ + GTP_DEBUG("[burn_fw_ss51]step7:load ss51 firmware section 3 file data"); + ret = gup_load_section_file(fw_ss51, 2 * FW_SECTION_LENGTH, FW_SECTION_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 3 fail."); + goto exit_burn_fw_ss51; + } + /* step8:burn ss51 firmware section 3 */ + GTP_DEBUG("[burn_fw_ss51]step8:burn ss51 firmware section 3"); + ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x13); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 3 fail."); + goto exit_burn_fw_ss51; + } + /* step9:load ss51 firmware section 4 file data */ + GTP_DEBUG("[burn_fw_ss51]step9:load ss51 firmware section 4 file data"); + ret = gup_load_section_file(fw_ss51, 3 * FW_SECTION_LENGTH, FW_SECTION_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 4 fail."); + goto exit_burn_fw_ss51; + } + /* step10:burn ss51 firmware section 4 */ + GTP_DEBUG("[burn_fw_ss51]step10:burn ss51 firmware section 4"); + ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x14); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 4 fail."); + goto exit_burn_fw_ss51; + } + + ret = SUCCESS; + + exit_burn_fw_ss51: + kfree(fw_ss51); + return ret; +} + +static u8 gup_burn_fw_dsp(struct i2c_client *client) +{ + s32 ret = 0; + u8 *fw_dsp = NULL; + u8 retry = 0; + u8 rd_buf[5]; + + GTP_DEBUG("[burn_fw_dsp]Begin burn dsp firmware---->>"); + /* step1:alloc memory */ + GTP_DEBUG("[burn_fw_dsp]step1:alloc memory"); + + while (retry++ < 5) { + fw_dsp = (u8 *) kzalloc(FW_DSP_LENGTH, GFP_KERNEL); + + if (fw_dsp == NULL) { + continue; + } else { + GTP_INFO("[burn_fw_dsp]Alloc %dk byte memory success.", + (FW_SECTION_LENGTH / 1024)); + break; + } + } + + if (retry >= 5) { + GTP_ERROR("[burn_fw_dsp]Alloc memory fail,exit."); + return FAIL; + } + /* step2:load firmware dsp */ + GTP_DEBUG("[burn_fw_dsp]step2:load firmware dsp"); + ret = gup_load_section_file(fw_dsp, 4 * FW_SECTION_LENGTH, FW_DSP_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_dsp]load firmware dsp fail."); + goto exit_burn_fw_dsp; + } + /* step3:select bank3 */ + GTP_DEBUG("[burn_fw_dsp]step3:select bank3"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_dsp]select bank3 fail."); + ret = FAIL; + goto exit_burn_fw_dsp; + } + /* step4:hold ss51 & dsp */ + GTP_DEBUG("[burn_fw_dsp]step4:hold ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_dsp]hold ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_fw_dsp; + } + /* step5:set scramble */ + GTP_DEBUG("[burn_fw_dsp]step5:set scramble"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_dsp]set scramble fail."); + ret = FAIL; + goto exit_burn_fw_dsp; + } + /* step6:release ss51 & dsp */ + GTP_DEBUG("[burn_fw_dsp]step6:release ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x00); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_dsp]release ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_fw_dsp; + } + /* must delay */ + msleep(1); + + /* step7:burn 4k dsp firmware */ + GTP_DEBUG("[burn_fw_dsp]step7:burn 4k dsp firmware"); + ret = gup_burn_proc(client, fw_dsp, 0x9000, FW_DSP_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_dsp]burn fw_section fail."); + goto exit_burn_fw_dsp; + } + /* step8:send burn cmd to move data to flash from sram */ + GTP_DEBUG("[burn_fw_dsp]step8:send burn cmd to move data to flash from sram"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x05); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_dsp]send burn cmd fail."); + goto exit_burn_fw_dsp; + } + + GTP_DEBUG("[burn_fw_dsp]Wait for the burn is complete......"); + + do { + ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_dsp]Get burn state fail"); + goto exit_burn_fw_dsp; + } + + msleep(10); + /* GTP_DEBUG("[burn_fw_dsp]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]); */ + } + while (rd_buf[GTP_ADDR_LENGTH]); + + /* step9:recall check 4k dsp firmware */ + GTP_DEBUG("[burn_fw_dsp]step9:recall check 4k dsp firmware"); + ret = gup_recall_check(client, fw_dsp, 0x9000, FW_DSP_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_dsp]recall check 4k dsp firmware fail."); + goto exit_burn_fw_dsp; + } + + ret = SUCCESS; + + exit_burn_fw_dsp: + kfree(fw_dsp); + return ret; +} + +static u8 gup_burn_fw_boot(struct i2c_client *client) +{ + s32 ret = 0; + u8 *fw_boot = NULL; + u8 retry = 0; + u8 rd_buf[5]; + + GTP_DEBUG("[burn_fw_boot]Begin burn bootloader firmware---->>"); + + /* step1:Alloc memory */ + GTP_DEBUG("[burn_fw_boot]step1:Alloc memory"); + + while (retry++ < 5) { + fw_boot = (u8 *) kzalloc(FW_BOOT_LENGTH, GFP_KERNEL); + + if (fw_boot == NULL) { + continue; + } else { + GTP_INFO("[burn_fw_boot]Alloc %dk byte memory success.", + (FW_BOOT_LENGTH / 1024)); + break; + } + } + + if (retry >= 5) { + GTP_ERROR("[burn_fw_boot]Alloc memory fail,exit."); + return FAIL; + } + /* step2:load firmware bootloader */ + GTP_DEBUG("[burn_fw_boot]step2:load firmware bootloader"); + ret = + gup_load_section_file(fw_boot, (4 * FW_SECTION_LENGTH + FW_DSP_LENGTH), FW_BOOT_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_boot]load firmware dsp fail."); + goto exit_burn_fw_boot; + } + /* step3:hold ss51 & dsp */ + GTP_DEBUG("[burn_fw_boot]step3:hold ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]hold ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + /* step4:set scramble */ + GTP_DEBUG("[burn_fw_boot]step4:set scramble"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]set scramble fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + /* step5:release ss51 & dsp */ + GTP_DEBUG("[burn_fw_boot]step5:release ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x00); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]release ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + /* must delay */ + msleep(1); + + /* step6:burn 2k bootloader firmware */ + GTP_DEBUG("[burn_fw_boot]step6:burn 2k bootloader firmware"); + ret = gup_burn_proc(client, fw_boot, 0x9000, FW_BOOT_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_boot]burn fw_section fail."); + goto exit_burn_fw_boot; + } + /* step7:send burn cmd to move data to flash from sram */ + GTP_DEBUG("[burn_fw_boot]step7:send burn cmd to move data to flash from sram"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x06); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]send burn cmd fail."); + goto exit_burn_fw_boot; + } + + GTP_DEBUG("[burn_fw_boot]Wait for the burn is complete......"); + + do { + ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]Get burn state fail"); + goto exit_burn_fw_boot; + } + + msleep(10); + /* GTP_DEBUG("[burn_fw_boot]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]); */ + } + while (rd_buf[GTP_ADDR_LENGTH]); + + /* step8:recall check 2k bootloader firmware */ + GTP_DEBUG("[burn_fw_boot]step8:recall check 2k bootloader firmware"); + ret = gup_recall_check(client, fw_boot, 0x9000, FW_BOOT_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[burn_fw_boot]recall check 4k dsp firmware fail."); + goto exit_burn_fw_boot; + } + /* step9:enable download DSP code */ + GTP_DEBUG("[burn_fw_boot]step9:enable download DSP code "); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x99); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]enable download DSP code fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + /* step10:release ss51 & hold dsp */ + GTP_DEBUG("[burn_fw_boot]step10:release ss51 & hold dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x08); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]release ss51 & hold dsp fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + + ret = SUCCESS; + + exit_burn_fw_boot: + kfree(fw_boot); + return ret; +} + +s32 gup_update_proc(void *dir) +{ + s32 ret = 0; + u8 retry = 0; + st_fw_head fw_head; + + GTP_DEBUG("[update_proc]Begin update ......"); + + fw_buf = (u8 *) kzalloc(FW_LENGTH, GFP_KERNEL); + + show_len = 1; + total_len = 100; + + if (dir == NULL) { + msleep(3000); /* wait main thread to be completed */ + } + + if (guitar_client == NULL) /* 20121212 */ + { + guitar_client = i2c_client_point; + } + + ret = gup_check_update_file(guitar_client, &fw_head, (u8 *) dir); /* 20121212 */ + if (FAIL == ret) { + GTP_ERROR("[update_proc]check update file fail."); + goto update_fail; + } + /* gtp_reset_guitar(guitar_client, 20); //no need 20130419 */ + ret = gup_get_ic_fw_msg(guitar_client); + + if (FAIL == ret) { + GTP_ERROR("[update_proc]get ic message fail."); + goto update_fail; + } + + ret = gup_enter_upadte_judge(&fw_head); /* 20121212 */ +#ifdef GTP_FORCE_UPDATE_FW + ret = SUCCESS; /* for test */ +#endif + + if (FAIL == ret) { + GTP_ERROR("[update_proc]check update file fail."); + goto update_fail; + } + + ret = gup_enter_update_mode(guitar_client); + + if (FAIL == ret) { + GTP_ERROR("[update_proc]enter update mode fail."); + goto update_fail; + } + + while (retry++ < 5) { + show_len = 10; + total_len = 100; + ret = gup_burn_dsp_isp(guitar_client); + + if (FAIL == ret) { + GTP_ERROR("[update_proc]burn dsp isp fail."); + continue; + } + + show_len += 10; + ret = gup_burn_fw_ss51(guitar_client); + + if (FAIL == ret) { + GTP_ERROR("[update_proc]burn ss51 firmware fail."); + continue; + } + + show_len += 40; + ret = gup_burn_fw_dsp(guitar_client); + + if (FAIL == ret) { + GTP_ERROR("[update_proc]burn dsp firmware fail."); + continue; + } + + show_len += 20; + ret = gup_burn_fw_boot(guitar_client); + + if (FAIL == ret) { + GTP_ERROR("[update_proc]burn bootloader firmware fail."); + continue; + } + + show_len += 10; + GTP_INFO("[update_proc]UPDATE SUCCESS."); + break; + } + + if (retry >= 5) { + GTP_ERROR("[update_proc]retry timeout,UPDATE FAIL."); + goto update_fail; + } +/* original source code */ + /* + GTP_DEBUG("[update_proc]leave update mode."); + gup_leave_update_mode(); + */ + /* Modified by Andrew --start */ + /* GTP_DEBUG("[update_proc]reset chip."); */ + /* gtp_reset_guitar(guitar_client, 20); */ + /* Modified by Andrew --end */ + +#if 0 + msleep(100); + GTP_DEBUG("[update_proc]send config."); + ret = gtp_send_cfg(guitar_client); + + if (ret < 0) { + GTP_ERROR("[update_proc]send config fail."); + } +#endif + show_len = 100; + total_len = 100; + kfree(fw_buf); + + /* Modified by Andrew --start */ + GTP_DEBUG("[update_proc]leave update mode."); + gup_leave_update_mode(); + /* Modified by Andrew --end */ + + return SUCCESS; + + update_fail: + show_len = 200; + total_len = 100; + kfree(fw_buf); + return FAIL; +} + +u8 gup_init_update_proc(struct i2c_client *client) +{ + struct task_struct *thread = NULL; + + GTP_INFO("Ready to run update thread."); + + guitar_client = client; + GTP_INFO("Ready to run update thread"); + + thread = kthread_run(gup_update_proc, (void *)NULL, "guitar_update"); + + if (IS_ERR(thread)) { + GTP_ERROR("Failed to create update thread.\n"); + return -1; + } + + return 0; +} + + + +static u8 gup_download_fw_ss51(struct i2c_client *client) +{ + u8 *fw_ss51 = NULL; + u8 retry = 0; + s32 ret = 0; + + GTP_INFO("[download_fw_ss51]Begin burn ss51 firmware---->>"); + + /* step1:alloc memory */ + GTP_DEBUG("[download_fw_ss51]step1:alloc memory"); + + while (retry++ < 5) { + fw_ss51 = (u8 *) kzalloc(2 * FW_DOWNLOAD_LENGTH, GFP_KERNEL); + + if (fw_ss51 == NULL) { + continue; + } else { + GTP_INFO("[download_fw_ss51]Alloc %dk byte memory success.", + (FW_DOWNLOAD_LENGTH / 1024)); + break; + } + } + + if (retry >= 5) { + GTP_ERROR("[download_fw_ss51]Alloc memory fail,exit."); + return FAIL; + } + /* step2:clear control flag */ + GTP_DEBUG("[download_fw_ss51]step2:clear control flag"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00); + + if (ret <= 0) { + GTP_ERROR("[download_fw_ss51]clear control flag fail."); + ret = FAIL; + goto exit_download_fw_ss51; + } + /* step3:disable wdt,clear cache enable */ + GTP_DEBUG("[download_fw_ss51]step3:disable wdt,clear cache enable"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00); + + if (ret <= 0) { + GTP_ERROR("[download_fw_ss51]disable wdt fail."); + ret = FAIL; + goto exit_download_fw_ss51; + } + + ret = gup_set_ic_msg(client, _bRW_MISCTL__CACHE_EN, 0x00); + + if (ret <= 0) { + GTP_ERROR("[download_fw_ss51]clear cache enable fail."); + ret = FAIL; + goto exit_download_fw_ss51; + } + /* step4:hold ss51 & dsp */ + GTP_DEBUG("[download_fw_ss51]step4:hold ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + + if (ret <= 0) { + GTP_ERROR("[download_fw_ss51]hold ss51 & dsp fail."); + ret = FAIL; + goto exit_download_fw_ss51; + } + + msleep(5); + + /* step5:set boot from sram */ + GTP_DEBUG("[download_fw_ss51]step5:set boot from sram"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02); + + if (ret <= 0) { + GTP_ERROR("[download_fw_ss51]set boot from sram fail."); + ret = FAIL; + goto exit_download_fw_ss51; + } + /* step6:software reboot */ + GTP_DEBUG("[download_fw_ss51]step6:software reboot"); + ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01); + + if (ret <= 0) { + GTP_ERROR("[download_fw_ss51]software reboot fail."); + ret = FAIL; + goto exit_download_fw_ss51; + } + + msleep(5); + /* step7:load ss51 firmware section 1 file data */ + GTP_DEBUG("[download_fw_ss51]step7:load ss51 firmware section 1 file data"); + ret = gup_load_section_file(fw_ss51, 0, 2 * FW_DOWNLOAD_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[download_fw_ss51]load ss51 firmware section 1 fail."); + goto exit_download_fw_ss51; + } + + + + ret = gup_set_ic_msg(guitar_client, _bRW_MISCTL__SRAM_BANK, 0x00); + ret = gup_set_ic_msg(guitar_client, _bRW_MISCTL__MEM_CD_EN, 0x01); + + ret = i2c_write_bytes(client, 0xC000, fw_ss51, FW_DOWNLOAD_LENGTH); /* write the first bank */ + + if (ret == -1) { + GTP_ERROR("[download_fw_dsp]download FW section 1 fail."); + goto exit_download_fw_ss51; + } + + ret = gup_set_ic_msg(guitar_client, _bRW_MISCTL__SRAM_BANK, 0x01); + ret = gup_set_ic_msg(guitar_client, _bRW_MISCTL__MEM_CD_EN, 0x01); + ret = i2c_write_bytes(client, 0xC000, &fw_ss51[FW_DOWNLOAD_LENGTH], FW_DOWNLOAD_LENGTH); /* write the second bank */ + + if (ret == -1) { + GTP_ERROR("[download_fw_dsp]download FW section 2 fail."); + goto exit_download_fw_ss51; + } + + + + ret = SUCCESS; + + exit_download_fw_ss51: + kfree(fw_ss51); + return ret; +} + +static u8 gup_download_fw_dsp(struct i2c_client *client) +{ + s32 ret = 0; + u8 *fw_dsp = NULL; + u8 retry = 0; + + + GTP_INFO("[download_fw_dsp]Begin download dsp firmware---->>"); + /* step1:alloc memory */ + GTP_DEBUG("[download_fw_dsp]step1:alloc memory"); + + while (retry++ < 5) { + fw_dsp = (u8 *) kzalloc(FW_DSP_LENGTH, GFP_KERNEL); + + if (fw_dsp == NULL) { + continue; + } else { + GTP_INFO("[download_fw_dsp]Alloc %dk byte memory success.", + (FW_SECTION_LENGTH / 1024)); + break; + } + } + + if (retry >= 5) { + GTP_ERROR("[download_fw_dsp]Alloc memory fail,exit."); + return FAIL; + } + /* step2:load firmware dsp */ + GTP_DEBUG("[download_fw_dsp]step2:load firmware dsp"); + ret = gup_load_section_file(fw_dsp, 4 * FW_SECTION_LENGTH, FW_DSP_LENGTH); + + if (FAIL == ret) { + GTP_ERROR("[download_fw_dsp]load firmware dsp fail."); + goto exit_download_fw_dsp; + } + + /* step3:select bank2 */ + GTP_DEBUG("[download_fw_dsp]step3:select bank2"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02); + + if (ret <= 0) { + GTP_ERROR("[download_fw_dsp]select bank2 fail."); + ret = FAIL; + goto exit_download_fw_dsp; + } + /* step4:enable accessing code */ + ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); + + if (ret <= 0) { + GTP_ERROR("[download_fw_dsp]enable accessing code fail."); + ret = FAIL; + goto exit_download_fw_dsp; + } + + ret = i2c_write_bytes(client, 0xC000, fw_dsp, FW_DSP_LENGTH); /* write the second bank */ + + ret = SUCCESS; + + exit_download_fw_dsp: + kfree(fw_dsp); + return ret; +} + + + +s32 gup_fw_download_proc(void *dir) +{ + u8 buf[3]; + s32 ret = 0; + u8 retry = 0; + st_fw_head fw_head; + + GTP_INFO("[fw_download_proc]Begin fw download ......"); + fw_buf = (u8 *) kzalloc(FW_LENGTH, GFP_KERNEL); + + if (dir == NULL) { + msleep(2000); /* wait main thread to be completed */ + } + + if (guitar_client == NULL) /* 20121212 */ + { + guitar_client = i2c_client_point; + } + + download: + ret = gup_check_update_file(guitar_client, &fw_head, (u8 *) dir); + + if (FAIL == ret) { + GTP_ERROR("[fw_download_proc]check update file fail."); + goto download_fail; + } + /* gtp_reset_guitar(guitar_client, 20); //no need 20130419 */ + ret = gup_get_ic_fw_msg(guitar_client); + + if (FAIL == ret) { + GTP_ERROR("[fw_download_proc]get ic message fail."); + goto download_fail; + } + + ret = gup_enter_upadte_judge(&fw_head); /* 20121212 */ + + if (FAIL == ret) { + GTP_ERROR("[update_proc]Check *.bin file fail."); + goto download_fail; + } + + ret = gup_enter_update_mode(guitar_client); /* 20121212 */ + + if (FAIL == ret) { + GTP_ERROR("[fw_download_proc]check update file fail."); + goto download_fail; + } + + while (retry++ < 5) { + + ret = gup_download_fw_ss51(guitar_client); + + if (FAIL == ret) { + GTP_ERROR("[fw_download_proc]burn ss51 firmware fail."); + continue; + } + + ret = gup_download_fw_dsp(guitar_client); + + if (FAIL == ret) { + GTP_ERROR("[fw_download_proc]burn dsp firmware fail."); + continue; + } + + + GTP_INFO("[fw_download_proc]UPDATE SUCCESS."); + break; + } + + if (retry >= 5) { + GTP_ERROR("[fw_download_proc]retry timeout,UPDATE FAIL."); + goto download_fail; + } + /* -- set scramble */ + GTP_DEBUG("[download_fw_dsp]step5:set scramble"); + ret = gup_set_ic_msg(guitar_client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); + + if (ret <= 0) { + GTP_ERROR("[download_fw_dsp]set scramble fail."); + ret = FAIL; + goto download_fail; + } + /* -- clear control flag */ + GTP_DEBUG("[burn_fw_ss51]step3:clear control flag"); + ret = gup_set_ic_msg(guitar_client, _rRW_MISCTL__BOOT_CTL_, 0xAA); + + if (ret <= 0) { + GTP_ERROR("[burn_fw_ss51]clear control flag fail."); + ret = FAIL; + goto download_fail; + } + /* -- hold ss51 & dsp */ + GTP_DEBUG("[download_fw_dsp]step4:hold ss51 & dsp"); + ret = gup_set_ic_msg(guitar_client, _rRW_MISCTL__SWRST_B0_, 0x00); + + if (ret <= 0) { + GTP_ERROR("[download_fw_dsp]hold ss51 & dsp fail."); + ret = FAIL; + goto download_fail; + } + + msleep(5); + + gup_get_ic_msg(guitar_client, GUP_REG_FW_MSG, buf, 1); + GTP_DEBUG("[fw_download_proc] BOOT Status %02X\n", buf[2]); + + if (buf[2] != 0xBE) { + goto download; + } +#if 0 + GTP_DEBUG("[fw_download_proc]send config."); + ret = gtp_send_cfg(guitar_client); + + if (ret < 0) { + GTP_ERROR("[fw_download_proc]send config fail."); + } +#endif + + gtp_int_sync(); + + kfree(fw_buf); + return SUCCESS; + download_fail: + kfree(fw_buf); + return FAIL; +} + + +u8 gup_init_fw_proc(struct i2c_client *client) +{ + struct task_struct *thread = NULL; + + GTP_INFO("Ready to run fw download thread."); + + guitar_client = client; + GTP_INFO("Ready to run fw download thread."); + + thread = kthread_run(gup_fw_download_proc, (void *)NULL, "guitar_fw_download"); + + if (IS_ERR(thread)) { + GTP_ERROR("Failed to create fw download thread.\n"); + return -1; + } + + return 0; +} |
