| /* drivers/input/touchscreen/atmel_mXT224E.c - ATMEL Touch driver |
| * |
| * Copyright (C) 2008 ATMEL |
| * Copyright (C) 2011 Huawei Corporation. |
| * |
| * Based on touchscreen code from Atmel Corporation. |
| * |
| * This software is licensed under the terms of the GNU General Public |
| * License version 2, as published by the Free Software Foundation, and |
| * may be copied, distributed, and modified under those terms. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/input.h> |
| #include <linux/interrupt.h> |
| #include <linux/platform_device.h> |
| #include <linux/i2c.h> |
| #include <linux/delay.h> |
| #include <linux/gpio.h> |
| #include <linux/jiffies.h> |
| #include <linux/stat.h> |
| #include <linux/slab.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/regulator/driver.h> |
| #include <linux/regulator/machine.h> |
| #include <linux/bitops.h> |
| #include <linux/of_gpio.h> |
| #include <linux/pinctrl/consumer.h> |
| |
| #include <linux/kthread.h> |
| |
| #ifdef TS_ATMEL_DEBUG |
| #define TS_DEBUG_ATMEL(fmt, args...) pr_info(fmt, ##args) |
| #else |
| #define TS_DEBUG_ATMEL(fmt, args...) |
| #endif |
| |
| #define ATMEL_MXT224E_NAME "atmel_mxt224e" |
| |
| #define INFO_BLK_FID 0 |
| #define INFO_BLK_VID 1 |
| #define INFO_BLK_VER 2 |
| #define INFO_BLK_BUILD 3 |
| #define INFO_BLK_XSIZE 4 |
| #define INFO_BLK_YSIZE 5 |
| #define INFO_BLK_OBJS 6 |
| |
| #define OBJ_TABLE_TYPE 0 |
| #define OBJ_TABLE_LSB 1 |
| #define OBJ_TABLE_MSB 2 |
| #define OBJ_TABLE_SIZE 3 |
| #define OBJ_TABLE_INSTANCES 4 |
| #define OBJ_TABLE_RIDS 5 |
| |
| #define RESERVED_T0 0u |
| #define RESERVED_T1 1u |
| #define DEBUG_DELTAS_T2 2u |
| #define DEBUG_REFERENCES_T3 3u |
| #define DEBUG_SIGNALS_T4 4u |
| #define GEN_MESSAGEPROCESSOR_T5 5u |
| #define GEN_COMMANDPROCESSOR_T6 6u |
| #define GEN_POWERCONFIG_T7 7u |
| #define GEN_ACQUISITIONCONFIG_T8 8u |
| #define TOUCH_MULTITOUCHSCREEN_T9 9u |
| #define TOUCH_SINGLETOUCHSCREEN_T10 10u |
| #define TOUCH_XSLIDER_T11 11u |
| #define TOUCH_YSLIDER_T12 12u |
| #define TOUCH_XWHEEL_T13 13u |
| #define TOUCH_YWHEEL_T14 14u |
| #define TOUCH_KEYARRAY_T15 15u |
| #define PROCG_SIGNALFILTER_T16 16u |
| #define PROCI_LINEARIZATIONTABLE_T17 17u |
| #define SPT_COMCONFIG_T18 18u |
| #define SPT_GPIOPWM_T19 19u |
| #define PROCI_GRIPFACESUPPRESSION_T20 20u |
| #define RESERVED_T21 21u |
| #define PROCG_NOISESUPPRESSION_T22 22u |
| #define TOUCH_PROXIMITY_T23 23u |
| #define PROCI_ONETOUCHGESTUREPROCESSOR_T24 24u |
| #define SPT_SELFTEST_T25 25u |
| #define DEBUG_CTERANGE_T26 26u |
| #define PROCI_TWOTOUCHGESTUREPROCESSOR_T27 27u |
| #define SPT_CTECONFIG_T28 28u |
| #define SPT_GPI_T29 29u |
| #define SPT_GATE_T30 30u |
| #define TOUCH_KEYSET_T31 31u |
| #define TOUCH_XSLIDERSET_T32 32u |
| #define DIAGNOSTIC_T37 37u |
| #define PROCI_GRIPSUPPRESSION_T40 40u |
| #define PROCI_TOUCHSUPPRESSION_T42 42u |
| #define SPT_CTECONFIG_T46 46u |
| #define PROCI_STYLUS_T47 47u |
| #define PROCG_NOISESUPPRESSION_T48 48u |
| |
| #define T37_PAGE_SIZE 128 |
| |
| #define T37_TCH_FLAG_SIZE 80 |
| #define T37_TCH_FLAG_IDX 0 |
| #define T37_ATCH_FLAG_IDX 40 |
| |
| #define T37_MODE 0 |
| #define T37_PAGE 1 |
| #define T37_DATA 2 /* n bytes */ |
| |
| #define T37_PAGE_NUM0 0 |
| #define T37_PAGE_NUM1 1 |
| #define T37_PAGE_NUM2 2 |
| #define T37_PAGE_NUM3 3 |
| |
| #define MSG_RID 0 |
| |
| #define T6_CFG_RESET 0 |
| #define T6_CFG_BACKUPNV 1 |
| #define T6_CFG_CALIBRATE 2 |
| #define T6_CFG_REPORTALL 3 |
| /* Reserved */ |
| #define T6_CFG_DIAG 5 |
| |
| #define T6_CFG_DIAG_CMD_PAGEUP 0x01 |
| #define T6_CFG_DIAG_CMD_PAGEDOWN 0x02 |
| #define T6_CFG_DIAG_CMD_DELTAS 0x10 |
| #define T6_CFG_DIAG_CMD_REF 0x11 |
| #define T6_CFG_DIAG_CMD_CTE 0x31 |
| #define T6_CFG_DIAG_CMD_TCH 0xF3 |
| |
| #define T6_MSG_STATUS 1 |
| #define T6_MSG_CHECKSUM 2 /* three bytes */ |
| |
| #define T6_MSG_STATUS_COMSERR BIT(2) |
| #define T6_MSG_STATUS_CFGERR BIT(3) |
| #define T6_MSG_STATUS_CAL BIT(4) |
| #define T6_MSG_STATUS_SIGERR BIT(5) |
| #define T6_MSG_STATUS_OFL BIT(6) |
| #define T6_MSG_STATUS_RESET BIT(7) |
| |
| #define T7_CFG_IDLEACQINT 0 |
| #define T7_CFG_ACTVACQINT 1 |
| #define T7_CFG_ACTV2IDLETO 2 |
| |
| #define T8_CFG_CHRGTIME 0 |
| /* Reserved */ |
| #define T8_CFG_TCHDRIFT 2 |
| #define T8_CFG_DRIFTST 3 |
| #define T8_CFG_TCHAUTOCAL 4 |
| #define T8_CFG_SYNC 5 |
| #define T8_CFG_ATCHCALST 6 |
| #define T8_CFG_ATCHCALSTHR 7 |
| #define T8_CFG_ATCHFRCCALTHR 8 /* FW v2.x */ |
| #define T8_CFG_ATCHFRCCALRATIO 9 /* FW v2.x */ |
| |
| #define T9_CFG_CTRL 0 |
| #define T9_CFG_XORIGIN 1 |
| #define T9_CFG_YORIGIN 2 |
| #define T9_CFG_XSIZE 3 |
| #define T9_CFG_YSIZE 4 |
| #define T9_CFG_AKSCFG 5 |
| #define T9_CFG_BLEN 6 |
| #define T9_CFG_TCHTHR 7 |
| #define T9_CFG_TCHDI 8 |
| #define T9_CFG_ORIENT 9 |
| #define T9_CFG_MRGTIMEOUT 10 |
| #define T9_CFG_MOVHYSTI 11 |
| #define T9_CFG_MOVHYSTN 12 |
| #define T9_CFG_MOVFILTER 13 |
| #define T9_CFG_NUMTOUCH 14 |
| #define T9_CFG_MRGHYST 15 |
| #define T9_CFG_MRGTHR 16 |
| #define T9_CFG_AMPHYST 17 |
| #define T9_CFG_XRANGE 18 /* two bytes */ |
| #define T9_CFG_YRANGE 20 /* two bytes */ |
| #define T9_CFG_XLOCLIP 22 |
| #define T9_CFG_XHICLIP 23 |
| #define T9_CFG_YLOCLIP 24 |
| #define T9_CFG_YHICLIP 25 |
| #define T9_CFG_XEDGECTRL 26 |
| #define T9_CFG_XEDGEDIST 27 |
| #define T9_CFG_YEDGECTRL 28 |
| #define T9_CFG_YEDGEDIST 29 |
| #define T9_CFG_JUMPLIMIT 30 |
| #define T9_CFG_TCHHYST 31 /* FW v2.x */ |
| |
| #define T9_MSG_STATUS 1 |
| #define T9_MSG_XPOSMSB 2 |
| #define T9_MSG_YPOSMSB 3 |
| #define T9_MSG_XYPOSLSB 4 |
| #define T9_MSG_TCHAREA 5 |
| #define T9_MSG_TCHAMPLITUDE 6 |
| #define T9_MSG_TCHVECTOR 7 |
| |
| #define T9_MSG_STATUS_UNGRIP BIT(0) /* FW v2.x */ |
| #define T9_MSG_STATUS_SUPPRESS BIT(1) |
| #define T9_MSG_STATUS_AMP BIT(2) |
| #define T9_MSG_STATUS_VECTOR BIT(3) |
| #define T9_MSG_STATUS_MOVE BIT(4) |
| #define T9_MSG_STATUS_RELEASE BIT(5) |
| #define T9_MSG_STATUS_PRESS BIT(6) |
| #define T9_MSG_STATUS_DETECT BIT(7) |
| |
| #define T20_CFG_CTRL 0 |
| #define T20_CFG_XLOGRIP 1 |
| #define T20_CFG_XHIGRIP 2 |
| #define T20_CFG_YLOGRIP 3 |
| #define T20_CFG_YHIGRIP 4 |
| #define T20_CFG_MAXTCHS 5 |
| /* Reserved */ |
| #define T20_CFG_SZTHR1 7 |
| #define T20_CFG_SZTHR2 8 |
| #define T20_CFG_SHPTHR1 9 |
| #define T20_CFG_SHPTHR2 10 |
| #define T20_CFG_SHPEXTTO 11 |
| |
| #define T20_MSG_STATUS 1 |
| |
| #define T20_MSG_STATUS_FACESUP BIT(0) |
| |
| #define T22_CFG_CTRL 0 |
| /* Reserved */ |
| #define T22_CFG_GCAFUL 3 /* two bytes */ |
| #define T22_CFG_GCAFLL 5 /* two bytes */ |
| #define T22_CFG_ACTVGCAFVALID 7 |
| #define T22_CFG_NOISETHR 8 |
| /* Reserved */ |
| #define T22_CFG_FREQHOPSCALE 10 |
| #define T22_CFG_FREQ 11 /* five bytes */ |
| #define T22_CFG_IDLEGCAFVAILD 16 |
| |
| #define T22_MSG_STATUS 1 |
| #define T22_MSG_GCAFDEPTH 2 |
| #define T22_MSG_FREQINDEX 3 |
| |
| #define T22_MSG_STATUS_FHCHG BIT(0) |
| #define T22_MSG_STATUS_GCAFERR BIT(2) |
| #define T22_MSG_STATUS_FHERR BIT(3) |
| #define T22_MSG_STATUS_GCAFCHG BIT(4) |
| |
| #define T19_CFG_CTRL 0 |
| #define T19_CFG_REPORTMASK 1 |
| #define T19_CFG_DIR 2 |
| #define T19_CFG_INTPULLUP 3 |
| #define T19_CFG_OUT 4 |
| #define T19_CFG_WAKE 5 |
| #define T19_CFG_PWM 6 |
| #define T19_CFG_PERIOD 7 |
| #define T19_CFG_DUTY0 8 |
| #define T19_CFG_DUTY1 9 |
| #define T19_CFG_DUTY2 10 |
| #define T19_CFG_DUTY3 11 |
| #define T19_CFG_TRIGGER0 12 |
| #define T19_CFG_TRIGGER1 13 |
| #define T19_CFG_TRIGGER2 14 |
| #define T19_CFG_TRIGGER3 15 |
| |
| #define T19_CFG_CTRL_ENABLE BIT(0) |
| #define T19_CFG_CTRL_RPTEN BIT(1) |
| #define T19_CFG_CTRL_FORCERPT BIT(2) |
| |
| #define T19_MSG_STATUS 1 |
| |
| #define T25_CFG_CTRL 0 |
| #define T25_CFG_CMD 1 |
| |
| #define T25_MSG_STATUS 1 |
| #define T25_MSG_INFO 2 /* five bytes */ |
| |
| #define T28_CFG_CTRL 0 |
| #define T28_CFG_CMD 1 |
| #define T28_CFG_MODE 2 |
| #define T28_CFG_IDLEGCAFDEPTH 3 |
| #define T28_CFG_ACTVGCAFDEPTH 4 |
| #define T28_CFG_VOLTAGE 5 |
| |
| #define T28_CFG_MODE0_X 16 |
| #define T28_CFG_MODE0_Y 14 |
| |
| #define T28_MSG_STATUS 1 |
| |
| #define T48_NOISESUPPRESSION_CFG 1 |
| |
| /* cable_config[] of atmel_i2c_platform_data */ |
| /* config[] of atmel_config_data */ |
| #define CB_TCHTHR 0 |
| #define CB_NOISETHR 1 |
| #define CB_IDLEGCAFDEPTH 2 |
| #define CB_ACTVGCAFDEPTH 3 |
| |
| #define NC_TCHTHR 0 |
| #define NC_TCHDI 1 |
| #define NC_NOISETHR 2 |
| |
| /* filter_level */ |
| #define FL_XLOGRIPMIN 0 |
| #define FL_XLOGRIPMAX 1 |
| #define FL_XHIGRIPMIN 2 |
| #define FL_XHIGRIPMAX 3 |
| |
| struct info_id_t { |
| uint8_t family_id; |
| uint8_t variant_id; |
| uint8_t version; |
| uint8_t build; |
| uint8_t matrix_x_size; |
| uint8_t matrix_y_size; |
| uint8_t num_declared_objects; |
| }; |
| |
| struct object_t { |
| uint8_t object_type; |
| uint16_t i2c_address; |
| uint8_t size; |
| uint8_t instances; |
| uint8_t num_report_ids; |
| uint8_t report_ids; |
| }; |
| |
| struct atmel_virtual_key { |
| int keycode; |
| int range_min; |
| int range_max; |
| }; |
| |
| struct atmel_finger_data { |
| int x; |
| int y; |
| int w; |
| int z; |
| }; |
| |
| struct atmel_i2c_platform_data { |
| uint16_t version; |
| uint16_t source; |
| uint16_t abs_x_min; |
| uint16_t abs_x_max; |
| uint16_t abs_y_min; |
| uint16_t abs_y_max; |
| uint8_t abs_pressure_min; |
| uint8_t abs_pressure_max; |
| uint8_t abs_width_min; |
| uint8_t abs_width_max; |
| uint8_t abs_area_min; |
| uint8_t abs_area_max; |
| int gpio_irq; |
| int gpio_reset; |
| int (*power)(int on); |
| u8 config_T6[6]; |
| u8 config_T7[3]; |
| u8 config_T8[10]; |
| u8 config_T9[35]; |
| u8 config_T15[11]; |
| u8 config_T19[16]; |
| u8 config_T20[12]; |
| u8 config_T22[17]; |
| u8 config_T23[15]; |
| u8 config_T24[19]; |
| u8 config_T25[14]; |
| u8 config_T27[7]; |
| u8 config_T28[6]; |
| u8 config_T40[5]; |
| u8 config_T42[8]; |
| u8 config_T46[9]; |
| u8 config_T47[10]; |
| u8 config_T48[54]; |
| u8 object_crc[3]; |
| u8 cable_config[4]; |
| u8 cable_config_T7[3]; |
| u8 cable_config_T8[10]; |
| u8 cable_config_T9[35]; |
| u8 cable_config_T22[17]; |
| u8 cable_config_T28[6]; |
| u8 cable_config_T46[9]; |
| u8 cable_config_T48[54]; |
| u8 noise_config[3]; |
| u16 filter_level[4]; |
| u8 GCAF_level[5]; |
| u8 ATCH_NOR[6]; |
| u8 ATCH_NOR_20S[6]; |
| }; |
| |
| struct atmel_config_data { |
| int8_t config[4]; |
| int8_t *config_T7; |
| int8_t *config_T8; |
| int8_t *config_T9; |
| int8_t *config_T22; |
| int8_t *config_T28; |
| int8_t *config_T46; |
| int8_t *config_T48; |
| }; |
| |
| #define ATMEL_I2C_RETRY_TIMES 10 |
| |
| /* config_setting */ |
| #define NONE 0 |
| #define CONNECTED 1 |
| struct atmel_ts_data { |
| struct i2c_client *client; |
| struct input_dev *input_dev; |
| struct atmel_i2c_platform_data *pdata; |
| struct workqueue_struct *atmel_wq; |
| struct work_struct work; |
| int (*power) (int on); |
| struct info_id_t *id; |
| struct object_t *object_table; |
| struct iomux_block *gpio_block; |
| struct block_config *gpio_block_config; |
| uint8_t finger_count; |
| uint16_t abs_x_min; |
| uint16_t abs_x_max; |
| uint16_t abs_y_min; |
| uint16_t abs_y_max; |
| uint8_t abs_area_min; |
| uint8_t abs_area_max; |
| uint8_t abs_width_min; |
| uint8_t abs_width_max; |
| uint8_t abs_pressure_min; |
| uint8_t abs_pressure_max; |
| uint8_t first_pressed; |
| struct atmel_finger_data finger_data[10]; |
| uint8_t finger_type; |
| uint8_t finger_support; |
| uint16_t finger_pressed; |
| uint8_t face_suppression; |
| uint8_t grip_suppression; |
| uint8_t noise_status[2]; |
| uint16_t *filter_level; |
| uint8_t calibration_confirm; |
| uint64_t timestamp; |
| struct atmel_config_data config_setting[2]; |
| int8_t noise_config[3]; |
| uint8_t status; |
| uint8_t GCAF_sample; |
| uint8_t *GCAF_level; |
| uint8_t noisethr; |
| uint8_t noisethr_config; |
| uint8_t diag_command; |
| uint8_t *ATCH_EXT; |
| int8_t *ATCH_NOR; |
| int8_t *ATCH_NOR_20S; |
| int pre_data[11]; |
| /*unlock flag used to indicate calibration after unlock system*/ |
| int unlock_flag; |
| |
| /*For usb detect*/ |
| struct work_struct usb_work; |
| struct notifier_block nb; |
| unsigned long usb_event; |
| struct mutex lock; |
| }; |
| |
| static struct atmel_ts_data *private_ts; |
| |
| #define LDO_POWR_VOLTAGE 2700000 /*2.7v*/ |
| static struct regulator *LDO; |
| |
| int i2c_atmel_read(struct i2c_client *client, uint16_t address, uint8_t *data, uint8_t length) |
| { |
| int retry, ret; |
| uint8_t addr[2]; |
| |
| struct i2c_msg msg[] = { |
| { |
| .addr = client->addr, |
| .flags = 0, |
| .len = 2, |
| .buf = addr, |
| }, |
| { |
| .addr = client->addr, |
| .flags = I2C_M_RD, |
| .len = length, |
| .buf = data, |
| } |
| }; |
| addr[0] = address & 0xFF; |
| addr[1] = (address >> 8) & 0xFF; |
| |
| for (retry = 0; retry < ATMEL_I2C_RETRY_TIMES; retry++) { |
| ret = i2c_transfer(client->adapter, msg, 2); |
| if ((ret == 2) || (ret == -ERESTARTSYS)) |
| break; |
| mdelay(10); |
| } |
| if (retry == ATMEL_I2C_RETRY_TIMES) { |
| dev_err(&client->dev, "k3ts, %s: i2c_read_block retry over %d\n", __func__, |
| ATMEL_I2C_RETRY_TIMES); |
| return -EIO; |
| } |
| return 0; |
| } |
| |
| int i2c_atmel_write(struct i2c_client *client, uint16_t address, uint8_t *data, uint8_t length) |
| { |
| int retry, loop_i, ret; |
| uint8_t buf[length + 2]; |
| |
| struct i2c_msg msg[] = { |
| { |
| .addr = client->addr, |
| .flags = 0, |
| .len = length + 2, |
| .buf = buf, |
| } |
| }; |
| |
| buf[0] = address & 0xFF; |
| buf[1] = (address >> 8) & 0xFF; |
| |
| for (loop_i = 0; loop_i < length; loop_i++) |
| buf[loop_i + 2] = data[loop_i]; |
| |
| for (retry = 0; retry < ATMEL_I2C_RETRY_TIMES; retry++) { |
| ret = i2c_transfer(client->adapter, msg, 1); |
| if ((ret == 1) || (ret == -ERESTARTSYS)) |
| break; |
| mdelay(10); |
| } |
| |
| if (retry == ATMEL_I2C_RETRY_TIMES) { |
| dev_err(&client->dev, "k3ts, %s: i2c_write_block retry over %d\n", __func__, |
| ATMEL_I2C_RETRY_TIMES); |
| return -EIO; |
| } |
| return 0; |
| |
| } |
| |
| int i2c_atmel_write_byte_data(struct i2c_client *client, uint16_t address, uint8_t value) |
| { |
| i2c_atmel_write(client, address, &value, 1); |
| return 0; |
| } |
| |
| uint16_t get_object_address(struct atmel_ts_data *ts, uint8_t object_type) |
| { |
| uint8_t loop_i; |
| for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) { |
| if (ts->object_table[loop_i].object_type == object_type) |
| return ts->object_table[loop_i].i2c_address; |
| } |
| return 0; |
| } |
| uint8_t get_object_size(struct atmel_ts_data *ts, uint8_t object_type) |
| { |
| uint8_t loop_i; |
| for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) { |
| if (ts->object_table[loop_i].object_type == object_type) |
| return ts->object_table[loop_i].size; |
| } |
| return 0; |
| } |
| |
| uint8_t get_object_size_from_address(struct atmel_ts_data *ts, int address) |
| { |
| uint8_t loop_i; |
| for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) { |
| if (ts->object_table[loop_i].i2c_address == address) |
| return ts->object_table[loop_i].size; |
| } |
| return 0; |
| } |
| |
| uint8_t get_rid(struct atmel_ts_data *ts, uint8_t object_type) |
| { |
| uint8_t loop_i; |
| for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) { |
| if (ts->object_table[loop_i].object_type == object_type) |
| return ts->object_table[loop_i].report_ids; |
| } |
| return 0; |
| } |
| |
| static void check_calibration(struct atmel_ts_data *ts) |
| { |
| uint8_t data[T37_DATA + T37_TCH_FLAG_SIZE]; |
| uint8_t loop_i, loop_j, x_limit = 0, check_mask, tch_ch = 0, atch_ch = 0; |
| |
| memset(data, 0xFF, sizeof(data)); |
| i2c_atmel_write_byte_data(ts->client, |
| get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + |
| T6_CFG_DIAG, T6_CFG_DIAG_CMD_TCH); |
| |
| for (loop_i = 0; |
| !(data[T37_MODE] == T6_CFG_DIAG_CMD_TCH && data[T37_PAGE] == T37_PAGE_NUM0) && loop_i < 10; loop_i++) { |
| msleep(5); |
| i2c_atmel_read(ts->client, |
| get_object_address(ts, DIAGNOSTIC_T37), data, 2); |
| } |
| |
| if (loop_i == 10) |
| dev_err(&ts->client->dev, "k3ts, %s: Diag data not ready\n", __func__); |
| |
| i2c_atmel_read(ts->client, get_object_address(ts, DIAGNOSTIC_T37), data, |
| T37_DATA + T37_TCH_FLAG_SIZE); |
| if (data[T37_MODE] == T6_CFG_DIAG_CMD_TCH && |
| data[T37_PAGE] == T37_PAGE_NUM0) { |
| x_limit = T28_CFG_MODE0_X + ts->config_setting[NONE].config_T28[T28_CFG_MODE]; |
| x_limit = x_limit << 1; |
| if (x_limit <= 40) { |
| for (loop_i = 0; loop_i < x_limit; loop_i += 2) { |
| for (loop_j = 0; loop_j < BITS_PER_BYTE; loop_j++) { |
| check_mask = BIT_MASK(loop_j); |
| if (data[T37_DATA + T37_TCH_FLAG_IDX + loop_i] & |
| check_mask) |
| tch_ch++; |
| if (data[T37_DATA + T37_TCH_FLAG_IDX + loop_i + 1] & |
| check_mask) |
| tch_ch++; |
| if (data[T37_DATA + T37_ATCH_FLAG_IDX + loop_i] & |
| check_mask) |
| atch_ch++; |
| if (data[T37_DATA + T37_ATCH_FLAG_IDX + loop_i + 1] & |
| check_mask) |
| atch_ch++; |
| } |
| } |
| } |
| } |
| i2c_atmel_write_byte_data(ts->client, |
| get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + |
| T6_CFG_DIAG, T6_CFG_DIAG_CMD_PAGEUP); |
| |
| if (tch_ch && (atch_ch == 0)) { |
| if (jiffies > (ts->timestamp + HZ/2) && (ts->calibration_confirm == 1)) { |
| ts->calibration_confirm = 2; |
| } |
| if (ts->calibration_confirm < 2) |
| ts->calibration_confirm = 1; |
| ts->timestamp = jiffies; |
| } else if (atch_ch > 1 || tch_ch > 8) { |
| ts->calibration_confirm = 0; |
| i2c_atmel_write_byte_data(ts->client, |
| get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + |
| T6_CFG_CALIBRATE, 0x55); |
| } |
| } |
| |
| static void confirm_calibration(struct atmel_ts_data *ts) |
| { |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + |
| T8_CFG_TCHAUTOCAL, ts->ATCH_NOR_20S, 6); |
| ts->pre_data[0] = 2; |
| } |
| |
| static void msg_process_finger_data_x10y10bit(struct atmel_finger_data *fdata, uint8_t *data) |
| { |
| fdata->x = data[T9_MSG_XPOSMSB] << 2 | data[T9_MSG_XYPOSLSB] >> 6; |
| fdata->y = data[T9_MSG_YPOSMSB] << 2 | (data[T9_MSG_XYPOSLSB] & 0x0C) >>2; |
| fdata->w = data[T9_MSG_TCHAREA]; |
| fdata->z = data[T9_MSG_TCHAMPLITUDE]; |
| } |
| static void msg_process_finger_data_x10y12bit(struct atmel_finger_data *fdata, uint8_t *data) |
| { |
| fdata->x = data[T9_MSG_XPOSMSB] << 2 | data[T9_MSG_XYPOSLSB] >> 6; |
| fdata->y = data[T9_MSG_YPOSMSB] << 4 | (data[T9_MSG_XYPOSLSB] & 0x0F) ; |
| fdata->w = data[T9_MSG_TCHAREA]; |
| fdata->z = data[T9_MSG_TCHAMPLITUDE]; |
| } |
| |
| static void msg_process_multitouch(struct atmel_ts_data *ts, uint8_t *data, uint8_t idx) |
| { |
| if (ts->calibration_confirm < 2 && ts->id->version == 0x10) |
| check_calibration(ts); |
| if(ts->abs_y_max >= 1024) { |
| msg_process_finger_data_x10y12bit(&ts->finger_data[idx], data); |
| } else { |
| msg_process_finger_data_x10y10bit(&ts->finger_data[idx], data); |
| } |
| if (data[T9_MSG_STATUS] & T9_MSG_STATUS_RELEASE) { |
| if (ts->grip_suppression & BIT(idx)) |
| ts->grip_suppression &= ~BIT(idx); |
| if (ts->finger_pressed & BIT(idx)) { |
| ts->finger_count--; |
| ts->finger_pressed &= ~BIT(idx); |
| if (!ts->first_pressed) { |
| if (!ts->finger_count) |
| ts->first_pressed = 1; |
| } |
| if (ts->pre_data[0] < 2 && ts->unlock_flag != 1) { |
| |
| if (ts->finger_count) { |
| i2c_atmel_write_byte_data(ts->client, |
| get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + |
| T6_CFG_CALIBRATE, 0x55); |
| } else if (!ts->finger_count && ts->pre_data[0] == 1) |
| ts->pre_data[0] = 0; |
| } |
| } |
| } else if ((data[T9_MSG_STATUS] & (T9_MSG_STATUS_DETECT | T9_MSG_STATUS_PRESS)) |
| && !(ts->finger_pressed & BIT(idx))) { |
| if (ts->id->version >= 0x10 && ts->pre_data[0] < 2) { |
| if (jiffies > (ts->timestamp + 20 * HZ)) { |
| confirm_calibration(ts); |
| } |
| } |
| if (!(ts->grip_suppression & BIT(idx))) { |
| ts->finger_count++; |
| ts->finger_pressed |= BIT(idx); |
| if (ts->id->version >= 0x10 && ts->pre_data[0] < 2) { |
| ts->pre_data[idx + 1] = ts->finger_data[idx].x; |
| ts->pre_data[idx + 2] = ts->finger_data[idx].y; |
| if (ts->finger_count == ts->finger_support) { |
| i2c_atmel_write_byte_data(ts->client, |
| get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + |
| T6_CFG_CALIBRATE, 0x55); |
| } else if (!ts->pre_data[0] && ts->finger_count == 1) |
| ts->pre_data[0] = 1; |
| } |
| } |
| } else if ((data[T9_MSG_STATUS] & (T9_MSG_STATUS_DETECT|T9_MSG_STATUS_PRESS)) |
| && ts->pre_data[0] < 2 && ts->unlock_flag != 1) { |
| if (ts->finger_count == 1 && ts->pre_data[0] && |
| (idx == 0 && ((abs(ts->finger_data[idx].y - ts->pre_data[idx + 2]) > 50) |
| || (abs(ts->finger_data[idx].x - ts->pre_data[idx + 1]) > 50)))) |
| { |
| ts->unlock_flag = 1; |
| ts->calibration_confirm = 2; |
| } |
| } |
| |
| } |
| |
| static void compatible_input_report(struct input_dev *idev, |
| struct atmel_finger_data *fdata, uint8_t press, uint8_t last) |
| { |
| if (!press) { |
| input_mt_sync(idev); |
| /*input_report_key(idev, BTN_TOUCH, 0);*/ |
| input_report_key(idev, BTN_TOUCH, 1); |
| |
| } else { |
| TS_DEBUG_ATMEL("k3ts, %s: Touch report_key x = %d, y = %d, z = %d, w = %d\n ", __func__, |
| fdata->x, fdata->y, fdata->z, fdata->w); |
| input_report_abs(idev, ABS_MT_TOUCH_MAJOR, fdata->z); |
| input_report_abs(idev, ABS_MT_WIDTH_MAJOR, fdata->w); |
| input_report_abs(idev, ABS_MT_POSITION_X, fdata->x); |
| input_report_abs(idev, ABS_MT_POSITION_Y, fdata->y); |
| input_mt_sync(idev); |
| } |
| } |
| |
| |
| |
| static void multi_input_report(struct atmel_ts_data *ts) |
| { |
| uint8_t loop_i, finger_report = 0; |
| |
| for (loop_i = 0; loop_i < ts->finger_support; loop_i++) { |
| if (ts->finger_pressed & BIT(loop_i)) { |
| compatible_input_report(ts->input_dev, &ts->finger_data[loop_i], |
| 1, (ts->finger_count == ++finger_report)); |
| } |
| } |
| } |
| static irqreturn_t atmel_interrupt_fun(int irq, void *dev_id) |
| { |
| int ret; |
| struct atmel_ts_data *ts = dev_id; |
| uint8_t data[7]; |
| int8_t report_type; |
| uint8_t msg_byte_num = 7; |
| |
| memset(data, 0x0, sizeof(data)); |
| |
| mutex_lock(&ts->lock); |
| ret = i2c_atmel_read(ts->client, get_object_address(ts, |
| GEN_MESSAGEPROCESSOR_T5), data, 7); |
| |
| report_type = data[MSG_RID] - ts->finger_type; |
| if (report_type >= 0 && report_type < ts->finger_support) { |
| msg_process_multitouch(ts, data, report_type); |
| } else { |
| if (data[MSG_RID] == get_rid(ts, GEN_COMMANDPROCESSOR_T6)) { |
| if (data[1] & 0x10) { |
| ts->timestamp = jiffies; |
| } |
| if (data[1] & 0x80) { |
| msleep(100); |
| |
| i2c_atmel_write_byte_data(ts->client, |
| get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + |
| T6_CFG_CALIBRATE, 0x55); |
| } |
| msg_byte_num = 5; |
| } |
| if (data[MSG_RID] == get_rid(ts, PROCI_TOUCHSUPPRESSION_T42)) { |
| if (ts->calibration_confirm < 2 && ts->id->version == 0x10) { |
| i2c_atmel_write_byte_data(ts->client, |
| get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + |
| T6_CFG_CALIBRATE, 0x55); |
| } |
| ts->face_suppression = data[T20_MSG_STATUS]; |
| printk(KERN_INFO "Touch Face suppression %s: ", |
| ts->face_suppression ? "Active" : "Inactive"); |
| msg_byte_num = 2; |
| } |
| } |
| if (!ts->finger_count || ts->face_suppression) { |
| ts->finger_pressed = 0; |
| ts->finger_count = 0; |
| compatible_input_report(ts->input_dev, NULL, 0, 1); |
| } else { |
| multi_input_report(ts); |
| } |
| input_sync(ts->input_dev); |
| mutex_unlock(&ts->lock); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int read_object_table(struct atmel_ts_data *ts) |
| { |
| uint8_t i, type_count = 0; |
| uint8_t data[6]; |
| memset(data, 0x0, sizeof(data)); |
| |
| ts->object_table = kzalloc(sizeof(struct object_t)*ts->id->num_declared_objects, GFP_KERNEL); |
| if (ts->object_table == NULL) { |
| dev_err(&ts->client->dev, "k3ts, %s: allocate object_table failed\n", __func__); |
| return -ENOMEM; |
| } |
| |
| for (i = 0; i < ts->id->num_declared_objects; i++) { |
| i2c_atmel_read(ts->client, i * 6 + 0x07, data, 6); |
| ts->object_table[i].object_type = data[OBJ_TABLE_TYPE]; |
| ts->object_table[i].i2c_address = |
| data[OBJ_TABLE_LSB] | data[OBJ_TABLE_MSB] << 8; |
| ts->object_table[i].size = data[OBJ_TABLE_SIZE] + 1; |
| ts->object_table[i].instances = data[OBJ_TABLE_INSTANCES]; |
| ts->object_table[i].num_report_ids = data[OBJ_TABLE_RIDS]; |
| if (data[OBJ_TABLE_RIDS]) { |
| ts->object_table[i].report_ids = type_count + 1; |
| type_count += data[OBJ_TABLE_RIDS]; |
| } |
| if (data[OBJ_TABLE_TYPE] == TOUCH_MULTITOUCHSCREEN_T9) |
| ts->finger_type = ts->object_table[i].report_ids; |
| } |
| |
| return 0; |
| } |
| |
| struct atmel_i2c_platform_data *atmel_ts_get_pdata(struct i2c_client *client) |
| { |
| struct device_node *node = client->dev.of_node; |
| struct atmel_i2c_platform_data *pdata = client->dev.platform_data; |
| u32 data[8]; |
| |
| if (pdata) |
| return pdata; |
| |
| if (!node) |
| return NULL; |
| |
| pdata = devm_kzalloc(&client->dev, sizeof(struct atmel_i2c_platform_data), |
| GFP_KERNEL); |
| pdata->gpio_irq = of_get_named_gpio(node, "atmel-ts,gpio-irq", 0); |
| pdata->gpio_reset = of_get_named_gpio(node, "atmel-ts,gpio-reset", 0); |
| |
| of_property_read_u32_array(node, "atmel-ts,abs", &data[0], 8); |
| pdata->abs_x_min = data[0]; |
| pdata->abs_x_max = data[1]; |
| pdata->abs_y_min = data[2]; |
| pdata->abs_y_max = data[3]; |
| pdata->abs_pressure_min = data[4]; |
| pdata->abs_pressure_max = data[5]; |
| pdata->abs_width_min = data[6]; |
| pdata->abs_width_max = data[7]; |
| |
| of_property_read_u8_array(node, "atmel-ts,cfg_t6", &pdata->config_T6[0], 6); |
| of_property_read_u8_array(node, "atmel-ts,cfg_t7", &pdata->config_T7[0], 3); |
| of_property_read_u8_array(node, "atmel-ts,cfg_t8", &pdata->config_T8[0], 10); |
| of_property_read_u8_array(node, "atmel-ts,cfg_t9", &pdata->config_T9[0], 35); |
| of_property_read_u8_array(node, "atmel-ts,cfg_t15", &pdata->config_T15[0], 11); |
| of_property_read_u8_array(node, "atmel-ts,cfg_t19", &pdata->config_T19[0], 16); |
| of_property_read_u8_array(node, "atmel-ts,cfg_t23", &pdata->config_T23[0], 15); |
| of_property_read_u8_array(node, "atmel-ts,cfg_t25", &pdata->config_T25[0], 14); |
| of_property_read_u8_array(node, "atmel-ts,cfg_t40", &pdata->config_T40[0], 5); |
| of_property_read_u8_array(node, "atmel-ts,cfg_t42", &pdata->config_T42[0], 8); |
| of_property_read_u8_array(node, "atmel-ts,cfg_t46", &pdata->config_T46[0], 9); |
| of_property_read_u8_array(node, "atmel-ts,cfg_t47", &pdata->config_T47[0], 10); |
| of_property_read_u8_array(node, "atmel-ts,cfg_t48", &pdata->config_T48[0], 54); |
| of_property_read_u8_array(node, "atmel-ts,object_crc", &pdata->object_crc[0], 3); |
| of_property_read_u8_array(node, "atmel-ts,cable_config", &pdata->cable_config[0], 4); |
| of_property_read_u8_array(node, "atmel-ts,cable_config_t7", &pdata->cable_config_T7[0], 3); |
| of_property_read_u8_array(node, "atmel-ts,cable_config_t8", &pdata->cable_config_T8[0], 10); |
| of_property_read_u8_array(node, "atmel-ts,cable_config_t46", &pdata->cable_config_T46[0], 9); |
| of_property_read_u8_array(node, "atmel-ts,cable_config_t48", &pdata->cable_config_T48[0], 54); |
| of_property_read_u8_array(node, "atmel-ts,noise_config", &pdata->noise_config[0], 3); |
| of_property_read_u16_array(node, "atmel-ts,filter_level", &pdata->filter_level[0], 4); |
| of_property_read_u8_array(node, "atmel-ts,gcaf_level", &pdata->GCAF_level[0], 5); |
| of_property_read_u8_array(node, "atmel-ts,atch_nor", &pdata->ATCH_NOR[0], 6); |
| of_property_read_u8_array(node, "atmel-ts,atch_nor_20s", &pdata->ATCH_NOR_20S[0], 6); |
| |
| return pdata; |
| } |
| |
| static int atmel_ts_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| struct atmel_ts_data *ts; |
| struct atmel_i2c_platform_data *pdata; |
| int ret = 0, intr = 0; |
| uint8_t loop_i; |
| struct i2c_msg msg[2]; |
| uint8_t data[16]; |
| uint8_t CRC_check = 0; |
| |
| client->dev.init_name = "atmel-ts"; |
| LDO = regulator_get(&client->dev, "ldo"); |
| if (IS_ERR(LDO)) { |
| dev_err(&client->dev, "no regulator found\n"); |
| LDO = NULL; |
| } else { |
| ret = regulator_enable(LDO); |
| if (!ret) |
| ret = regulator_set_voltage(LDO, LDO_POWR_VOLTAGE, LDO_POWR_VOLTAGE); |
| if (ret) |
| dev_err(&client->dev, "k3ts, %s: failed to set LDO\n", __func__); |
| } |
| |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| dev_err(&client->dev, "k3ts, %s: need I2C_FUNC_I2C\n", __func__); |
| ret = -ENODEV; |
| goto err_check_functionality_failed; |
| } |
| |
| ts = kzalloc(sizeof(struct atmel_ts_data), GFP_KERNEL); |
| if (ts == NULL) { |
| ret = -ENOMEM; |
| goto err_alloc_data_failed; |
| } |
| |
| ts->unlock_flag = 0; |
| mutex_init(&ts->lock); |
| |
| ts->atmel_wq = create_singlethread_workqueue("atmel_wq"); |
| if (!ts->atmel_wq) { |
| dev_err(&client->dev, "k3ts, %s: create workqueue failed\n", __func__); |
| ret = -ENOMEM; |
| goto err_cread_wq_failed; |
| } |
| |
| ts->client = client; |
| i2c_set_clientdata(client, ts); |
| |
| pdata = atmel_ts_get_pdata(client); |
| ts->pdata = pdata; |
| |
| if (pdata) { |
| ts->power = pdata->power; |
| intr = pdata->gpio_irq; |
| client->irq = gpio_to_irq(intr); |
| } |
| if (ts->power) |
| ret = ts->power(1); |
| ret = gpio_request(intr, "gpio_tp_intr"); |
| if (ret) { |
| dev_err(&client->dev, "gpio_request %d failed\n", intr); |
| goto err_request_gpio_failed; |
| } |
| ret = gpio_direction_input(intr); |
| if (ret) { |
| dev_err(&client->dev, "k3ts, %s: gpio_direction_input failed %d\n", __func__, intr); |
| goto err_gpio_direction_failed; |
| } |
| |
| ret = gpio_request(ts->pdata->gpio_reset, "gpio_tp_reset"); |
| if (ret) { |
| dev_err(&client->dev, "k3ts, %s: gpio_request failed %d, ret = %d\n", |
| __func__, ts->pdata->gpio_reset, ret); |
| goto err_request_gpio_reset_failed; |
| } |
| |
| gpio_direction_output(ts->pdata->gpio_reset, 1); |
| mdelay(5); |
| gpio_direction_output(ts->pdata->gpio_reset, 0); |
| mdelay(10); |
| gpio_direction_output(ts->pdata->gpio_reset, 1); |
| mdelay(50); |
| |
| for (loop_i = 0; loop_i < 10; loop_i++) { |
| if (!gpio_get_value(intr)) |
| break; |
| msleep(10); |
| } |
| |
| if (loop_i == 10) |
| dev_err(&client->dev, "k3ts, %s: No Messages\n", __func__); |
| |
| /* read message*/ |
| msg[0].addr = ts->client->addr; |
| msg[0].flags = I2C_M_RD; |
| msg[0].len = 7; |
| msg[0].buf = data; |
| ret = i2c_transfer(client->adapter, msg, 1); |
| |
| if (ret < 0) { |
| dev_err(&client->dev, "k3ts, %s: No Atmel chip inside\n", __func__); |
| goto err_detect_failed; |
| } |
| if (ts->power) |
| ret = ts->power(2); |
| |
| if (data[MSG_RID] == 0x01 && |
| (data[T6_MSG_STATUS] & (T6_MSG_STATUS_SIGERR|T6_MSG_STATUS_COMSERR))) { |
| dev_err(&client->dev, "k3ts, %s: init err: %x\n", __func__, data[1]); |
| goto err_detect_failed; |
| } else { |
| for (loop_i = 0; loop_i < 10; loop_i++) { |
| if (gpio_get_value(intr)) { |
| dev_err(&client->dev, "k3ts, %s: No more message\n", __func__); |
| break; |
| } |
| ret = i2c_transfer(client->adapter, msg, 1); |
| msleep(10); |
| } |
| } |
| |
| /* Read the info block data. */ |
| ts->id = kzalloc(sizeof(struct info_id_t), GFP_KERNEL); |
| if (ts->id == NULL) { |
| dev_err(&client->dev, "k3ts, %s: allocate info_id_t failed\n", __func__); |
| goto err_alloc_failed; |
| } |
| ret = i2c_atmel_read(client, 0x00, data, 7); |
| |
| ts->id->family_id = data[INFO_BLK_FID]; |
| ts->id->variant_id = data[INFO_BLK_VID]; |
| if (ts->id->family_id == 0x80 && ts->id->variant_id == 0x10) |
| ts->id->version = data[INFO_BLK_VER] + 6; |
| else |
| ts->id->version = data[INFO_BLK_VER]; |
| |
| ts->id->build = data[INFO_BLK_BUILD]; |
| ts->id->matrix_x_size = data[INFO_BLK_XSIZE]; |
| ts->id->matrix_y_size = data[INFO_BLK_YSIZE]; |
| ts->id->num_declared_objects = data[INFO_BLK_OBJS]; |
| |
| /* Read object table. */ |
| ret = read_object_table(ts); |
| if (ret < 0) |
| goto err_read_table_failed; |
| |
| |
| if (pdata) { |
| ts->finger_support = pdata->config_T9[T9_CFG_NUMTOUCH]; |
| |
| /* OBJECT CONFIG CRC check */ |
| if (pdata->object_crc[0]) { |
| ret = i2c_atmel_write_byte_data(client, |
| get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + |
| T6_CFG_CALIBRATE, 0x55); |
| for (loop_i = 0; loop_i < 10; loop_i++) { |
| if (!gpio_get_value(intr)) { |
| ret = i2c_atmel_read(ts->client, get_object_address(ts, |
| GEN_MESSAGEPROCESSOR_T5), data, 5); |
| if (data[MSG_RID] == get_rid(ts, GEN_COMMANDPROCESSOR_T6)) |
| break; |
| } |
| msleep(10); |
| } |
| if (loop_i == 10) |
| dev_err(&client->dev, "k3ts, %s: No checksum read\n", __func__); |
| else { |
| dev_info(&client->dev, "k3ts, %s: CRC print : %x, %x, %x\n", __func__, |
| data[T6_MSG_CHECKSUM + 0], data[T6_MSG_CHECKSUM + 1], data[T6_MSG_CHECKSUM + 2]); |
| for (loop_i = 0; loop_i < 3; loop_i++) { |
| if (pdata->object_crc[loop_i] != data[T6_MSG_CHECKSUM + loop_i]) { |
| dev_err(&client->dev, |
| "k3ts, %s: CRC Error: %x, %x\n", __func__, |
| pdata->object_crc[loop_i], |
| data[T6_MSG_CHECKSUM + loop_i]); |
| break; |
| } |
| } |
| if (loop_i == 3) { |
| dev_info(&client->dev, "k3ts, %s: CRC passed: ", __func__); |
| for (loop_i = 0; loop_i < 3; loop_i++) |
| pr_info("0x%2.2X ", pdata->object_crc[loop_i]); |
| pr_info("\n"); |
| CRC_check = 1;/*means CRC check OK*/ |
| } |
| } |
| } |
| ts->abs_x_min = pdata->abs_x_min; |
| ts->abs_x_max = pdata->abs_x_max; |
| ts->abs_y_min = pdata->abs_y_min; |
| ts->abs_y_max = pdata->abs_y_max; |
| ts->abs_pressure_min = pdata->abs_pressure_min; |
| ts->abs_pressure_max = pdata->abs_pressure_max; |
| ts->abs_width_min = pdata->abs_width_min; |
| ts->abs_width_max = pdata->abs_width_max; |
| |
| ts->GCAF_level = pdata->GCAF_level; |
| if (ts->id->version >= 0x10) { |
| ts->ATCH_EXT = &pdata->config_T8[6]; |
| ts->timestamp = jiffies + 60 * HZ; |
| } |
| ts->ATCH_NOR = pdata->ATCH_NOR; |
| ts->ATCH_NOR_20S = pdata->ATCH_NOR_20S; |
| ts->filter_level = pdata->filter_level; |
| |
| ts->config_setting[NONE].config_T7 |
| = ts->config_setting[CONNECTED].config_T7 |
| = pdata->config_T7; |
| ts->config_setting[NONE].config_T8 = pdata->config_T8; |
| ts->config_setting[CONNECTED].config_T8 = pdata->cable_config_T8; |
| ts->config_setting[NONE].config_T9 = pdata->config_T9; |
| ts->config_setting[NONE].config_T22 = pdata->config_T22; |
| ts->config_setting[NONE].config_T28 = pdata->config_T28; |
| ts->config_setting[NONE].config_T46 = pdata->config_T46; |
| ts->config_setting[NONE].config_T48 = pdata->config_T48; |
| ts->config_setting[CONNECTED].config_T46 = pdata->cable_config_T46; |
| ts->config_setting[CONNECTED].config_T48 = pdata->cable_config_T48; |
| |
| if (pdata->noise_config[0]) |
| for (loop_i = 0; loop_i < 3; loop_i++) |
| ts->noise_config[loop_i] = pdata->noise_config[loop_i]; |
| |
| if (pdata->cable_config[0]) { |
| ts->config_setting[NONE].config[CB_TCHTHR] = |
| pdata->config_T9[T9_CFG_TCHTHR]; |
| ts->config_setting[NONE].config[CB_NOISETHR] = |
| pdata->config_T22[T22_CFG_NOISETHR]; |
| ts->config_setting[NONE].config[CB_IDLEGCAFDEPTH] = |
| pdata->config_T28[T28_CFG_IDLEGCAFDEPTH]; |
| ts->config_setting[NONE].config[CB_ACTVGCAFDEPTH] = |
| pdata->config_T28[T28_CFG_ACTVGCAFDEPTH]; |
| for (loop_i = 0; loop_i < 4; loop_i++) |
| ts->config_setting[CONNECTED].config[loop_i] = |
| pdata->cable_config[loop_i]; |
| ts->GCAF_sample = |
| ts->config_setting[CONNECTED].config[CB_ACTVGCAFDEPTH]; |
| if (ts->id->version >= 0x20) |
| ts->noisethr = pdata->cable_config[CB_TCHTHR] - |
| pdata->config_T9[T9_CFG_TCHHYST]; |
| else |
| ts->noisethr = pdata->cable_config[CB_TCHTHR]; |
| ts->noisethr_config = |
| ts->config_setting[CONNECTED].config[CB_NOISETHR]; |
| } else { |
| if (pdata->cable_config_T7[0]) |
| ts->config_setting[CONNECTED].config_T7 = |
| pdata->cable_config_T7; |
| if (pdata->cable_config_T8[0]) |
| ts->config_setting[CONNECTED].config_T8 = |
| pdata->cable_config_T8; |
| if (pdata->cable_config_T9[0]) { |
| ts->config_setting[CONNECTED].config_T9 = |
| pdata->cable_config_T9; |
| ts->config_setting[CONNECTED].config_T22 = |
| pdata->cable_config_T22; |
| ts->config_setting[CONNECTED].config_T28 = |
| pdata->cable_config_T28; |
| ts->GCAF_sample = |
| ts->config_setting[CONNECTED].config_T28[T28_CFG_ACTVGCAFDEPTH]; |
| } |
| if (ts->status == CONNECTED) |
| ts->noisethr = (ts->id->version >= 0x20) ? |
| pdata->cable_config_T9[T9_CFG_TCHTHR] - pdata->cable_config_T9[T9_CFG_TCHHYST] : |
| pdata->cable_config_T9[T9_CFG_TCHTHR]; |
| else |
| ts->noisethr = (ts->id->version >= 0x20) ? |
| pdata->config_T9[T9_CFG_TCHTHR] - pdata->config_T9[T9_CFG_TCHHYST] : |
| pdata->config_T9[T9_CFG_TCHTHR]; |
| ts->noisethr_config = pdata->cable_config_T22[T22_CFG_NOISETHR]; |
| |
| } |
| |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, GEN_COMMANDPROCESSOR_T6), |
| pdata->config_T6, |
| get_object_size(ts, GEN_COMMANDPROCESSOR_T6)); |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, GEN_POWERCONFIG_T7), |
| pdata->config_T7, |
| get_object_size(ts, GEN_POWERCONFIG_T7)); |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, GEN_ACQUISITIONCONFIG_T8), |
| pdata->config_T8, |
| get_object_size(ts, GEN_ACQUISITIONCONFIG_T8)); |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9), |
| pdata->config_T9, |
| get_object_size(ts, TOUCH_MULTITOUCHSCREEN_T9)); |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, TOUCH_KEYARRAY_T15), |
| pdata->config_T15, |
| get_object_size(ts, TOUCH_KEYARRAY_T15)); |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, SPT_GPIOPWM_T19), |
| pdata->config_T19, |
| get_object_size(ts, SPT_GPIOPWM_T19)); |
| |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, PROCI_GRIPSUPPRESSION_T40), |
| pdata->config_T40, |
| get_object_size(ts, PROCI_GRIPSUPPRESSION_T40)); |
| |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, PROCI_TOUCHSUPPRESSION_T42), |
| pdata->config_T42, |
| get_object_size(ts, PROCI_TOUCHSUPPRESSION_T42)); |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, PROCG_NOISESUPPRESSION_T48), |
| pdata->config_T48, |
| get_object_size(ts, PROCG_NOISESUPPRESSION_T48)); |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, TOUCH_PROXIMITY_T23), |
| pdata->config_T23, |
| get_object_size(ts, TOUCH_PROXIMITY_T23)); |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, SPT_SELFTEST_T25), |
| pdata->config_T25, |
| get_object_size(ts, SPT_SELFTEST_T25)); |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, SPT_CTECONFIG_T46), |
| pdata->config_T46, |
| get_object_size(ts, SPT_CTECONFIG_T46)); |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, PROCI_STYLUS_T47), |
| pdata->config_T47, |
| get_object_size(ts, PROCI_STYLUS_T47)); |
| |
| ret = i2c_atmel_write_byte_data(client, |
| get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + |
| T6_CFG_BACKUPNV, 0x55); |
| |
| for (loop_i = 0; loop_i < 10; loop_i++) { |
| if (!gpio_get_value(intr)) |
| break; |
| dev_err(&client->dev, "k3ts, %s: wait for Message(%d)\n", __func__, loop_i + 1); |
| msleep(10); |
| } |
| |
| i2c_atmel_read(client, |
| get_object_address(ts, GEN_MESSAGEPROCESSOR_T5), data, 7); |
| |
| ret = i2c_atmel_write_byte_data(client, |
| get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + |
| T6_CFG_RESET, 0x11);/*reset*/ |
| msleep(100); |
| |
| if (ts->status == CONNECTED) { |
| if (ts->config_setting[CONNECTED].config_T8 != NULL) |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, GEN_ACQUISITIONCONFIG_T8), |
| ts->config_setting[CONNECTED].config_T8, |
| get_object_size(ts, GEN_ACQUISITIONCONFIG_T8)); |
| if (ts->config_setting[CONNECTED].config_T46 != NULL) |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, SPT_CTECONFIG_T46), |
| ts->config_setting[CONNECTED].config_T46, |
| get_object_size(ts, SPT_CTECONFIG_T46)); |
| if (ts->config_setting[CONNECTED].config_T48 != NULL) { |
| i2c_atmel_write(ts->client, |
| get_object_address(ts, PROCG_NOISESUPPRESSION_T48), |
| ts->config_setting[CONNECTED].config_T48, |
| get_object_size(ts, PROCG_NOISESUPPRESSION_T48)); |
| } |
| } |
| } |
| ts->calibration_confirm = 0; |
| ts->input_dev = input_allocate_device(); |
| if (ts->input_dev == NULL) { |
| ret = -ENOMEM; |
| dev_err(&client->dev, "k3ts, %s: Failed to allocate input device\n", __func__); |
| goto err_input_dev_alloc_failed; |
| } |
| /*Modified by z181527 for Debug Only*/ |
| ts->input_dev->name = "synaptics"/*"atmel-touchscreen"*/; |
| set_bit(EV_SYN, ts->input_dev->evbit); |
| set_bit(EV_KEY, ts->input_dev->evbit); |
| set_bit(BTN_TOUCH, ts->input_dev->keybit); |
| set_bit(BTN_2, ts->input_dev->keybit); |
| set_bit(EV_ABS, ts->input_dev->evbit); |
| set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); |
| input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, |
| ts->abs_x_min, ts->abs_x_max, 0, 0); |
| input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, |
| ts->abs_y_min, ts->abs_y_max, 0, 0); |
| input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, |
| ts->abs_pressure_min, ts->abs_pressure_max, |
| 0, 0); |
| input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, |
| ts->abs_width_min, ts->abs_width_max, 0, 0); |
| |
| ret = input_register_device(ts->input_dev); |
| if (ret) { |
| dev_err(&client->dev, |
| "k3ts, %s: atmel_ts_probe: Unable to register %s input device\n", __func__, |
| ts->input_dev->name); |
| goto err_input_register_device_failed; |
| } |
| |
| ret = request_threaded_irq(client->irq, NULL, atmel_interrupt_fun, |
| IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
| client->name, ts); |
| if (ret) |
| dev_err(&client->dev, "k3ts, %s: request_irq failed\n", __func__); |
| |
| private_ts = ts; |
| |
| dev_info(&client->dev, "k3ts, %s: probe %s successfully\n", __func__, |
| ts->input_dev->name); |
| |
| return 0; |
| |
| err_input_register_device_failed: |
| input_free_device(ts->input_dev); |
| err_input_dev_alloc_failed: |
| err_read_table_failed: |
| kfree(ts->id); |
| err_alloc_failed: |
| err_detect_failed: |
| err_gpio_direction_failed: |
| err_request_gpio_reset_failed: |
| gpio_free(ts->pdata->gpio_reset); |
| gpio_free(intr); |
| err_request_gpio_failed: |
| destroy_workqueue(ts->atmel_wq); |
| err_cread_wq_failed: |
| kfree(ts); |
| err_alloc_data_failed: |
| err_check_functionality_failed: |
| if (LDO != NULL) { |
| regulator_disable(LDO); |
| regulator_put(LDO); |
| } |
| return ret; |
| } |
| |
| static int atmel_ts_remove(struct i2c_client *client) |
| { |
| struct atmel_ts_data *ts = i2c_get_clientdata(client); |
| |
| free_irq(client->irq, ts); |
| |
| destroy_workqueue(ts->atmel_wq); |
| input_unregister_device(ts->input_dev); |
| kfree(ts); |
| |
| regulator_disable(LDO); |
| regulator_put(LDO); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_PM_SLEEP |
| static int atmel_ts_suspend(struct device *dev) |
| { |
| struct i2c_client *client = to_i2c_client(dev); |
| struct atmel_ts_data *ts = i2c_get_clientdata(client); |
| struct atmel_i2c_platform_data *pdata = ts->pdata; |
| uint8_t data[7]; |
| int ret = 0; |
| |
| mutex_lock(&ts->lock); |
| ts->finger_pressed = 0; |
| ts->finger_count = 0; |
| ts->first_pressed = 0; |
| |
| if (ts->id->version >= 0x10) { |
| ts->pre_data[0] = 0; |
| ret = i2c_atmel_write(ts->client, |
| get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + T8_CFG_ATCHCALST, |
| ts->ATCH_EXT, 4); |
| if (ret < 0) |
| pr_err("k3ts, %s: failed to write config T8\n", __func__); |
| } |
| |
| ret = i2c_atmel_write_byte_data(client, |
| get_object_address(ts, GEN_POWERCONFIG_T7) + T7_CFG_IDLEACQINT, 0x0); |
| if (ret < 0) |
| pr_err("k3ts, %s: failed to write config T7\n", __func__); |
| |
| ret = i2c_atmel_write_byte_data(client, |
| get_object_address(ts, GEN_POWERCONFIG_T7) + T7_CFG_ACTVACQINT, 0x0); |
| if (ret < 0) |
| pr_err("k3ts, %s: failed to write config T7\n", __func__); |
| |
| /* Read T5 until gpio_irq is HIGH level */ |
| if (!gpio_get_value(pdata->gpio_irq)) { |
| ret = i2c_atmel_read(ts->client, get_object_address(ts, |
| GEN_MESSAGEPROCESSOR_T5), data, 7); |
| if (ret < 0) { |
| pr_err("k3ts, %s: failed to read T5\n", __func__); |
| } |
| } |
| |
| mutex_unlock(&ts->lock); |
| |
| pr_info("[%s]: -\n", __func__); |
| return 0; |
| } |
| |
| static int atmel_ts_resume(struct device *dev) |
| { |
| struct i2c_client *client = to_i2c_client(dev); |
| struct atmel_ts_data *ts = i2c_get_clientdata(client); |
| int ret = 0; |
| |
| pr_info("[%s]: +\n", __func__); |
| |
| mutex_lock(&ts->lock); |
| if (ts->id->version >= 0x10) |
| ts->timestamp = jiffies; |
| |
| ts->unlock_flag = 0; |
| |
| ret = i2c_atmel_write(ts->client, |
| get_object_address(ts, GEN_POWERCONFIG_T7), |
| ts->config_setting[ts->status].config_T7, |
| get_object_size(ts, GEN_POWERCONFIG_T7)); |
| if (ret < 0) |
| pr_err("k3ts, %s: failed to write config T7\n", __func__); |
| |
| ts->calibration_confirm = 0; |
| msleep(1); |
| |
| ret = i2c_atmel_write(ts->client, |
| get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + |
| T8_CFG_TCHAUTOCAL, ts->ATCH_NOR, 6); |
| if (ret < 0) |
| pr_err("k3ts, %s: failed to write config T8\n", __func__); |
| |
| ret = i2c_atmel_write_byte_data(client, |
| get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + |
| T6_CFG_CALIBRATE, 0x55); |
| if (ret < 0) |
| pr_err("k3ts, %s: failed to write config T6\n", __func__); |
| |
| mutex_unlock(&ts->lock); |
| |
| pr_info("[%s]: -\n", __func__); |
| |
| return 0; |
| } |
| #endif |
| static SIMPLE_DEV_PM_OPS(atmel_ts_pm_ops, atmel_ts_suspend, atmel_ts_resume); |
| |
| static const struct i2c_device_id atml_ts_i2c_id[] = { |
| { ATMEL_MXT224E_NAME, 0 }, |
| { } |
| }; |
| |
| #ifdef CONFIG_OF |
| static const struct of_device_id atmel_ts_dt_ids[] = { |
| { .compatible = "atmel,ts-mxt224e", }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(of, atmel_ts_dt_ids); |
| #endif |
| |
| static struct i2c_driver atmel_ts_driver = { |
| .id_table = atml_ts_i2c_id, |
| .probe = atmel_ts_probe, |
| .remove = atmel_ts_remove, |
| .driver = { |
| .of_match_table = of_match_ptr(atmel_ts_dt_ids), |
| .name = ATMEL_MXT224E_NAME, |
| .pm = &atmel_ts_pm_ops, |
| }, |
| }; |
| module_i2c_driver(atmel_ts_driver); |
| |
| MODULE_DESCRIPTION("ATMEL Touch driver"); |
| MODULE_LICENSE("GPL"); |