| /***************************************************************************** |
| * Copyright 2012 Broadcom Corporation. All rights reserved. |
| * |
| * Unless you and Broadcom execute a separate written software license |
| * agreement governing use of this software, this software is licensed to you |
| * under the terms of the GNU General Public License version 2, available at |
| * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). |
| * |
| * Notwithstanding the above, under no circumstances may you combine this |
| * software in any way with any other Broadcom software provided under a |
| * license other than the GPL, without Broadcom's express prior written |
| * consent. |
| *****************************************************************************/ |
| |
| /* ---- Include Files ---------------------------------------------------- */ |
| |
| |
| #include <linux/module.h> |
| #include <linux/input.h> |
| #include <linux/interrupt.h> |
| #include <linux/hrtimer.h> |
| #include <linux/platform_device.h> |
| #include <linux/i2c.h> |
| #include <linux/delay.h> |
| #include <linux/i2c/ft5306.h> |
| #include <linux/io.h> |
| #include <linux/gpio.h> |
| #include <linux/delay.h> |
| #include <linux/slab.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/regulator/driver.h> |
| #include <linux/notifier.h> |
| #include <linux/i2c/ft6x06_ex_fun.h> |
| #include <linux/wakelock.h> |
| #include <linux/vmalloc.h> |
| #include <linux/leds.h> |
| #include <linux/of_gpio.h> |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| #include <linux/earlysuspend.h> |
| #endif |
| |
| #define GPIO_TO_IRQ gpio_to_irq |
| |
| static int ts_gpio_irq_pin=0; |
| #if (TP_CNTRL_PIN_TYPE == TP_CNTRL_PIN_RESET) |
| static int ts_gpio_reset_pin=0; |
| #endif |
| static int ts_gpio_wakeup_pin=0; |
| static int ts_x_max_value=0; |
| static int ts_y_max_value=0; |
| static const char *tp_power; |
| static const char *vkey_scope; |
| static const char *fw_header; |
| static bool have_vkey = false; |
| static char firmware_str[50]; |
| |
| /*******************************/ |
| typedef unsigned char FTS_BYTE; |
| typedef unsigned short FTS_WORD; |
| typedef unsigned int FTS_DWRD; |
| typedef signed int FTS_BOOL; |
| |
| #define FTS_MAX_FINGER 5 |
| |
| #define FTS_NULL 0x0 |
| #define FTS_TRUE 0x01 |
| #define FTS_FALSE 0x0 |
| |
| #define DEVIDE_MODE_REG 0x00 |
| #define READ_ROW_ADDR 0x01 //rawdata row |
| #define RAWDATA_BEGIN_ADDR 0x10 //rawdata addr |
| |
| #define DEVICE_PM_REG 0xA5 |
| #define DEVICE_PM_ACTIVE 0x00 |
| #define DEVICE_PM_MONITOR 0x01 |
| #define DEVICE_PM_HIBERNATE 0x03 |
| |
| #define HAWAII_GARNET_FT5X06_VENDOR_ID 0x87 |
| #define G5_A18_FT5X06_VENDOR_ID 0x79 |
| #define G5_A21_FT5316_VENDOR_ID 0x98 |
| #define KTOUCH_W68_FT6X06_VENDOR_ID 0x5a |
| #define KTOUCH_5606_FT6X06_VENDOR_ID 0x5a |
| #define KTOUCH_W81_FT6X06_VENDOR_ID 0x51 |
| #define TCL_UP823_FT6206_VENDOR_ID 0x11 |
| |
| #define PROTOCOL_LEN 33 |
| |
| #define POINTER_CHECK(p) if((p)==FTS_NULL){return FTS_FALSE;} |
| |
| /* |
| Error status codes: |
| */ |
| #define CTPM_NOERROR (0x01 << 0) |
| #define CTPM_ERR_PARAMETER (0x01 << 1) |
| #define CTPM_ERR_PROTOCOL (0x01 << 2) |
| #define CTPM_ERR_ECC (0x01 << 3) |
| #define CTPM_ERR_MODE (0x01 << 4) |
| #define CTPM_ERR_I2C (0x01 << 5) |
| #define CTPM_ERR_SCAN (0x01 << 6) |
| |
| /*support 5 points*/ |
| #define CTPM_STD_POINTS_MAX 0x05 |
| |
| /*register for checking ----------->*/ |
| /* |
| Work mode: 0x01 |
| Factory mode: 0x03 |
| */ |
| #define REG_RUN_MODE (0x27+0x80) |
| |
| /* |
| the Firmware version number should be up from 0x10 |
| */ |
| #define REG_FW_VER (0x26+0x80) |
| |
| /* |
| the frequence should be from 0x04 to 0x08 |
| */ |
| #define REG_SCAN_FREQUENCE (0x08+0x80) |
| /*<-------------register for checking*/ |
| |
| |
| /*event difinition*/ |
| #define TC_PutDown 0x00 |
| #define TC_PutUp 0x01 |
| #define TC_Contact 0x02 |
| #define TC_NoEvent 0x03 |
| |
| /*the information of one touch point */ |
| typedef struct |
| { |
| /*x coordinate*/ |
| FTS_WORD w_tp_x, last_tp_x; |
| /*y coordinate*/ |
| FTS_WORD w_tp_y, last_tp_y; |
| /*point id: start from 0*/ |
| FTS_BYTE bt_tp_id; |
| /*ref. event difinition */ |
| FTS_BYTE bt_tp_property; |
| /*the strength of the press*/ |
| FTS_WORD w_tp_strenth; |
| FTS_WORD w_tp_area; |
| }ST_TOUCH_POINT, *PST_TOUCH_POINT; |
| |
| typedef enum |
| { |
| TYPE_Z00M_IN, |
| TYPE_Z00M_OUT, |
| TYPE_INVALIDE |
| }E_GESTURE_TYPE; |
| |
| /*the information of one touch */ |
| typedef struct |
| { |
| /*the number of touch points*/ |
| FTS_BYTE bt_tp_num; |
| /*touch gesture*/ |
| E_GESTURE_TYPE bt_gesture; |
| /*point to a list which stored 1 to 5 touch points information*/ |
| ST_TOUCH_POINT *pst_point_info; |
| }ST_TOUCH_INFO, *PST_TOUCH_INFO; |
| |
| #define TP_MIN_GAP 3 |
| static int pressure_support; |
| static int auto_update_fw; |
| static int fw_header_needed; |
| static BLOCKING_NOTIFIER_HEAD(touch_key_notifier); |
| static int focaltec_fts_ctpm_fw_upgrade(struct i2c_client *client, u8 *pbt_buf, |
| u32 dw_lenth); |
| |
| static struct workqueue_struct *synaptics_wq; |
| static struct delayed_work ft6x06_firmware_update; |
| static struct wake_lock update_wake_lock; |
| |
| |
| static struct i2c_client *ft5306_i2c_client; |
| static ST_TOUCH_INFO ft5306_touch_info; |
| static ST_TOUCH_POINT ft5306_touch_point[FTS_MAX_FINGER]; |
| static int min_gap = TP_MIN_GAP; |
| enum ft520x_ts_regs { |
| FT520X_REG_THGROUP = 0x80, |
| FT520X_REG_THPEAK = 0x81, |
| FT520X_REG_THCAL = 0x82, |
| FT520X_REG_THWATER = 0x83, |
| FT520X_REG_THTEMP = 0x84, |
| FT520X_REG_THDIFF = 0x85, |
| FT520X_REG_CTRL = 0x86, |
| FT520X_REG_TIMEENTERMONITOR = 0x87, |
| FT520X_REG_PERIODACTIVE = 0x88, |
| FT520X_REG_PERIODMONITOR = 0x89, |
| FT520X_REG_HEIGHT_B = 0x8a, |
| FT520X_REG_MAX_FRAME = 0x8b, |
| FT520X_REG_DIST_MOVE = 0x8c, |
| FT520X_REG_DIST_POINT = 0x8d, |
| FT520X_REG_FEG_FRAME = 0x8e, |
| FT520X_REG_SINGLE_CLICK_OFFSET = 0x8f, |
| FT520X_REG_DOUBLE_CLICK_TIME_MIN = 0x90, |
| FT520X_REG_SINGLE_CLICK_TIME = 0x91, |
| FT520X_REG_LEFT_RIGHT_OFFSET = 0x92, |
| FT520X_REG_UP_DOWN_OFFSET = 0x93, |
| FT520X_REG_DISTANCE_LEFT_RIGHT = 0x94, |
| FT520X_REG_DISTANCE_UP_DOWN = 0x95, |
| FT520X_REG_ZOOM_DIS_SQR = 0x96, |
| FT520X_REG_RADIAN_VALUE =0x97, |
| FT520X_REG_MAX_X_HIGH = 0x98, |
| FT5X0X_REG_MAX_X_LOW = 0x99, |
| FT520X_REG_MAX_Y_HIGH = 0x9a, |
| FT520X_REG_MAX_Y_LOW = 0x9b, |
| FT520X_REG_K_X_HIGH = 0x9c, |
| FT520X_REG_K_X_LOW = 0x9d, |
| FT520X_REG_K_Y_HIGH = 0x9e, |
| FT520X_REG_K_Y_LOW = 0x9f, |
| FT520X_REG_AUTO_CLB_MODE = 0xa0, |
| FT520X_REG_LIB_VERSION_H = 0xa1, |
| FT520X_REG_LIB_VERSION_L = 0xa2, |
| FT520X_REG_CIPHER = 0xa3, |
| FT520X_REG_MODE = 0xa4, |
| FT520X_REG_PMODE = 0xa5, /* Power Consume Mode */ |
| FT520X_REG_FIRMID = 0xa6, |
| FT520X_REG_STATE = 0xa7, |
| FT520X_REG_FT5201ID = 0xa8, |
| FT520X_REG_ERR = 0xa9, |
| FT520X_REG_CLB = 0xaa, |
| }; |
| |
| /*FT5X0X_REG_PMODE*/ |
| #define PMODE_ACTIVE 0x00 |
| #define PMODE_MONITOR 0x01 |
| #define PMODE_STANDBY 0x02 |
| #define PMODE_HIBERNATE 0x03 |
| |
| static int register_touch_key_notifier(struct notifier_block *n) |
| { |
| return blocking_notifier_chain_register(&touch_key_notifier, n); |
| } |
| |
| static int unregister_touch_key_notifier(struct notifier_block *n) |
| { |
| return blocking_notifier_chain_unregister(&touch_key_notifier, n); |
| } |
| |
| static int ft520x_i2c_rxdata(char *rxdata, int length) |
| { |
| int ret; |
| |
| struct i2c_msg msgs[] = { |
| { |
| .addr = ft5306_i2c_client->addr, |
| .flags = 0, |
| .len = 1, |
| .buf = rxdata, |
| }, |
| { |
| .addr = ft5306_i2c_client->addr, |
| .flags = I2C_M_RD, |
| .len = length, |
| .buf = rxdata, |
| }, |
| }; |
| |
| ret = i2c_transfer(ft5306_i2c_client->adapter, msgs, 2); |
| if (ret < 0) |
| printk(KERN_ERR "msg %s i2c read error: %d\n", __func__, ret); |
| |
| return ret; |
| } |
| |
| static int ft520x_read_reg(u8 addr, u8 *pdata) |
| { |
| int ret; |
| u8 buf[2] = {0}; |
| |
| buf[0] = addr; |
| struct i2c_msg msgs[] = { |
| { |
| .addr = ft5306_i2c_client->addr, |
| .flags = 0, |
| .len = 1, |
| .buf = buf, |
| }, |
| { |
| .addr = ft5306_i2c_client->addr, |
| .flags = I2C_M_RD, |
| .len = 1, |
| .buf = buf, |
| }, |
| }; |
| |
| ret = i2c_transfer(ft5306_i2c_client->adapter, msgs, 2); |
| if (ret < 0) |
| printk(KERN_ERR "msg %s i2c read error: %d\n", __func__, ret); |
| |
| *pdata = buf[0]; |
| return ret; |
| |
| } |
| |
| static int ft520x_i2c_txdata(char *txdata, int length) |
| { |
| int ret; |
| |
| struct i2c_msg msg[] = { |
| { |
| .addr = ft5306_i2c_client->addr, |
| .flags = 0, |
| .len = length, |
| .buf = txdata, |
| }, |
| }; |
| |
| ret = i2c_transfer(ft5306_i2c_client->adapter, msg, 1); |
| if (ret < 0) |
| printk(KERN_ERR "%s i2c write error: %d\n", __func__, ret); |
| |
| return ret; |
| } |
| |
| static int ft520x_write_reg(u8 addr, u8 para) |
| { |
| u8 buf[2]; |
| int ret = -1; |
| |
| buf[0] = addr; |
| buf[1] = para; |
| ret = ft520x_i2c_txdata(buf, 2); |
| if (ret < 0) { |
| printk(KERN_ERR "write reg failed! %#x ret:%d", buf[0], ret); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static unsigned char ft520x_read_fw_ver(void) |
| { |
| unsigned char ver=0xff; |
| ft520x_read_reg(FT520X_REG_FIRMID, &ver); |
| return ver; |
| } |
| |
| static int ft520x_shutdown(void) |
| { |
| int ret; |
| ret = ft520x_write_reg(FT520X_REG_PMODE, PMODE_HIBERNATE); |
| if (ret != 0) |
| printk(" --- ft520x_shutdown -- error!\r\n"); |
| return ret; |
| } |
| |
| #if 0 |
| static int ft520x_turnon() |
| { |
| int ret; |
| ret = ft520x_write_reg(FT520X_REG_PMODE, PMODE_MONITOR); |
| if (ret != 0) |
| printk(KERN_INFO" --- ft520x_turnon -- error!\r\n"); |
| return ret; |
| } |
| #endif |
| |
| #define CONFIG_SUPPORT_FTS_CTP_UPG |
| |
| #ifdef CONFIG_SUPPORT_FTS_CTP_UPG |
| |
| #define FT520X_FIREWARE_VERSION 0x12 |
| |
| typedef enum |
| { |
| ERR_OK, |
| ERR_MODE, |
| ERR_READID, |
| ERR_ERASE, |
| ERR_STATUS, |
| ERR_ECC, |
| ERR_DL_ERASE_FAIL, |
| ERR_DL_PROGRAM_FAIL, |
| ERR_DL_VERIFY_FAIL |
| }E_UPGRADE_ERR_TYPE; |
| |
| #define I2C_CTPM_ADDRESS 0x7E |
| |
| |
| void delay_qt_ms(unsigned long w_ms) |
| { |
| /*unsigned long i; |
| unsigned long j; |
| |
| for (i = 0; i < w_ms; i++) |
| { |
| for (j = 0; j < 1000; j++) |
| { |
| udelay(1); |
| } |
| }*/ |
| msleep(w_ms); |
| } |
| |
| FTS_BOOL i2c_read_interface(FTS_BYTE bt_ctpm_addr, FTS_BYTE* pbt_buf, FTS_DWRD dw_lenth) |
| { |
| int ret; |
| |
| ret=i2c_master_recv(ft5306_i2c_client, pbt_buf, dw_lenth); |
| |
| if (ret <= 0) { |
| printk("[FT520X]i2c_read_interface error\n"); |
| return FTS_FALSE; |
| } |
| |
| return FTS_TRUE; |
| } |
| |
| FTS_BOOL i2c_write_interface(FTS_BYTE bt_ctpm_addr, FTS_BYTE* pbt_buf, FTS_DWRD dw_lenth) |
| { |
| int ret; |
| ret=i2c_master_send(ft5306_i2c_client, pbt_buf, dw_lenth); |
| if (ret <= 0) { |
| printk("[FT520X]i2c_write_interface error line = %d, ret = %d\n", __LINE__, ret); |
| return FTS_FALSE; |
| } |
| |
| return FTS_TRUE; |
| } |
| |
| FTS_BOOL cmd_write(FTS_BYTE btcmd,FTS_BYTE btPara1,FTS_BYTE btPara2,FTS_BYTE btPara3,FTS_BYTE num) |
| { |
| FTS_BYTE write_cmd[4] = {0}; |
| |
| write_cmd[0] = btcmd; |
| write_cmd[1] = btPara1; |
| write_cmd[2] = btPara2; |
| write_cmd[3] = btPara3; |
| return i2c_write_interface(I2C_CTPM_ADDRESS, write_cmd, num); |
| } |
| |
| FTS_BOOL byte_write(FTS_BYTE* pbt_buf, FTS_DWRD dw_len) |
| { |
| return i2c_write_interface(I2C_CTPM_ADDRESS, pbt_buf, dw_len); |
| } |
| |
| FTS_BOOL byte_read(FTS_BYTE* pbt_buf, FTS_BYTE bt_len) |
| { |
| return i2c_read_interface(I2C_CTPM_ADDRESS, pbt_buf, bt_len); |
| } |
| |
| #define FTS_PACKET_LENGTH 128 |
| |
| static unsigned char CTPM_FW[]= |
| { |
| #if defined(CONFIG_TOUCHSCREEN_HAWAII_GARNET_FT5306_FW) |
| #include "45HD_FT5306_U82_LCID0x87_ver0x10_20130320_app.i" |
| #elif defined(CONFIG_TOUCHSCREEN_JAVA_AMETHYST_FT5336_FW) |
| #include "Java_amethyst_050914_FT5336_20130910_V10_app.i" |
| #else |
| 0 |
| #endif |
| }; |
| |
| FTS_DWRD fts_ctpm_auto_clb(void) |
| { |
| FTS_BYTE uc_temp; |
| FTS_BYTE i ; |
| printk(KERN_INFO"[FTS] start auto CLB.\n"); |
| delay_qt_ms(200); |
| ft520x_write_reg(0, 0x40); |
| /*make sure already enter factory mode*/ |
| delay_qt_ms(100); |
| /*write command to start calibration */ |
| ft520x_write_reg(2, 0x4); |
| delay_qt_ms(300); |
| for (i = 0; i < 100; i++) { |
| ft520x_read_reg(0, &uc_temp); |
| if (((uc_temp&0x70)>>4) == 0x0) |
| break; |
| delay_qt_ms(200); |
| printk(KERN_INFO"[FTS] waiting calibration %d\n", i); |
| } |
| printk(KERN_INFO"[FTS] calibration OK.\n"); |
| delay_qt_ms(300); |
| /*goto factory mode*/ |
| ft520x_write_reg(0, 0x40); |
| /*make sure already enter factory mode*/ |
| delay_qt_ms(100); |
| /*store CLB result*/ |
| ft520x_write_reg(2, 0x5); |
| delay_qt_ms(300); |
| /*return to normal mode*/ |
| ft520x_write_reg(0, 0x0); |
| delay_qt_ms(300); |
| printk(KERN_INFO"[FTS] store CLB result OK.\n"); |
| return 0; |
| } |
| |
| |
| E_UPGRADE_ERR_TYPE fts_ctpm_fw_upgrade(FTS_BYTE* pbt_buf, FTS_DWRD dw_lenth) |
| { |
| FTS_BYTE reg_val[2] = {0}; |
| FTS_DWRD i = 0; |
| |
| FTS_DWRD packet_number; |
| FTS_DWRD j; |
| FTS_DWRD temp; |
| FTS_DWRD lenght; |
| FTS_BYTE packet_buf[FTS_PACKET_LENGTH + 6]; |
| FTS_BYTE auc_i2c_write_buf[10]; |
| FTS_BYTE bt_ecc; |
| int i_ret; |
| |
| /*write 0xaa to register 0xfc*/ |
| ft520x_write_reg(0xfc, 0xaa); |
| delay_qt_ms(50); |
| ft520x_write_reg(0xfc, 0x55); |
| printk("[FT520X] Step 1: Reset CTPM test\n"); |
| |
| delay_qt_ms(30); |
| |
| auc_i2c_write_buf[0] = 0x55; |
| auc_i2c_write_buf[1] = 0xaa; |
| do { |
| i ++; |
| i_ret = ft520x_i2c_txdata(auc_i2c_write_buf, 2); |
| delay_qt_ms(5); |
| } while (i_ret <= 0 && i < 5); |
| |
| cmd_write(0x90,0x00,0x00,0x00,4); |
| byte_read(reg_val,2); |
| if (reg_val[0] == 0x79 && reg_val[1] == 0x3) |
| printk(KERN_INFO"[FT520X] CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n", |
| reg_val[0], reg_val[1]); |
| else { |
| printk(KERN_INFO"[FT520X] Step 3: readid error\n"); |
| return ERR_READID; |
| } |
| cmd_write(0x61, 0x00, 0x00, 0x00, 1); |
| delay_qt_ms(1500); |
| bt_ecc = 0; |
| dw_lenth = dw_lenth - 8; |
| packet_number = (dw_lenth) / FTS_PACKET_LENGTH; |
| packet_buf[0] = 0xbf; |
| packet_buf[1] = 0x00; |
| for (j = 0; j < packet_number; j++) { |
| temp = j * FTS_PACKET_LENGTH; |
| packet_buf[2] = (FTS_BYTE)(temp>>8); |
| packet_buf[3] = (FTS_BYTE)temp; |
| lenght = FTS_PACKET_LENGTH; |
| packet_buf[4] = (FTS_BYTE)(lenght>>8); |
| packet_buf[5] = (FTS_BYTE)lenght; |
| for (i = 0; i < FTS_PACKET_LENGTH; i++) { |
| packet_buf[6+i] = pbt_buf[j*FTS_PACKET_LENGTH + i]; |
| bt_ecc ^= packet_buf[6+i]; |
| } |
| |
| byte_write(&packet_buf[0],FTS_PACKET_LENGTH + 6); |
| delay_qt_ms(FTS_PACKET_LENGTH/6 + 1); |
| if ((j * FTS_PACKET_LENGTH % 1024) == 0) |
| printk(KERN_INFO"[FT520X] upgrade the 0x%x th byte.\n", |
| ((unsigned int)j) * FTS_PACKET_LENGTH); |
| } |
| if ((dw_lenth) % FTS_PACKET_LENGTH > 0) { |
| temp = packet_number * FTS_PACKET_LENGTH; |
| packet_buf[2] = (FTS_BYTE)(temp>>8); |
| packet_buf[3] = (FTS_BYTE)temp; |
| |
| temp = (dw_lenth) % FTS_PACKET_LENGTH; |
| packet_buf[4] = (FTS_BYTE)(temp>>8); |
| packet_buf[5] = (FTS_BYTE)temp; |
| |
| for (i = 0; i < temp; i++) { |
| packet_buf[6+i] = pbt_buf[packet_number*FTS_PACKET_LENGTH + i]; |
| bt_ecc ^= packet_buf[6+i]; |
| } |
| byte_write(&packet_buf[0], temp+6); |
| delay_qt_ms(20); |
| } |
| |
| for (i = 0; i < 6; i++) { |
| temp = 0x6ffa + i; |
| packet_buf[2] = (FTS_BYTE)(temp>>8); |
| packet_buf[3] = (FTS_BYTE)temp; |
| temp =1; |
| packet_buf[4] = (FTS_BYTE)(temp>>8); |
| packet_buf[5] = (FTS_BYTE)temp; |
| packet_buf[6] = pbt_buf[dw_lenth + i]; |
| bt_ecc ^= packet_buf[6]; |
| |
| byte_write(&packet_buf[0], 7); |
| delay_qt_ms(20); |
| } |
| /*send the opration head*/ |
| cmd_write(0xcc, 0x00, 0x00, 0x00, 1); |
| byte_read(reg_val,1); |
| if (reg_val[0] != bt_ecc) { |
| printk(KERN_INFO"[FT520X] ecc error\n"); |
| return ERR_ECC; |
| } |
| cmd_write(0x07, 0x00, 0x00, 0x00, 1); |
| delay_qt_ms(300); |
| return ERR_OK; |
| } |
| |
| int focaltec_fts_ctpm_fw_upgrade_with_i_file(void) |
| { |
| FTS_BYTE* pbt_buf = FTS_NULL; |
| int i_ret; |
| |
| //=========FW upgrade========================*/ |
| pbt_buf = CTPM_FW; |
| /*call the upgrade function*/ |
| i_ret = focaltec_fts_ctpm_fw_upgrade(ft5306_i2c_client, pbt_buf, sizeof(CTPM_FW)); |
| if (i_ret != 0) { |
| printk(KERN_ERR "FT6x06 upgrade firmware failed! \r\n"); |
| return i_ret; |
| } else { |
| if (NEED_CALIBRATION) |
| fts_ctpm_auto_clb(); |
| printk(KERN_INFO "[FT6x06] upgrade successfully.\n"); |
| } |
| return i_ret; |
| } |
| |
| unsigned char fts_ctpm_get_upg_ver(void) |
| { |
| unsigned int ui_sz; |
| ui_sz = sizeof(CTPM_FW); |
| if (ui_sz > 2) |
| return CTPM_FW[ui_sz - 2]; |
| else |
| return 0xff; |
| } |
| |
| unsigned char fts_ctpm_get_vendor_id(void) |
| { |
| u8 vendor_id = 0; |
| ft520x_read_reg(0xA8, &vendor_id); |
| printk("tp vendor id: %x", vendor_id); |
| return vendor_id; |
| } |
| unsigned char fts_ctpm_get_chip_id(void) |
| { |
| u8 chip_id = 0; |
| ft520x_read_reg(0xA3, &chip_id); |
| printk("Focaltech tp chip id: %x", chip_id); |
| return chip_id; |
| } |
| |
| #endif |
| |
| int focaltec_fts_upgrade_firmware(void) |
| { |
| unsigned char ver = 0; |
| if (ft520x_read_fw_ver() < fts_ctpm_get_upg_ver()) { |
| ver = ft520x_read_fw_ver(); |
| printk(KERN_INFO "ft6x06 upgrading fw... before upgrd ver: %x\n", ver); |
| focaltec_fts_ctpm_fw_upgrade_with_i_file(); |
| ver = ft520x_read_fw_ver(); |
| printk(KERN_INFO "ft6x06 upgrading fw... after upgrd ver: %x\n", ver); |
| } else |
| printk(KERN_INFO "No need to upgrade firmware\n"); |
| |
| return 1; |
| } |
| |
| |
| FTS_BYTE bt_parser_std(FTS_BYTE* pbt_buf, FTS_BYTE bt_len, ST_TOUCH_INFO* pst_touch_info) |
| { |
| FTS_WORD low_byte = 0; |
| FTS_WORD high_byte = 0; |
| FTS_BYTE point_num = 0; |
| FTS_BYTE i = 0; |
| |
| /*check the pointer*/ |
| |
| /*check the length of the protocol data*/ |
| if(bt_len < PROTOCOL_LEN) { |
| return CTPM_ERR_PARAMETER; |
| } |
| pst_touch_info->bt_tp_num= 0; |
| |
| /* Device Mode[2:0] == 0 :Normal operating Mode*/ |
| if ((pbt_buf[0] & 0x70) != 0) { |
| printk(KERN_ERR "[tp] mode: %x", pbt_buf[0]); |
| return CTPM_ERR_PROTOCOL; |
| } |
| |
| /*get the Gesture ID*/ |
| pst_touch_info->bt_gesture = pbt_buf[1]; |
| |
| /*get the number of the touch points*/ |
| point_num = pbt_buf[2] & 0x0f; |
| if(point_num == 0 || point_num > CTPM_STD_POINTS_MAX) { |
| return CTPM_ERR_PROTOCOL; |
| } |
| |
| /*remove the touch point information into pst_touch_info.*/ |
| for(i = 0; i < point_num; i++) { |
| /*get the X coordinate, 2 bytes*/ |
| pst_touch_info->pst_point_info[i].bt_tp_property = pbt_buf[3+6*i] >> 6; |
| |
| high_byte = pbt_buf[3+6*i]; |
| high_byte <<= 8; |
| high_byte &= 0x0f00; |
| low_byte = pbt_buf[3+6*i + 1]; |
| pst_touch_info->pst_point_info[i].w_tp_x = high_byte |low_byte; |
| |
| /*get the Y coordinate, 2 bytes*/ |
| pst_touch_info->pst_point_info[i].bt_tp_id = pbt_buf[3+6*i+2] >> 4; |
| |
| high_byte = pbt_buf[3+6*i+2]; |
| high_byte <<= 8; |
| high_byte &= 0x0f00; |
| low_byte = pbt_buf[3+6*i+3]; |
| pst_touch_info->pst_point_info[i].w_tp_y = high_byte |low_byte; |
| if (pressure_support) { |
| pst_touch_info->pst_point_info[i].w_tp_strenth=pbt_buf[7+6*i]; |
| pst_touch_info->pst_point_info[i].w_tp_area=pbt_buf[8+6*i]; |
| } |
| |
| pst_touch_info->bt_tp_num++; |
| } |
| |
| return CTPM_NOERROR; |
| } |
| |
| /* |
| [function]: |
| get all the information of one touch. |
| [parameters]: |
| pst_touch_info[out] :stored all the information of one touch; |
| [return]: |
| CTPM_NOERROR :success; |
| CTPM_ERR_I2C :io fail; |
| CTPM_ERR_PROTOCOL :protocol data error; |
| CTPM_ERR_ECC :ecc error. |
| */ |
| void DumpFtsRegContext(FTS_BYTE *buf, int size) |
| { |
| int i; |
| |
| for (i = 0; i < size; i += 3) |
| printk(KERN_INFO "Addr: %x: %x, %x, %x\n", i, *(buf+i), |
| *(buf+i+1), *(buf+i+2)); |
| } |
| |
| FTS_BYTE fts_ctpm_get_touch_info(struct synaptics_rmi4 *ts, ST_TOUCH_INFO* pst_touch_info) |
| { |
| FTS_BYTE *p_data_buf; |
| FTS_BYTE data_buf[33] = {0}; |
| |
| POINTER_CHECK(pst_touch_info); |
| POINTER_CHECK(pst_touch_info->pst_point_info); |
| |
| p_data_buf = &data_buf[0]; |
| |
| /*Sent device to active*/ |
| ft5306_i2c_client = ts->client; |
| |
| memset(data_buf, 0x00, PROTOCOL_LEN); |
| pst_touch_info->bt_tp_num = 0; |
| |
| /*get the touch point information*/ |
| /*if(!i2c_read_interface(0x00, p_data_buf, PROTOCOL_LEN)) { |
| return CTPM_ERR_I2C; |
| }*/ |
| if (ft520x_i2c_rxdata(p_data_buf, PROTOCOL_LEN) < 0) { |
| printk(KERN_ERR "[tp] read axis err...\n"); |
| return CTPM_ERR_I2C; |
| } |
| |
| /*parse the data read out from ctpm and put the touch point information into pst_touch_info*/ |
| return bt_parser_std(p_data_buf, PROTOCOL_LEN, pst_touch_info); |
| } |
| |
| static void Ft5306_Enter_Sleep(void) |
| { |
| #if (TP_CNTRL_PIN_TYPE == TP_CNTRL_PIN_RESET) |
| ft520x_shutdown(); |
| #elif (TP_CNTRL_PIN_TYPE == TP_CNTRL_PIN_WAKEUP) |
| ft520x_shutdown(); |
| #else |
| #error NO TP CNTRL TYPE SPECIFIED!!! |
| #endif |
| } |
| |
| static void Ft5306_Exit_Sleep(void) |
| { |
| #if (TP_CNTRL_PIN_TYPE == TP_CNTRL_PIN_WAKEUP) |
| gpio_request(ts_gpio_wakeup_pin, "tp_wakeup"); |
| gpio_direction_output(ts_gpio_wakeup_pin, 0); |
| gpio_set_value(ts_gpio_wakeup_pin, 0); |
| mdelay(5); |
| gpio_set_value(ts_gpio_wakeup_pin, 1); |
| mdelay(20); |
| gpio_free(ts_gpio_wakeup_pin); |
| #endif |
| } |
| |
| #if (TP_CNTRL_PIN_TYPE == TP_CNTRL_PIN_RESET) |
| void Ft5306_Hw_Reset(void) |
| { |
| gpio_request(ts_gpio_reset_pin, "tp_reset"); |
| gpio_direction_output(ts_gpio_reset_pin, 1); |
| gpio_set_value(ts_gpio_reset_pin, 1); |
| mdelay(5); |
| gpio_set_value(ts_gpio_reset_pin, 0); |
| mdelay(5); |
| gpio_set_value(ts_gpio_reset_pin, 1); |
| gpio_free(ts_gpio_reset_pin); |
| mdelay(5); |
| } |
| #endif |
| |
| /* define in platform/board file(s) */ |
| static const struct i2c_device_id focaltech_ft5306_id[]= |
| { |
| {"FocalTech-Ft5306", 0}, |
| {}, |
| |
| }; |
| |
| static int focaltech_ft5306_turnOn(struct i2c_client *client); |
| static int focaltech_ft5306_turnOff(struct i2c_client *client); |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| static void focaltech_ft5306_early_suspend(struct early_suspend *h); |
| static void focaltech_ft5306_late_resume(struct early_suspend *h); |
| #endif |
| #define FINGER_DOWN 1 |
| #define FINGER_HOLD 2 |
| #define FINGER_STALE 3 |
| #define FINGER_UP 4 |
| #define FINGER_IDLE 5 |
| |
| #define POINT_HISTORY_DEPTH 2 |
| struct Pointer{ |
| uint8_t state; |
| int x,y; |
| int pressure; |
| int area; |
| } ; |
| |
| struct FingersQueue{ |
| int points_num; |
| struct Pointer points[FTS_MAX_FINGER]; |
| } ; |
| |
| static struct FingersQueue g_CurFingers[POINT_HISTORY_DEPTH]; |
| static int g_CurFingerInfoSlotId = 0; |
| |
| #define Switch_Finger_Slot() (g_CurFingerInfoSlotId = (g_CurFingerInfoSlotId+1 < POINT_HISTORY_DEPTH) ? (g_CurFingerInfoSlotId+1) : (0)) |
| #define Finger_Last_Slot() ((g_CurFingerInfoSlotId < 1) ? (POINT_HISTORY_DEPTH -1) : (g_CurFingerInfoSlotId - 1)) |
| #define Finger_Cur_Slot() (g_CurFingerInfoSlotId) |
| void InitFingersQueue(void) |
| { |
| int i,j; |
| |
| for (i = 0; i < FTS_MAX_FINGER; i++) { |
| for (j = 0; j < POINT_HISTORY_DEPTH; j++) { |
| g_CurFingers[j].points_num = 0; |
| g_CurFingers[j].points[i].x = 0; |
| g_CurFingers[j].points[i].y = 0; |
| g_CurFingers[j].points[i].state = FINGER_IDLE; |
| } |
| } |
| g_CurFingerInfoSlotId = 0; |
| } |
| |
| void InvalidateFingerSlot(struct FingersQueue *slot) |
| { |
| int i; |
| slot->points_num = 0; |
| for (i = 0; i < FTS_MAX_FINGER; i++) { |
| slot->points[i].state = FINGER_UP; |
| /*slot->points[i].x = 0; |
| slot->points[i].y = 0;*/ |
| } |
| } |
| |
| void UpdateFingerQueue(ST_TOUCH_INFO *touch_info) |
| { |
| int i; |
| struct FingersQueue *pFinger; |
| |
| if (touch_info->bt_tp_num > FTS_MAX_FINGER){ |
| printk(KERN_INFO "[tp err]: pt num %d exceed %d", touch_info->bt_tp_num, FTS_MAX_FINGER); |
| } |
| Switch_Finger_Slot(); |
| pFinger = &g_CurFingers[Finger_Cur_Slot()]; |
| InvalidateFingerSlot(pFinger); |
| pFinger->points_num = touch_info->bt_tp_num; |
| //printk(KERN_INFO "[tp] pnum=%d slot=%d", pFinger->points_num, Finger_Cur_Slot()); |
| for (i = 0; i < pFinger->points_num; i++) { |
| if (touch_info->pst_point_info[i].bt_tp_id >= FTS_MAX_FINGER) { |
| printk(KERN_INFO "[tp err]: invalid pt id %d", touch_info->pst_point_info[i].bt_tp_id); |
| pFinger->points_num --;//Invalid point remove it |
| continue; |
| } |
| //printk(KERN_INFO "[tp] p%d detected x=%d y=%d\n", touch_info->pst_point_info[i].bt_tp_id, touch_info->pst_point_info[i].w_tp_x, touch_info->pst_point_info[i].w_tp_y); |
| switch (touch_info->pst_point_info[i].bt_tp_property) { |
| case TC_PutDown: |
| pFinger->points[touch_info->pst_point_info[i].bt_tp_id].state = FINGER_DOWN; |
| break; |
| case TC_Contact: |
| pFinger->points[touch_info->pst_point_info[i].bt_tp_id].state = FINGER_HOLD; |
| break; |
| case TC_PutUp: |
| pFinger->points[touch_info->pst_point_info[i].bt_tp_id].state = FINGER_UP; |
| break; |
| default: |
| pFinger->points[touch_info->pst_point_info[i].bt_tp_id].state = FINGER_IDLE; |
| break; |
| } |
| pFinger->points[touch_info->pst_point_info[i].bt_tp_id].x = touch_info->pst_point_info[i].w_tp_x; |
| pFinger->points[touch_info->pst_point_info[i].bt_tp_id].y = touch_info->pst_point_info[i].w_tp_y; |
| if (pressure_support) { |
| pFinger->points[touch_info->pst_point_info[i].bt_tp_id].pressure = touch_info->pst_point_info[i].w_tp_strenth; |
| pFinger->points[touch_info->pst_point_info[i].bt_tp_id].area = touch_info->pst_point_info[i].w_tp_area; |
| } |
| } |
| } |
| |
| void ReportFingers(struct synaptics_rmi4 *ts, ST_TOUCH_INFO *touch_info) |
| { |
| struct FingersQueue * cur_info, *last_info; |
| int i, j, reported=0, active_finers = 0; |
| |
| if (!touch_info) { |
| printk(KERN_INFO "[tp err]: input touch data null"); |
| return; |
| } |
| |
| UpdateFingerQueue(touch_info); |
| cur_info = &g_CurFingers[Finger_Cur_Slot()]; |
| last_info = &g_CurFingers[Finger_Last_Slot()]; |
| for (i = 0; i < FTS_MAX_FINGER; i++) { |
| if (cur_info->points[i].state < FINGER_STALE) { |
| if (cur_info->points[i].y > ts_y_max_value) |
| blocking_notifier_call_chain( |
| &touch_key_notifier, 0, NULL); |
| |
| input_report_abs(ts->input_dev, ABS_MT_POSITION_X, cur_info->points[i].x); |
| input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, cur_info->points[i].y); |
| if (pressure_support) { |
| input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, cur_info->points[i].area); |
| input_report_abs(ts->input_dev, ABS_MT_PRESSURE, cur_info->points[i].pressure); |
| }else |
| input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 8); |
| input_report_key(ts->input_dev, BTN_TOUCH, 1); |
| //input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 1); |
| input_mt_sync(ts->input_dev); |
| reported = 1; |
| active_finers ++; |
| //printk(KERN_INFO "[tp]: p(%d) x=%d y=%d\n", i, cur_info->points[i].x, cur_info->points[i].y); |
| } else if (cur_info->points[i].state == FINGER_UP && last_info->points[i].state < FINGER_UP) { |
| cur_info->points[i].state = FINGER_STALE;//stale points |
| /*if ((last_info->points_num == 1) && (cur_info->points_num == 1)){ |
| cur_info->points[i].state = FINGER_UP; |
| input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); |
| input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); |
| input_mt_sync(ts->input_dev); |
| reported = 1; |
| active_finers ++; |
| printk(KERN_INFO "[tp]: single p(%d) up\n", i); |
| }else*/ |
| //printk(KERN_INFO "[tp]: stale p(%d)\n", i); |
| } |
| } |
| if (active_finers){ |
| if (reported) |
| input_sync(ts->input_dev); |
| //queue_work(synaptics_wq, &ts->work); |
| /*if (ts->use_irq) //Using int trigger can make the tp points more smooth |
| enable_irq(ts->client->irq);*/ |
| } else { |
| //printk(KERN_INFO "[tp]: no finger down\n"); |
| for (i = 0; i < FTS_MAX_FINGER; i++) { |
| if ((cur_info->points[i].state >= FINGER_STALE) && (last_info->points[i].state < FINGER_UP)) { |
| //input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); |
| input_report_key(ts->input_dev, BTN_TOUCH, 0); |
| input_mt_sync(ts->input_dev); |
| reported = 1; |
| //printk(KERN_INFO "[tp]: p(%d) up\n", i); |
| } |
| |
| for (j = 0; j < POINT_HISTORY_DEPTH; j++) { |
| //InvalidateFingerSlot(g_CurFingers[j].points[i].state == FINGER_IDLE); |
| g_CurFingers[j].points[i].state = FINGER_IDLE; |
| } |
| } |
| if (reported) |
| input_sync(ts->input_dev); |
| |
| /*if (ts->use_irq) |
| enable_irq(ts->client->irq);*/ |
| } |
| } |
| |
| #if 0 |
| static void DebugTpStatus(void) |
| { |
| int i; |
| |
| printk(KERN_INFO "point Num: %d, gesture: %d", ft5306_touch_info.bt_tp_num, ft5306_touch_info.bt_gesture); |
| for (i = 0; i < ft5306_touch_info.bt_tp_num; i++) { |
| printk(KERN_INFO "P[%d] property: %d, x: %d, y:%d", ft5306_touch_info.pst_point_info[i].bt_tp_id, |
| ft5306_touch_info.pst_point_info[i].bt_tp_property, |
| ft5306_touch_info.pst_point_info[i].w_tp_x, |
| ft5306_touch_info.pst_point_info[i].w_tp_y); |
| } |
| } |
| #endif |
| |
| #define UNEXPECT_POINT_MAX_TEST 5 |
| static void focaltech_ft5306_work_func(struct work_struct *work) |
| { |
| int ret = 0; |
| struct synaptics_rmi4 *ts = container_of(work, |
| struct synaptics_rmi4, work); |
| |
| ft5306_touch_info.pst_point_info = ft5306_touch_point; |
| ret = fts_ctpm_get_touch_info(ts, &ft5306_touch_info); |
| //printk(KERN_INFO "get_touch_info ret: %x", ret); |
| if (ret == CTPM_ERR_I2C) { |
| ft5306_i2c_client = ts->client; |
| focaltech_ft5306_turnOff(ft5306_i2c_client); |
| mdelay(5); |
| focaltech_ft5306_turnOn(ft5306_i2c_client); |
| } else { |
| ReportFingers(ts, &ft5306_touch_info); |
| } |
| //ReportFingers(ts, &ft5306_touch_info); |
| } |
| |
| irqreturn_t focaltech_ft5306_irq_handler(int irq, void *dev_id) |
| { |
| struct synaptics_rmi4 *ts = dev_id; |
| queue_work(synaptics_wq, &ts->work); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static void focaltech_ft5306_enable(struct synaptics_rmi4 *ts) |
| { |
| printk("%s: %s()\n", ts->client->name, __func__); |
| |
| if (ts->use_irq) |
| enable_irq(ts->client->irq); |
| |
| ts->enable = 1; |
| } |
| |
| static void focaltech_ft5306_disable(struct synaptics_rmi4 *ts) |
| { |
| printk("%s: %s()\n", ts->client->name, __func__); |
| |
| if (ts->use_irq) |
| disable_irq_nosync(ts->client->irq); |
| |
| ts->enable = 0; |
| } |
| |
| static int focaltech_ft5306_turnOff(struct i2c_client *client) |
| { |
| struct synaptics_rmi4 *ts; |
| |
| if (!client) return -1; |
| |
| ts = i2c_get_clientdata(client); |
| focaltech_ft5306_disable(ts); |
| Ft5306_Enter_Sleep(); |
| mdelay(10); |
| if (ts->power) { |
| ts->power(TS_OFF); |
| } |
| return 0; |
| } |
| |
| static int focaltech_ft5306_turnOn(struct i2c_client *client) |
| { |
| struct synaptics_rmi4 *ts; |
| |
| if (!client) return -1; |
| |
| ts = i2c_get_clientdata(client); |
| |
| Ft5306_Exit_Sleep(); |
| mdelay(20); |
| if (ts->power) { |
| ts->power(TS_ON); |
| } |
| #if (TP_CNTRL_PIN_TYPE == TP_CNTRL_PIN_RESET) |
| Ft5306_Hw_Reset(); |
| #endif |
| mdelay(50); |
| focaltech_ft5306_enable(ts); |
| |
| return 0; |
| } |
| |
| static ssize_t focaltech_ft5306_enable_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct synaptics_rmi4 *ts = dev_get_drvdata(dev); |
| |
| return sprintf(buf, "%u\n", ts->enable); |
| } |
| |
| static ssize_t focaltech_ft5306_enable_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct synaptics_rmi4 *ts = dev_get_drvdata(dev); |
| unsigned long val; |
| int error; |
| |
| error = strict_strtoul(buf, 10, &val); |
| |
| if (error) |
| return error; |
| |
| val = !!val; |
| |
| if (val != ts->enable) { |
| if (val) |
| focaltech_ft5306_enable(ts); |
| else |
| focaltech_ft5306_disable(ts); |
| } |
| |
| return count; |
| } |
| |
| //DEV_ATTR(synaptics_rmi4, enable, 0664); |
| static DEVICE_ATTR(enable, 0664, focaltech_ft5306_enable_show, focaltech_ft5306_enable_store); |
| static ssize_t ft5306_min_gap_show(struct kobject *kobj, |
| struct kobj_attribute *attr, char *buf) |
| { |
| return sprintf(buf, "min_gap: %d\n", min_gap); |
| } |
| |
| ssize_t ft5306_min_gap_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) |
| { |
| long set = 0; |
| if ((strict_strtol(buf, 0, &set)) < 0) |
| printk(KERN_WARNING "gap_store:error strict_strtol\n"); |
| |
| min_gap = set ; |
| return count; |
| } |
| |
| static struct kobj_attribute ft5306_min_gap_attr = { |
| .attr = { |
| .name = "min_gap", |
| .mode = 0644, |
| }, |
| .show = &ft5306_min_gap_show, |
| .store = &ft5306_min_gap_store, |
| }; |
| |
| static ssize_t ft5306_sensitivity_show(struct kobject *kobj, |
| struct kobj_attribute *attr, char *buf) |
| { |
| uint8_t sensitivity = 0; |
| ft520x_read_reg(0x88, &sensitivity); |
| return sprintf(buf, "sensitivity: %d\n", sensitivity); |
| } |
| |
| ssize_t ft5306_sensitivity_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) |
| { |
| uint8_t sensitivity = 8; |
| long set = 0; |
| |
| if ((strict_strtol(buf, 0, &set)) < 0) |
| printk(KERN_WARNING "senstivity:error strict_strtol\n"); |
| |
| if ( (set >= 4) && (set <= 8) ) { |
| sensitivity = set ; |
| } |
| printk(KERN_INFO "ft5306 change sensitivity to: %d", sensitivity); |
| ft520x_write_reg(0x88, sensitivity); |
| |
| return count; |
| } |
| |
| static struct kobj_attribute ft5306_sensitivity_attr = { |
| .attr = { |
| .name = "sensitivity", |
| .mode = 0644, |
| }, |
| .show = &ft5306_sensitivity_show, |
| .store = &ft5306_sensitivity_store, |
| }; |
| |
| int ft6x06_i2c_Read(struct i2c_client *client, char *writebuf, |
| int writelen, char *readbuf, int readlen) |
| { |
| int ret; |
| client = ft5306_i2c_client; |
| |
| if (writelen > 0) { |
| struct i2c_msg msgs[] = { |
| { |
| .addr = client->addr, |
| .flags = 0, |
| .len = writelen, |
| .buf = writebuf, |
| }, |
| { |
| .addr = client->addr, |
| .flags = I2C_M_RD, |
| .len = readlen, |
| .buf = readbuf, |
| }, |
| }; |
| ret = i2c_transfer(client->adapter, msgs, 2); |
| if (ret < 0) |
| dev_err(&client->dev, "f%s: i2c read error.\n", |
| __func__); |
| } else { |
| struct i2c_msg msgs[] = { |
| { |
| .addr = client->addr, |
| .flags = I2C_M_RD, |
| .len = readlen, |
| .buf = readbuf, |
| }, |
| }; |
| ret = i2c_transfer(client->adapter, msgs, 1); |
| if (ret < 0) |
| dev_err(&client->dev, "%s:i2c read error.\n", __func__); |
| } |
| return ret; |
| } |
| /*write data by i2c*/ |
| int ft6x06_i2c_Write(struct i2c_client *client, char *writebuf, int writelen) |
| { |
| int ret; |
| ret = ft520x_i2c_txdata(writebuf, writelen); |
| return ret; |
| } |
| |
| /* |
| *get firmware size |
| |
| @firmware_name:firmware name |
| *note:the firmware default path is sdcard. |
| if you want to change the dir, please modify by yourself. |
| */ |
| static int ft6x06_GetFirmwareSize(char *firmware_name) |
| { |
| struct file *pfile = NULL; |
| struct inode *inode; |
| unsigned long magic; |
| off_t fsize = 0; |
| char filepath[256]; |
| memset(filepath, 0, sizeof(filepath)); |
| |
| sprintf(filepath, "%s", firmware_name); |
| |
| if (NULL == pfile) |
| pfile = filp_open(filepath, O_RDONLY, 0); |
| |
| if (IS_ERR(pfile)) { |
| pr_err("error occured while opening file %s.\n", filepath); |
| return -EIO; |
| } |
| |
| inode = pfile->f_dentry->d_inode; |
| magic = inode->i_sb->s_magic; |
| fsize = inode->i_size; |
| filp_close(pfile, NULL); |
| return fsize; |
| } |
| |
| |
| |
| /* |
| *read firmware buf for .bin file. |
| |
| @firmware_name: fireware name of full path |
| @firmware_buf: data buf of fireware |
| |
| note:the firmware default path is sdcard. |
| if you want to change the dir, please modify by yourself. |
| */ |
| static int ft6x06_ReadFirmware(char *firmware_name, |
| unsigned char *firmware_buf) |
| { |
| struct file *pfile = NULL; |
| struct inode *inode; |
| unsigned long magic; |
| off_t fsize; |
| char filepath[256]; |
| loff_t pos; |
| mm_segment_t old_fs; |
| |
| if (!firmware_buf) { |
| pr_err("%s--firmware buffer NULL!\n", __func__); |
| return -EIO; |
| } |
| |
| memset(filepath, 0, sizeof(filepath)); |
| sprintf(filepath, "%s", firmware_name); |
| if (NULL == pfile) |
| pfile = filp_open(filepath, O_RDONLY, 0); |
| if (IS_ERR(pfile)) { |
| pr_err("error occured while opening file %s.\n", filepath); |
| return -EIO; |
| } |
| |
| inode = pfile->f_dentry->d_inode; |
| magic = inode->i_sb->s_magic; |
| fsize = inode->i_size; |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| pos = 0; |
| vfs_read(pfile, firmware_buf, fsize, &pos); |
| filp_close(pfile, NULL); |
| set_fs(old_fs); |
| |
| return 0; |
| } |
| |
| static int focaltec_fts_ctpm_fw_upgrade(struct i2c_client *client, u8 *pbt_buf, |
| u32 dw_lenth) |
| { |
| u8 reg_val[2] = {0}; |
| u32 i = 0; |
| u32 packet_number; |
| u32 j = 0; |
| u32 temp; |
| u32 lenght; |
| u8 packet_buf[FTS_PACKET_LENGTH + 6]; |
| u8 auc_i2c_write_buf[10]; |
| u8 bt_ecc; |
| int i_ret; |
| |
| if (dw_lenth < 5) { |
| printk(KERN_ERR "firmware size is too short, invalid.\n"); |
| return -1; |
| } |
| if (!pbt_buf) { |
| printk(KERN_ERR "firmware buffer is null!\n"); |
| return -1; |
| } |
| |
| for (i = 0; i < FTS_UPGRADE_LOOP; i++) { |
| /*********Step 1:Reset CTPM *****/ |
| /*write 0xaa to register 0xbc */ |
| ft520x_write_reg(FTS_UPGRADE_REG, FT_UPGRADE_AA); |
| msleep(FT6X06_UPGRADE_AA_DELAY); |
| |
| /*write 0x55 to register 0xbc */ |
| ft520x_write_reg(FTS_UPGRADE_REG, FT_UPGRADE_55); |
| |
| msleep(FT6X06_UPGRADE_55_DELAY + i*5); |
| |
| /*********Step 2:Enter upgrade mode *****/ |
| auc_i2c_write_buf[0] = FT_UPGRADE_55; |
| auc_i2c_write_buf[1] = FT_UPGRADE_AA; |
| do { |
| j++; |
| auc_i2c_write_buf[0] = FT_UPGRADE_55; |
| i_ret = ft6x06_i2c_Write(client, auc_i2c_write_buf, 1); |
| msleep(5); |
| auc_i2c_write_buf[0] = FT_UPGRADE_AA; |
| i_ret = ft6x06_i2c_Write(client, auc_i2c_write_buf, 1); |
| msleep(5); |
| } while (i_ret <= 0 && j < 5); |
| |
| |
| /*********Step 3:check READ-ID***********************/ |
| msleep(FT6X06_UPGRADE_READID_DELAY); |
| auc_i2c_write_buf[0] = 0x90; |
| auc_i2c_write_buf[1] = 0x00; |
| auc_i2c_write_buf[2] = 0x00; |
| auc_i2c_write_buf[3] = 0x00; |
| ft6x06_i2c_Read(client, auc_i2c_write_buf, 4, reg_val, 2); |
| |
| |
| if (reg_val[0] == FTS_UPGRADE_ID_1 |
| && reg_val[1] == FTS_UPGRADE_ID_2) { |
| DBG("[FTS] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n", |
| reg_val[0], reg_val[1]); |
| break; |
| } else { |
| dev_err(&client->dev, "[FTS] Step 3: Retry---CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n", |
| reg_val[0], reg_val[1]); |
| } |
| } |
| if (i > FTS_UPGRADE_LOOP) |
| return -EIO; |
| auc_i2c_write_buf[0] = 0xcd; |
| |
| ft6x06_i2c_Read(client, auc_i2c_write_buf, 1, reg_val, 1); |
| |
| |
| /*Step 4:erase app and panel paramenter area*/ |
| DBG("Step 4:erase app and panel paramenter area\n"); |
| auc_i2c_write_buf[0] = 0x61; |
| ft6x06_i2c_Write(client, auc_i2c_write_buf, 1); /*erase app area */ |
| msleep(FT6X06_UPGRADE_EARSE_DELAY); |
| /*erase panel parameter area */ |
| auc_i2c_write_buf[0] = 0x63; |
| ft6x06_i2c_Write(client, auc_i2c_write_buf, 1); |
| msleep(100); |
| |
| /*********Step 5:write firmware(FW) to ctpm flash*********/ |
| bt_ecc = 0; |
| DBG("Step 5:write firmware(FW) to ctpm flash\n"); |
| |
| dw_lenth = dw_lenth - 8; |
| packet_number = (dw_lenth) / FTS_PACKET_LENGTH; |
| packet_buf[0] = 0xbf; |
| packet_buf[1] = 0x00; |
| |
| for (j = 0; j < packet_number; j++) { |
| temp = j * FTS_PACKET_LENGTH; |
| packet_buf[2] = (u8) (temp >> 8); |
| packet_buf[3] = (u8) temp; |
| lenght = FTS_PACKET_LENGTH; |
| packet_buf[4] = (u8) (lenght >> 8); |
| packet_buf[5] = (u8) lenght; |
| |
| for (i = 0; i < FTS_PACKET_LENGTH; i++) { |
| packet_buf[6 + i] = pbt_buf[j * FTS_PACKET_LENGTH + i]; |
| bt_ecc ^= packet_buf[6 + i]; |
| } |
| ft6x06_i2c_Write(client, packet_buf, FTS_PACKET_LENGTH + 6); |
| msleep(FTS_PACKET_LENGTH / 6 + 1); |
| } |
| |
| if ((dw_lenth) % FTS_PACKET_LENGTH > 0) { |
| temp = packet_number * FTS_PACKET_LENGTH; |
| packet_buf[2] = (u8) (temp >> 8); |
| packet_buf[3] = (u8) temp; |
| temp = (dw_lenth) % FTS_PACKET_LENGTH; |
| packet_buf[4] = (u8) (temp >> 8); |
| packet_buf[5] = (u8) temp; |
| |
| for (i = 0; i < temp; i++) { |
| packet_buf[6 + i] = pbt_buf[packet_number * FTS_PACKET_LENGTH + i]; |
| bt_ecc ^= packet_buf[6 + i]; |
| } |
| |
| ft6x06_i2c_Write(client, packet_buf, temp + 6); |
| msleep(20); |
| } |
| |
| |
| /*send the last six byte */ |
| for (i = 0; i < 6; i++) { |
| temp = 0x6ffa + i; |
| packet_buf[2] = (u8) (temp >> 8); |
| packet_buf[3] = (u8) temp; |
| temp = 1; |
| packet_buf[4] = (u8) (temp >> 8); |
| packet_buf[5] = (u8) temp; |
| packet_buf[6] = pbt_buf[dw_lenth + i]; |
| bt_ecc ^= packet_buf[6]; |
| ft6x06_i2c_Write(client, packet_buf, 7); |
| msleep(20); |
| } |
| |
| |
| /*********Step 6: read out checksum***********************/ |
| /*send the opration head */ |
| DBG("Step 6: read out checksum\n"); |
| auc_i2c_write_buf[0] = 0xcc; |
| ft6x06_i2c_Read(client, auc_i2c_write_buf, 1, reg_val, 1); |
| if (reg_val[0] != bt_ecc) { |
| dev_err(&client->dev, "[FTS]--ecc error! FW=%02x bt_ecc=%02x\n", |
| reg_val[0], |
| bt_ecc); |
| return -EIO; |
| } |
| |
| /*********Step 7: reset the new FW***********************/ |
| DBG("Step 7: reset the new FW\n"); |
| auc_i2c_write_buf[0] = 0x07; |
| ft6x06_i2c_Write(client, auc_i2c_write_buf, 1); |
| msleep(300); /*make sure CTP startup normally */ |
| |
| return 0; |
| } |
| |
| static int update_complete = 1; |
| static void focaltech_fts_firmware_update_func(void) |
| { |
| update_complete = 0; |
| focaltec_fts_upgrade_firmware(); |
| wake_unlock(&update_wake_lock); |
| update_complete = 1; |
| } |
| |
| /* |
| upgrade with *.bin file with full path firmware_name |
| */ |
| |
| int fts_ctpm_fw_upgrade_with_app_file(struct i2c_client *client, |
| char *firmware_name) |
| { |
| u8 *pbt_buf = NULL; |
| int i_ret; |
| int fw_ver; |
| int length; |
| u8 pshare[100] = {0}; |
| char *fw_str = NULL; |
| int fwsize = ft6x06_GetFirmwareSize(firmware_name); |
| |
| if (fwsize <= 0) { |
| dev_err(&client->dev, "%s ERROR:Get firmware size failed\n", |
| __func__); |
| return -EIO; |
| } |
| |
| if (fwsize < 8 || fwsize > 32 * 1024) { |
| dev_dbg(&client->dev, "%s:FW length error\n", __func__); |
| return -EIO; |
| } |
| |
| /*=========FW upgrade========================*/ |
| pbt_buf = vmalloc(fwsize + 1); |
| if (!pbt_buf) { |
| dev_err(&client->dev, "%s() - ERROR: memory allocate failed\n", |
| __func__); |
| return -EIO; |
| } |
| if (ft6x06_ReadFirmware(firmware_name, pbt_buf)) { |
| dev_err(&client->dev, "%s() - ERROR: request_firmware failed\n", |
| __func__); |
| vfree(pbt_buf); |
| return -EIO; |
| } |
| if (fw_header_needed) { |
| /*parse the firmware file to ensure right firmware for special project*/ |
| memcpy(pshare, pbt_buf, 5);/*fetch the first 5 bytes*/ |
| pshare[5] = '\0'; |
| if (strcmp(pshare, "touch") != 0) { |
| printk(KERN_ERR "Invalid firmware head, please pack it first.\n"); |
| vfree(pbt_buf); |
| return -90; |
| } |
| memset(pshare, 0, 100); |
| memcpy(pshare, pbt_buf+5, 4);/*fetch the key string length*/ |
| length = *((unsigned *)pshare); |
| memset(pshare, 0, 100); |
| memcpy(pshare, pbt_buf+5+4, length);/*fetch the key string*/ |
| pshare[length] = '\0'; |
| if (strcmp(pshare, firmware_str) != 0) { |
| printk(KERN_ERR "wrong firmware, stop update.\n"); |
| vfree(pbt_buf); |
| return -100; |
| } |
| printk(KERN_INFO "Valid firmare, going to update.\n"); |
| /*call the upgrade function */ |
| i_ret = focaltec_fts_ctpm_fw_upgrade(client, pbt_buf+5+4+length, fwsize-5-4-length); |
| } else/*call the upgrade function */ |
| i_ret = focaltec_fts_ctpm_fw_upgrade(client, pbt_buf, fwsize); |
| |
| if (i_ret != 0) |
| dev_err(&client->dev, "%s() - ERROR:[FTS] upgrade failed..\n", |
| __func__); |
| else { |
| if (NEED_CALIBRATION) |
| fts_ctpm_auto_clb(); |
| printk(KERN_INFO "[FTS] upgrade successfully.\n"); |
| } |
| vfree(pbt_buf); |
| return i_ret; |
| } |
| |
| static ssize_t ft6x06_fwupgradeapp_show(struct kobject *kobj, |
| struct kobj_attribute *attr, char *buf) |
| { |
| unsigned char ver = 0; |
| ver = ft520x_read_fw_ver(); |
| printk(KERN_INFO "ft6x06 fw ver show: %x\n", ver); |
| return sprintf(buf, "fw version: %x\n", ver); |
| } |
| |
| |
| /*upgrade from app.bin*/ |
| ssize_t ft6x06_fwupgradeapp_store(struct kobject *kobj, |
| struct kobj_attribute *attr, const char *buf, size_t count) |
| { |
| char fwname[256]; |
| |
| struct i2c_client *client = ft5306_i2c_client; |
| |
| wake_lock(&update_wake_lock); |
| memset(fwname, 0, sizeof(fwname)); |
| sprintf(fwname, "%s", buf); |
| fwname[count - 1] = '\0'; |
| disable_irq(client->irq); |
| fts_ctpm_fw_upgrade_with_app_file(client, fwname); |
| enable_irq(client->irq); |
| wake_unlock(&update_wake_lock); |
| return count; |
| } |
| |
| static struct kobj_attribute ft6x06_upgrade_app_attr = { |
| .attr = { |
| .name = "upgradeapp", |
| .mode = 0644, |
| }, |
| .show = &ft6x06_fwupgradeapp_show, |
| .store = &ft6x06_fwupgradeapp_store, |
| }; |
| |
| static ssize_t ft5306_upgrade_fw_show(struct kobject *kobj, |
| struct kobj_attribute *attr, char *buf) |
| { |
| unsigned char ver = 0; |
| ver = ft520x_read_fw_ver(); |
| printk(KERN_INFO "ft5306 fw ver show: %x\n", ver); |
| return sprintf(buf, "fw version: %x\n", ver); |
| } |
| |
| ssize_t ft5306_upgrade_fw_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) |
| { |
| unsigned char ver = 0; |
| ver = ft520x_read_fw_ver(); |
| printk(KERN_INFO "ft5306 upgrading fw... before upgrd ver: %x\n", ver); |
| focaltec_fts_ctpm_fw_upgrade_with_i_file(); |
| ver = ft520x_read_fw_ver(); |
| printk(KERN_INFO "ft5306 upgrading fw... after upgrd ver: %x\n", ver); |
| return count; |
| } |
| |
| static struct kobj_attribute ft5306_upgrade_fw_attr = { |
| .attr = { |
| .name = "upgradefw", |
| .mode = 0644, |
| }, |
| .show = &ft5306_upgrade_fw_show, |
| .store = &ft5306_upgrade_fw_store, |
| }; |
| |
| static ssize_t ft5306_vendor_show(struct kobject *kobj, |
| struct kobj_attribute *attr, char *buf) |
| { |
| unsigned char vendor_id = 0; |
| vendor_id = fts_ctpm_get_vendor_id(); |
| printk(KERN_INFO "ft5306 vendor_id is: %x\n", vendor_id); |
| if (vendor_id == 0x53) |
| return sprintf(buf, "%s\n", "Mutto Optronics") ; |
| else if (vendor_id == 0x5D) |
| return sprintf(buf, "%s\n", "BAOMING OPTRONICS CO,.LTD") ; |
| else if (vendor_id == HAWAII_GARNET_FT5X06_VENDOR_ID) |
| return sprintf(buf, "%s\n", "FocalTech FT5X06 Hawaii Garnet"); |
| else if (vendor_id == G5_A18_FT5X06_VENDOR_ID) |
| return sprintf(buf, "%s\n", "FocalTech FT5X06 G5 A18"); |
| else if (vendor_id == G5_A21_FT5316_VENDOR_ID) |
| return sprintf(buf, "%s\n", "FocalTech FT5316 G5 A21"); |
| else if (vendor_id == KTOUCH_W68_FT6X06_VENDOR_ID) |
| return sprintf(buf, "%s\n", "FocalTech FT6X06 Ktouch W68"); |
| else |
| return sprintf(buf, "%s\n", "UNKNOWN"); |
| |
| } |
| |
| static struct kobj_attribute ft5306_vendor_attr = { |
| .attr = { |
| .name = "vendor", |
| .mode = S_IRUGO, |
| }, |
| .show = &ft5306_vendor_show, |
| .store = NULL, |
| }; |
| |
| static ssize_t ft5306_virtual_keys_show(struct kobject *kobj, |
| struct kobj_attribute *attr, char *buf) |
| { |
| return sprintf(buf, vkey_scope); |
| } |
| |
| static struct kobj_attribute ft5306_virtual_keys_attr = { |
| .attr = { |
| .name = "virtualkeys.FocalTech-Ft5306",//"virtualkeys.Synaptics-RMI4", |
| .mode = S_IRUGO, |
| }, |
| .show = &ft5306_virtual_keys_show, |
| }; |
| |
| static struct attribute *ft5306_properties_attrs[] = { |
| &ft5306_virtual_keys_attr.attr, |
| &ft5306_sensitivity_attr.attr, |
| &ft5306_min_gap_attr.attr, |
| &ft5306_vendor_attr.attr, |
| &ft6x06_upgrade_app_attr.attr, |
| &ft5306_upgrade_fw_attr.attr, |
| NULL |
| }; |
| |
| static struct attribute_group ft5306_properties_attr_group = { |
| .attrs = ft5306_properties_attrs, |
| }; |
| |
| extern int set_irq_type(unsigned int irq, unsigned int type); |
| extern int bcm_gpio_pull_up_down_enable(unsigned int gpio, bool enable); |
| extern int bcm_gpio_pull_up(unsigned int gpio, bool up); |
| static int ft5306_focaltech_init_platform_hw(void) |
| { |
| printk(KERN_INFO "ft5306_focaltech_init_platform_hw\n"); |
| /*bcm_gpio_pull_up_down_enable(26, false); |
| bcm_gpio_pull_up_down_enable(27, false); |
| gpio_request(TP_HW_INT_PIN, "ft5306_focaltech_ts"); |
| gpio_direction_input(TP_HW_INT_PIN); |
| set_irq_type(GPIO_TO_IRQ(TP_HW_INT_PIN), IRQF_TRIGGER_FALLING); |
| gpio_free(TP_HW_INT_PIN);*/ |
| return 0; |
| } |
| static int ts_power(ts_power_status vreg_en) |
| { |
| struct regulator *reg = NULL; |
| if (!reg) { |
| reg = regulator_get(NULL, tp_power); |
| if (!reg || IS_ERR(reg)) { |
| pr_err("No Regulator available for %s\n", tp_power); |
| return -1; |
| } |
| } |
| if (reg) { |
| if (vreg_en) { |
| regulator_set_voltage(reg, 3000000, 3000000); |
| pr_err("Turn on TP (%s) to 2.8V\n", tp_power); |
| regulator_enable(reg); |
| } else { |
| pr_err("Turn off TP (%s)\n", tp_power); |
| regulator_disable(reg); |
| } |
| regulator_put(reg); |
| } else { |
| pr_err("TP Regulator Alloc Failed"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static int i2c_probe_failed; |
| |
| static int focaltech_ft5306_probe( |
| struct i2c_client *client, const struct i2c_device_id *id) |
| { |
| int ret = 0; |
| u32 val; |
| int detect_fail = 0; |
| unsigned char vendor_id = 0; |
| unsigned char chip_id = 0; |
| unsigned char chip_id2 = 0; |
| struct synaptics_rmi4 *ts; |
| struct Synaptics_ts_platform_data *pdata; |
| struct device_node *np; |
| struct kobject *properties_kobj; |
| ret = -1; |
| |
| if(client==NULL) |
| { |
| printk(KERN_ERR "ft5306 client null.\n"); |
| goto err_i2c_client_check; |
| } |
| np = client->dev.of_node; |
| ft5306_i2c_client = client; |
| ts = kzalloc(sizeof(struct synaptics_rmi4), GFP_KERNEL); |
| ts->client = client; |
| i2c_set_clientdata(client, ts); |
| printk(KERN_ERR "probing for Synaptics RMI4 device %s at $%02X...\n", client->name, client->addr); |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| printk(KERN_ERR "%s: need I2C_FUNC_I2C\n", __func__); |
| ret = -ENODEV; |
| goto err_check_functionality_failed; |
| } |
| pdata = client->dev.platform_data; |
| if (pdata) { |
| ts->power = pdata->power; |
| ts_gpio_irq_pin = pdata->gpio_irq_pin; |
| #if (TP_CNTRL_PIN_TYPE == TP_CNTRL_PIN_RESET) |
| ts_gpio_reset_pin = pdata->gpio_reset_pin; |
| #else |
| ts_gpio_wakeup_pin = pdata->gpio_wakeup_pin; |
| #endif |
| ts_x_max_value = pdata->x_max_value; |
| ts_y_max_value = pdata->y_max_value; |
| |
| } else if (client->dev.of_node) { |
| ts_gpio_irq_pin = of_get_named_gpio(np, |
| "gpio-irq-pin", 0); |
| if (!gpio_is_valid(ts_gpio_irq_pin)) { |
| pr_err( |
| "%s: ERROR Invalid gpio-irq-pin\n", |
| __func__); |
| } |
| |
| #if (TP_CNTRL_PIN_TYPE == TP_CNTRL_PIN_RESET) |
| ts_gpio_reset_pin = of_get_named_gpio(np, |
| "gpio-reset-pin", 0); |
| if (!gpio_is_valid(ts_gpio_reset_pin)) { |
| pr_err( |
| "%s: ERROR Invalid gpio-reset-pin\n", |
| __func__); |
| } |
| #else |
| ts_gpio_wakeup_pin = of_get_named_gpio(np, |
| "gpio-wakeup-pin", 0); |
| if (!gpio_is_valid(ts_gpio_wakeup_pin)) { |
| pr_err( |
| "%s: ERROR Invalid gpio-wakeup-pin\n", |
| __func__); |
| } |
| #endif |
| if (!of_property_read_u32(np, "x-max-value", &val)) |
| ts_x_max_value = val; |
| if (!of_property_read_u32(np, "y-max-value", &val)) |
| ts_y_max_value = val; |
| if (!of_property_read_u32(np, "use-irq", &val)) |
| client->irq = val; |
| if (!of_property_read_u32(np, "auto_update_fw", &val)) |
| auto_update_fw = val; |
| if (!of_property_read_u32(np, "pressure_support", &val)) |
| pressure_support = val; |
| val = 0; |
| if (!of_property_read_u32(np, "chip_id", &val)) |
| chip_id2 = val; |
| of_property_read_string(np, "power", &tp_power); |
| if (!of_property_read_u32(np, "firmware_header_needed", &val)) |
| fw_header_needed = val; |
| if (fw_header_needed) { |
| if (of_property_read_string(np, "firmware_header", &fw_header)) |
| sprintf(firmware_str, "%s", "no_header"); |
| else |
| sprintf(firmware_str, "%s", fw_header); |
| } |
| if (of_property_read_string(np, "vkey_scope", &vkey_scope)) { |
| have_vkey = false; |
| vkey_scope = "\n"; |
| } |
| else |
| have_vkey = true; |
| |
| if (tp_power) |
| ts->power = ts_power; |
| } |
| |
| ft5306_focaltech_init_platform_hw(); |
| |
| #if (TP_CNTRL_PIN_TYPE == TP_CNTRL_PIN_WAKEUP) |
| gpio_request(ts_gpio_wakeup_pin, "tp_wakeup"); |
| gpio_direction_output(ts_gpio_wakeup_pin, 1); |
| gpio_set_value(ts_gpio_wakeup_pin, 0); |
| mdelay(5); |
| gpio_set_value(ts_gpio_wakeup_pin, 1); |
| gpio_free(ts_gpio_wakeup_pin); |
| mdelay(200); |
| #endif |
| if (ts->power) { |
| printk(KERN_ERR "Repower Tp Now...\n"); |
| ts->power(TS_ON); |
| mdelay(10); |
| } |
| /*read chip ID to detect if the chip exists*/ |
| if (chip_id2 > 0) { |
| chip_id = fts_ctpm_get_chip_id(); |
| if (chip_id != chip_id2) |
| detect_fail = 1; |
| } else { |
| vendor_id = fts_ctpm_get_vendor_id(); |
| |
| if ((vendor_id != HAWAII_GARNET_FT5X06_VENDOR_ID) |
| && (vendor_id != G5_A18_FT5X06_VENDOR_ID) |
| && (vendor_id != G5_A21_FT5316_VENDOR_ID) |
| && (vendor_id != KTOUCH_W81_FT6X06_VENDOR_ID) |
| && (vendor_id != TCL_UP823_FT6206_VENDOR_ID) |
| && (vendor_id != KTOUCH_5606_FT6X06_VENDOR_ID) |
| && (vendor_id != KTOUCH_W68_FT6X06_VENDOR_ID)) |
| detect_fail = 1; |
| } |
| |
| if (detect_fail) { |
| if (ts->power) { |
| ts->power(TS_OFF); |
| mdelay(10); |
| } |
| |
| kfree(ts); |
| ret = -ENODEV; |
| i2c_probe_failed = 1; |
| printk(KERN_ERR "Focaltech tp not exist!\r\n"); |
| goto err_chip_not_exist ; |
| } |
| |
| |
| #if (TP_CNTRL_PIN_TYPE == TP_CNTRL_PIN_RESET) |
| Ft5306_Hw_Reset(); |
| #endif |
| INIT_WORK(&ts->work, focaltech_ft5306_work_func); |
| wake_lock_init(&update_wake_lock, WAKE_LOCK_SUSPEND, "touch"); |
| if (auto_update_fw) { |
| update_complete = 0; |
| INIT_DELAYED_WORK(&ft6x06_firmware_update, focaltech_fts_firmware_update_func); |
| wake_lock(&update_wake_lock); |
| queue_delayed_work(synaptics_wq, &ft6x06_firmware_update, 5*HZ); |
| } |
| |
| |
| #if ENABLE_TP_DIAG |
| INIT_WORK(&ts->diag_work, focaltech_ft5306_diag_work_func); |
| #endif |
| |
| properties_kobj = kobject_create_and_add("board_properties", NULL); |
| if (properties_kobj) |
| ret = sysfs_create_group(properties_kobj, |
| &ft5306_properties_attr_group); |
| if (!properties_kobj || ret) |
| pr_err("failed to create board_properties\n"); |
| |
| ts->input_dev = input_allocate_device(); |
| if (!ts->input_dev) { |
| printk(KERN_ERR "failed to allocate input device.\n"); |
| ret = -EBUSY; |
| goto err_alloc_dev_failed; |
| } |
| |
| #if (TP_CNTRL_PIN_TYPE == TP_CNTRL_PIN_RESET) |
| if (ts->power) { |
| ts->power(TS_ON); |
| ts->power(TS_OFF); |
| mdelay(500); |
| } |
| #endif |
| |
| ft5306_touch_info.pst_point_info = ft5306_touch_point; |
| |
| ts->input_dev->name = "FocalTech-Ft5306"; |
| ts->input_dev->phys = client->name; |
| if (have_vkey) { |
| ts->input_dev->id.bustype = BUS_I2C; |
| ts->input_dev->id.vendor = 0x8888; |
| ts->input_dev->id.product = 0x6666; |
| ts->input_dev->id.version = 1000; |
| } |
| set_bit(EV_ABS, ts->input_dev->evbit); |
| set_bit(EV_SYN, ts->input_dev->evbit); |
| set_bit(BTN_TOUCH, ts->input_dev->keybit); |
| if (have_vkey) { |
| set_bit(KEY_MENU, ts->input_dev->keybit); |
| set_bit(KEY_HOME, ts->input_dev->keybit); |
| set_bit(KEY_BACK, ts->input_dev->keybit); |
| set_bit(KEY_SEARCH, ts->input_dev->keybit); |
| } |
| |
| set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); |
| |
| input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts_x_max_value, 0, 0); |
| input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts_y_max_value, 0, 0); |
| input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 8, 0, 0); |
| if (pressure_support) |
| input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); |
| |
| InitFingersQueue(); |
| if (client->irq) { |
| printk("%s IRQ %d", __func__, client->irq); |
| ret = gpio_request(ts_gpio_irq_pin, client->name); |
| printk(KERN_INFO "%s: request tp int ret: %d\n", __func__, ret); |
| |
| gpio_direction_input(ts_gpio_irq_pin); |
| printk("Requesting IRQ...\n"); |
| |
| if (request_irq(GPIO_TO_IRQ(ts_gpio_irq_pin), focaltech_ft5306_irq_handler, |
| IRQF_TRIGGER_FALLING, client->name, ts) >= 0) { |
| printk("Requested IRQ\n"); |
| ts->use_irq = 1; |
| printk(KERN_INFO "GPIO_%d INT: %d", ts_gpio_irq_pin, |
| GPIO_TO_IRQ(ts_gpio_irq_pin)); |
| /*if ((ret = set_irq_wake(client->irq, 1)) < 0) { |
| printk(KERN_ERR "failed to set IRQ wake: %d\n", ret); |
| }*/ |
| } else { |
| printk("Failed to request IRQ!\n"); |
| } |
| } |
| |
| if (!ts->use_irq) { |
| printk(KERN_ERR "Synaptics RMI4 device %s in polling mode\n", client->name); |
| } |
| |
| /* |
| * Device will be /dev/input/event# |
| * For named device files, use udev |
| */ |
| ret = input_register_device(ts->input_dev); |
| if (ret) { |
| printk(KERN_ERR "focaltech_ft5306_probe: Unable to register %s input device\n", ts->input_dev->name); |
| goto err_input_register_device_failed; |
| } else { |
| printk("tp input device registered\n"); |
| } |
| |
| ts->enable = 1; |
| |
| dev_set_drvdata(&ts->input_dev->dev, ts); |
| |
| if (sysfs_create_file(&ts->input_dev->dev.kobj, &dev_attr_enable.attr) < 0) |
| printk("failed to create sysfs file for input device\n"); |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; |
| ts->early_suspend.suspend = focaltech_ft5306_early_suspend; |
| ts->early_suspend.resume = focaltech_ft5306_late_resume; |
| register_early_suspend(&ts->early_suspend); |
| #endif |
| #ifdef CONFIG_LEDS_TRIGGER_KPBL |
| led_kpbl_register(®ister_touch_key_notifier); |
| led_kpbl_unregister(&unregister_touch_key_notifier); |
| #endif |
| |
| printk("Tp Probe Done!!!"); |
| |
| return 0; |
| |
| err_input_register_device_failed: |
| input_free_device(ts->input_dev); |
| err_chip_not_exist: |
| err_alloc_dev_failed: |
| err_check_functionality_failed: |
| err_i2c_client_check: |
| |
| return ret; |
| } |
| |
| |
| static int focaltech_ft5306_remove(struct i2c_client *client) |
| { |
| struct synaptics_rmi4 *ts = i2c_get_clientdata(client); |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| unregister_early_suspend(&ts->early_suspend); |
| #endif |
| if (ts->use_irq) |
| free_irq(client->irq, ts); |
| |
| input_unregister_device(ts->input_dev); |
| kfree(ts); |
| return 0; |
| } |
| |
| static int focaltech_ft5306_suspend(struct i2c_client *client, pm_message_t mesg) |
| { |
| struct synaptics_rmi4 *ts = i2c_get_clientdata(client); |
| |
| if (!update_complete) |
| return 0; |
| focaltech_ft5306_disable(ts); |
| Ft5306_Enter_Sleep(); |
| mdelay(10); |
| if (ts->power) { |
| ts->power(TS_OFF); |
| } |
| return 0; |
| } |
| |
| static int focaltech_ft5306_resume(struct i2c_client *client) |
| { |
| struct synaptics_rmi4 *ts = i2c_get_clientdata(client); |
| |
| if (!update_complete) |
| return 0; |
| Ft5306_Exit_Sleep(); |
| mdelay(2); |
| if (ts->power) |
| ts->power(TS_ON); |
| #if (TP_CNTRL_PIN_TYPE == TP_CNTRL_PIN_RESET) |
| Ft5306_Hw_Reset(); |
| #endif |
| mdelay(5); |
| focaltech_ft5306_enable(ts); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| static void focaltech_ft5306_early_suspend(struct early_suspend *h) |
| { |
| struct synaptics_rmi4 *ts; |
| printk(KERN_INFO "call %s\n", __func__); |
| ts = container_of(h, struct synaptics_rmi4, early_suspend); |
| focaltech_ft5306_suspend(ts->client, PMSG_SUSPEND); |
| } |
| |
| static void focaltech_ft5306_late_resume(struct early_suspend *h) |
| { |
| struct synaptics_rmi4 *ts; |
| printk(KERN_INFO "call %s\n", __func__); |
| ts = container_of(h, struct synaptics_rmi4, early_suspend); |
| focaltech_ft5306_resume(ts->client); |
| } |
| #endif |
| |
| static struct i2c_driver focaltech_ft5306_driver = { |
| .probe = focaltech_ft5306_probe, |
| .remove = focaltech_ft5306_remove, |
| #ifndef CONFIG_HAS_EARLYSUSPEND |
| .suspend = focaltech_ft5306_suspend, |
| .resume = focaltech_ft5306_resume, |
| #endif |
| .id_table = focaltech_ft5306_id, |
| .driver = { |
| .name = "FocalTech-Ft5306", |
| }, |
| }; |
| |
| static int focaltech_ft5306_init(void) |
| { |
| int retval; |
| |
| printk(KERN_INFO "Synaptics I2C RMI4 driver init"); |
| |
| synaptics_wq = create_singlethread_workqueue("synaptics_wq"); |
| if (!synaptics_wq) { |
| printk(KERN_ERR "Could not create work queue synaptics_wq: no memory"); |
| return -ENOMEM; |
| } |
| |
| retval = i2c_add_driver(&focaltech_ft5306_driver); |
| if (i2c_probe_failed) { |
| i2c_del_driver(&focaltech_ft5306_driver); |
| if (synaptics_wq) |
| destroy_workqueue(synaptics_wq); |
| } |
| return retval; |
| } |
| |
| static void __exit focaltech_ft5306_exit(void) |
| { |
| i2c_del_driver(&focaltech_ft5306_driver); |
| if (synaptics_wq) |
| destroy_workqueue(synaptics_wq); |
| } |
| |
| module_init(focaltech_ft5306_init); |
| module_exit(focaltech_ft5306_exit); |
| |
| MODULE_DESCRIPTION("FocalTech FT5306 Driver"); |
| MODULE_LICENSE("GPL"); |