diff options
Diffstat (limited to 'drivers/input/touchscreen/synaptics_dsx_i2c.c')
| -rw-r--r-- | drivers/input/touchscreen/synaptics_dsx_i2c.c | 7992 |
1 files changed, 7992 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx_i2c.c new file mode 100644 index 00000000..b0e331ce --- /dev/null +++ b/drivers/input/touchscreen/synaptics_dsx_i2c.c @@ -0,0 +1,7992 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> + * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/gpio.h> +#include <linux/ctype.h> +#include <linux/jiffies.h> +#include <linux/semaphore.h> +#include <linux/regulator/consumer.h> +#include <linux/reboot.h> +#include <linux/input/synaptics_rmi_dsx.h> +#include <linux/perftags.h> + +#include "./tpd_property/tpd_property.h" +#include "synaptics_dsx_i2c.h" +#include "synaptics_dsx_control_access_block.h" +#ifdef CONFIG_TOUCHSCREEN_TOUCHX_BASE +#include "touchx.h" +#endif +#include <linux/pinctrl/consumer.h> +#include <linux/mmi_hall_notifier.h> +#ifdef KERNEL_ABOVE_2_6_38 +#include <linux/input/mt.h> +#endif + +#define DRIVER_NAME "synaptics_dsx_i2c" +#define INPUT_PHYS_NAME "synaptics_dsx_i2c/input0" +#define TYPE_B_PROTOCOL + +#define RMI4_WAIT_READY 0 +#define RMI4_HW_RESET 1 +#define RMI4_SW_RESET 2 + +#define NO_0D_WHILE_2D +/* +#define REPORT_2D_Z +*/ +#define REPORT_2D_W + +#define RPT_TYPE (1 << 0) +#define RPT_X_LSB (1 << 1) +#define RPT_X_MSB (1 << 2) +#define RPT_Y_LSB (1 << 3) +#define RPT_Y_MSB (1 << 4) +#define RPT_Z (1 << 5) +#define RPT_WX (1 << 6) +#define RPT_WY (1 << 7) +#define RPT_DEFAULT (RPT_TYPE | RPT_X_LSB | RPT_X_MSB | RPT_Y_LSB | RPT_Y_MSB) + +#define EXP_FN_DET_INTERVAL 1000 /* ms */ +#define POLLING_PERIOD 1 /* ms */ +#define SYN_I2C_RETRY_TIMES 10 +#define SYN_FPS_REGISTERED_RETRY_TIMES 15 +#define MAX_ABS_MT_TOUCH_MAJOR 15 +#define SYN_MAX_BUTTONS 4 + +#define F01_STD_QUERY_LEN 21 +#define F01_BUID_ID_OFFSET 18 +#define F01_QUERY1_OFFSET 1 +#define F01_QUERY21_OFFSET 21 +#define F11_STD_QUERY_LEN 9 +#define F11_STD_CTRL_LEN 10 +#define F11_STD_DATA_LEN 12 +#define F12_STD_QUERY_LEN 10 +#define F12_STD_CTRL_LEN 4 +#define F12_STD_DATA_LEN 80 + +#define RESET_GPIO_NAME "touch_reset" +#define IRQ_GPIO_NAME "touch_irq" +#define VIO_EN_GPIO_NAME "ts_vio_en" +#define VCI_EN_GPIO_NAME "ts_vci_en" + +#define SYDBG(fmt, args...) printk(KERN_ERR "%s: " fmt, __func__, ##args) +#define SYDBG_REG(subpkt, fld) SYDBG(#subpkt "." #fld " = 0x%02X\n", subpkt.fld) + +#define tk_debug(fmt, args...) + +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS +static int folio_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +#endif +#ifdef FPS_NOTIFIER +static int fps_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +#endif +static void synaptics_dsx_resumeinfo_start( + struct synaptics_rmi4_data *rmi4_data); +static void synaptics_dsx_resumeinfo_finish( + struct synaptics_rmi4_data *rmi4_data); +static void synaptics_dsx_resumeinfo_isr( + struct synaptics_rmi4_data *rmi4_data); +static void synaptics_dsx_resumeinfo_purgeoff( + struct synaptics_rmi4_data *rmi4_data); +static void synaptics_dsx_resumeinfo_ignore( + struct synaptics_rmi4_data *rmi4_data); +static void synaptics_dsx_resumeinfo_touch( + struct synaptics_rmi4_data *rmi4_data); +static void synaptics_dsx_free_patch( + struct synaptics_dsx_patch *patch); +static struct synaptics_dsx_patch * + synaptics_dsx_init_patch(const char *name); +static int synaptics_rmi4_set_page( + struct synaptics_rmi4_data *rmi4_data, + unsigned int address); +static int control_access_block_update_static( + struct synaptics_rmi4_data *rmi4_data); +static int control_access_block_update_dynamic( + struct synaptics_rmi4_data *rmi4_data); + +/* F12 packet register description */ +static struct { + unsigned char max_x_lsb; + unsigned char max_x_msb; + unsigned char max_y_lsb; + unsigned char max_y_msb; +} f12_c08_0; + +static struct { + unsigned char zone0_x_lsb; + unsigned char zone0_x_msb; + unsigned char zone0_y_lsb; + unsigned char zone0_y_msb; + unsigned char zone1_x_lsb; + unsigned char zone1_x_msb; + unsigned char zone1_y_lsb; + unsigned char zone1_y_msb; + unsigned char max_timer; + unsigned char max_distance; +} f12_c18_0; + +static struct f12_c20_0_type { + unsigned char x_suppression; + unsigned char y_suppression; +} f12_c20_0; + +static struct { + union { + struct { + unsigned char flags:1; + unsigned char wakeup_gesture_only:1; + unsigned char dribble:1; + unsigned char proximity_only:1; + unsigned char hover_swipe:1; + unsigned char proximity:1; + unsigned char hover_pinch:1; + unsigned char lp_proximity:1; + } __packed; + unsigned char report; + }; +} f12_c20_1; + +static struct f12_c23_0_type { + union { + struct { + unsigned char finger:1; + unsigned char stylus:1; + unsigned char palm:1; + unsigned char unclassified:1; + unsigned char Hovering:1; + unsigned char gloved:1; + unsigned char reserved:2; + } __packed; + unsigned char data[1]; + }; +} f12_c23_0; + +static struct f12_c23_1_type { + unsigned char max_num_reported_objects; +} f12_c23_1; + +static struct { + union { + struct { + unsigned char glove_det:1; + unsigned char close_cover:1; + unsigned char reserved:6; + } __packed; + unsigned char glove_setting; + }; +} f12_c26_0; + +static struct { + union { + struct { + unsigned char double_tap:1; + unsigned char swipe:1; + unsigned char tap_n_hold:1; + unsigned char circle:1; + unsigned char triangle:1; + unsigned char vee:1; + unsigned char unicode:1; + unsigned char reserved:1; + } __packed; + unsigned char wakeup_gesture; + }; +} f12_c27_0; + +static struct { + unsigned char reported_bytes_per_object; +} f12_c28_0; + +static struct { + unsigned char finger_threshold; + unsigned char small_finger_threshold; + unsigned char small_finger_border; + unsigned char negative_finger_threshold; +} f12_c15_0; + +static struct { + unsigned char noise_floor; + unsigned char min_peak_amplitude; + unsigned char peak_merge_threshold_lsb; + unsigned char peak_merge_threshold_msb; +} f12_c10_0; + +static struct { + unsigned char data[128]; +} dummy_subpkt; + +static struct synaptics_rmi4_subpkt f12_c08[] = { + RMI4_SUBPKT(f12_c08_0), +}; + +static struct synaptics_rmi4_subpkt f12_c09[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_subpkt f12_c10[] = { + RMI4_SUBPKT(f12_c10_0), +}; + +static struct synaptics_rmi4_subpkt f12_c11[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_subpkt f12_c12[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_subpkt f12_c13[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_subpkt f12_c14[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_subpkt f12_c15[] = { + RMI4_SUBPKT(f12_c15_0), +}; + +static struct synaptics_rmi4_subpkt f12_c16[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_subpkt f12_c17[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_subpkt f12_c18[] = { + RMI4_SUBPKT(f12_c18_0), +}; + +static struct synaptics_rmi4_subpkt f12_c19[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_subpkt f12_c20[] = { + RMI4_SUBPKT(f12_c20_0), + RMI4_SUBPKT(f12_c20_1), +}; +static struct synaptics_rmi4_subpkt f12_c21[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_subpkt f12_c22[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_subpkt f12_c23[] = { + RMI4_SUBPKT(f12_c23_0), + RMI4_SUBPKT(f12_c23_1), +}; + +static struct synaptics_rmi4_subpkt f12_c24[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_subpkt f12_c25[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_subpkt f12_c26[] = { + RMI4_SUBPKT(f12_c26_0), +}; + +static struct synaptics_rmi4_subpkt f12_c27[] = { + RMI4_SUBPKT(f12_c27_0), +}; + +static struct synaptics_rmi4_subpkt f12_c28[] = { + RMI4_SUBPKT(f12_c28_0), +}; + +static struct synaptics_rmi4_subpkt f12_c29[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_subpkt f12_c30[] = { + RMI4_SUBPKT(dummy_subpkt), +}; + +static struct synaptics_rmi4_packet_reg f12_ctrl_reg_array[] = { + RMI4_REG(8, f12_c08), + RMI4_REG(9, f12_c09), + RMI4_REG(10, f12_c10), + RMI4_REG(11, f12_c11), + RMI4_REG(12, f12_c12), + RMI4_REG(13, f12_c13), + RMI4_REG(14, f12_c14), + RMI4_REG(15, f12_c15), + RMI4_REG(16, f12_c16), + RMI4_REG(17, f12_c17), + RMI4_REG(18, f12_c18), + RMI4_REG(19, f12_c19), + RMI4_REG(20, f12_c20), + RMI4_REG(21, f12_c21), + RMI4_REG(22, f12_c22), + RMI4_REG(23, f12_c23), + RMI4_REG(24, f12_c24), + RMI4_REG(25, f12_c25), + RMI4_REG(26, f12_c26), + RMI4_REG(27, f12_c27), + RMI4_REG(28, f12_c28), + RMI4_REG(29, f12_c29), + RMI4_REG(30, f12_c30), +}; + +static struct f12_d1_type { + unsigned char type_and_stylus; + unsigned char x_lsb; + unsigned char x_msb; + unsigned char y_lsb; + unsigned char y_msb; + unsigned char z; + unsigned char wx; + unsigned char wy; +} f12_d1_0, f12_d1_1, f12_d1_2, f12_d1_3, f12_d1_4, f12_d1_5, f12_d1_6, +f12_d1_7, f12_d1_8, f12_d1_9; + +static struct synaptics_rmi4_subpkt f12_d1[] = { + RMI4_SUBPKT(f12_d1_0), + RMI4_SUBPKT(f12_d1_1), + RMI4_SUBPKT(f12_d1_2), + RMI4_SUBPKT(f12_d1_3), + RMI4_SUBPKT(f12_d1_4), + RMI4_SUBPKT(f12_d1_5), + RMI4_SUBPKT(f12_d1_6), + RMI4_SUBPKT(f12_d1_7), + RMI4_SUBPKT(f12_d1_8), + RMI4_SUBPKT(f12_d1_9), +}; + +/* RMI4 has the following wakeup gesture defined: + • 0x00 = No gesture + • 0x01 = One-Finger single tap + • 0x02 = One-Finger tap-and-hold + • 0x03 = One-Finger double tap + • 0x04 = One-Finger early tap + • 0x05 = One-Finger flick + • 0x06 = One-Finger press + • 0x07 = One-Finger swipe + • 0x08 = One-Finger Circle + • 0x09 = One-Finger Triangle + • 0x0A = One-Finger Vee + • 0x0B = Reserved + • 0x0C = Triple Tap + • 0x0D = Click + • 0x0E = Reserved + • 0x30 = Edge gesture + • 0x7F = Reserved + • 0x80 = Pinch + • 0x81 = Rotate + • 0x82 through 0xFF = Reserved +*/ + +#define DOUBLE_TAP_GESTURE 0x03 + +static struct f12_d4_type { + unsigned char gesture; +} f12_d4_0; + +static struct synaptics_rmi4_subpkt f12_d4[] = { + RMI4_SUBPKT(f12_d4_0), +}; + +static struct { + unsigned char attn[2]; +} f12_d15_0; + +static struct synaptics_rmi4_subpkt f12_d15[] = { + RMI4_SUBPKT(f12_d15_0), +}; + +static struct synaptics_rmi4_packet_reg f12_data_reg_array[] = { + RMI4_REG(1, f12_d1), + RMI4_REG(4, f12_d4), + RMI4_REG(15, f12_d15), +}; + +static struct { + union { + struct { + unsigned char sleep_mode:2; + unsigned char no_sleep:1; + unsigned char reserved:2; + unsigned char charger_connected:1; + unsigned char report_rate:1; + unsigned char configured:1; + } __packed; + unsigned char data[1]; + }; +} f01_c0_0; + +static struct { + unsigned char doze_interval; +} f01_c2_0; + +static struct { + unsigned char wakeup_threshold; +} f01_c3_0; + +static struct { + unsigned char doze_holdoff; +} f01_c5_0; + +static struct { + unsigned char recalibration_interval; +} f01_c9_0; + +static struct synaptics_rmi4_subpkt f01_c0[] = { + RMI4_SUBPKT_STATIC(0, f01_c0_0), +}; + +static struct synaptics_rmi4_subpkt f01_c2[] = { + RMI4_SUBPKT(f01_c2_0), +}; + +static struct synaptics_rmi4_subpkt f01_c3[] = { + RMI4_SUBPKT(f01_c3_0), +}; + +static struct synaptics_rmi4_subpkt f01_c5[] = { + RMI4_SUBPKT(f01_c5_0), +}; + +static struct synaptics_rmi4_subpkt f01_c9[] = { + RMI4_SUBPKT(f01_c9_0), +}; + +static struct synaptics_rmi4_packet_reg f01_ctrl_reg_array[] = { + RMI4_REG_STATIC(0, f01_c0, 1), + RMI4_REG_STATIC(2, f01_c2, 1), + RMI4_REG_STATIC(3, f01_c3, 1), + RMI4_REG_STATIC(5, f01_c5, 1), + RMI4_REG_STATIC(9, f01_c9, 1), +}; + +static struct { + struct { + unsigned short saturation_cap; + } __packed; +} f54_c2_0; + +static struct synaptics_rmi4_subpkt f54_c2[] = { + RMI4_SUBPKT(f54_c2_0), +}; + +static struct f54_control_95n f54_c95_0, f54_c95_1, f54_c95_2, + f54_c95_3, f54_c95_4, f54_c95_5, f54_c95_6, f54_c95_7; + +static struct synaptics_rmi4_subpkt f54_c95[] = { + RMI4_SUBPKT(f54_c95_0), + RMI4_SUBPKT(f54_c95_1), + RMI4_SUBPKT(f54_c95_2), + RMI4_SUBPKT(f54_c95_3), + RMI4_SUBPKT(f54_c95_4), + RMI4_SUBPKT(f54_c95_5), + RMI4_SUBPKT(f54_c95_6), + RMI4_SUBPKT(f54_c95_7), +}; + +static struct synaptics_rmi4_packet_reg f54_ctrl_reg_array[] = { + RMI4_REG(2, f54_c2), + RMI4_REG(95, f54_c95), +}; + +static struct { + unsigned char command; +} f54_m0_0; + +static struct synaptics_rmi4_subpkt f54_m0[] = { + RMI4_SUBPKT(f54_m0_0), +}; + +static struct synaptics_rmi4_packet_reg f54_cmd_reg_array[] = { + RMI4_REG(0, f54_m0), +}; + +static struct { + union { + struct { + unsigned char interference_metric_lsb; + unsigned char interference_metric_msb; + } __packed; + unsigned char data[2]; + }; +} f54_d6_0; + +static struct synaptics_rmi4_subpkt f54_d6[] = { + RMI4_SUBPKT(f54_d6_0), +}; + +static struct { + unsigned char noise_state; +} f54_d10_0; + +static struct synaptics_rmi4_subpkt f54_d10[] = { + RMI4_SUBPKT(f54_d10_0), +}; + +static struct { + union { + struct { + unsigned char cid_im_lsb; + unsigned char cid_im_msb; + } __packed; + unsigned char data[2]; + }; +} f54_d14_0; + +static struct synaptics_rmi4_subpkt f54_d14[] = { + RMI4_SUBPKT(f54_d14_0), +}; + +static struct { + union { + struct { + unsigned char freq_scan_im_lsb; + unsigned char freq_scan_im_msb; + } __packed; + unsigned char data[2]; + }; +} f54_d16_0; + +static struct synaptics_rmi4_subpkt f54_d16[] = { + RMI4_SUBPKT(f54_d16_0), +}; + +static struct { + union { + struct { + unsigned char freq:7; + unsigned char inhibit_freq_shift:1; + } __packed; + unsigned char data[1]; + }; +} f54_d17_0; + +static struct synaptics_rmi4_subpkt f54_d17[] = { + RMI4_SUBPKT(f54_d17_0), +}; + +static struct synaptics_rmi4_packet_reg f54_data_reg_array[] = { + RMI4_REG(6, f54_d6), + RMI4_REG(10, f54_d10), + RMI4_REG(14, f54_d14), + RMI4_REG(16, f54_d16), + RMI4_REG(17, f54_d17), +}; + +static struct { + union { + struct { + unsigned char cid_check_enable:1; + unsigned char fsim_clear_enable:1; + unsigned char nsm_intr_enable:1; + unsigned char unused:5; + } __packed; + unsigned char data[1]; + }; +} f51_c0_0; + +static struct { + struct { + unsigned char dyn_jitter_strength; + unsigned char dyn_jitter_gain; + unsigned char dyn_jitter_step_size; + } __packed; +} f51_c1_0; + +static struct { + union { + struct { + unsigned char md_feature_enable:1; + unsigned char md_no_relax:1; + unsigned char md_uncond_shutoff:1; + unsigned char unused:5; + } __packed; + unsigned char data[1]; + }; + unsigned char md_min_threshold; + unsigned char md_max_threshold; + unsigned char md_2d_area; + unsigned char md_frame_count_in; + unsigned char md_frame_count_out; +} f51_c4_0; + +static struct synaptics_rmi4_subpkt f51_c0[] = { + RMI4_SUBPKT(f51_c0_0), +}; + +static struct synaptics_rmi4_subpkt f51_c1[] = { + RMI4_SUBPKT(f51_c1_0), +}; + +static struct synaptics_rmi4_subpkt f51_c4[] = { + RMI4_SUBPKT(f51_c4_0), +}; + +static struct synaptics_rmi4_packet_reg f51_ctrl_reg_array[] = { + RMI4_REG(0, f51_c0), + RMI4_REG(1, f51_c1), + RMI4_REG(4, f51_c4), +}; + +struct { + union { + struct { + unsigned char noise_state:1; + unsigned char gear_change:1; + unsigned char guard_state:1; + unsigned char unused:5; + unsigned char md_present:1; + unsigned char reserved:7; + } __packed; + unsigned char data[2]; + }; +} f51_d0_0; + +static struct synaptics_rmi4_subpkt f51_d0[] = { + RMI4_SUBPKT(f51_d0_0), +}; + +static struct synaptics_rmi4_packet_reg f51_data_reg_array[] = { + RMI4_REG(0, f51_d0), +}; + +static struct { + union { + struct { + unsigned char num_of_scan_freq:4; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +} f54_q12_0; + +static struct synaptics_rmi4_subpkt f54_q12[] = { + RMI4_SUBPKT(f54_q12_0), +}; + +static struct synaptics_rmi4_packet_reg f54_query_reg_array[] = { + RMI4_REG(12, f54_q12), +}; + +#define CTRL_TYPE (0 << 8) +#define DATA_TYPE (1 << 8) +#define QUERY_TYPE (2 << 8) +#define COMMAND_TYPE (3 << 8) + +#ifdef CONFIG_TOUCHSCREEN_PROPERTY +extern void property_init(void); +#endif +// @wakeup_gesture: control wake up gesture function +static atomic_t wakeup_gesture; +// @glove_ctrl: control glove function +static atomic_t glove_ctrl; +// @letter: record gesture flag +static int letter = 0; +// @ flag for represents weather the touch information has been set. +unsigned int have_correct_setting = 0; +struct tpd_version_info *tpd_info_t = NULL; + +static unsigned char power_sleep_value = 1; +static struct synaptics_dsx_func_patch power_sleep_func_patch = { + .func = SYNAPTICS_RMI4_F01, + .regstr = 0, + .subpkt = 0, + .size = 1, + .bitmask = 7, + .data = &power_sleep_value, +}; + +static unsigned char force_update_value = 4; +static struct synaptics_dsx_func_patch force_update_func_patch = { + .func = SYNAPTICS_RMI4_F54 | COMMAND_TYPE, + .regstr = 0, + .subpkt = 0, + .size = 1, + .bitmask = 0, + .data = &force_update_value, +}; + +static struct synaptics_dsx_patch *force_update_patch; +static struct synaptics_dsx_patch *power_sleep_patch; + +static inline int register_ascii_to_type(unsigned char *symbol) +{ + int reg_type = CTRL_TYPE; + switch (*symbol) { + case 'Q': + reg_type = QUERY_TYPE; + break; + case 'D': + reg_type = DATA_TYPE; + break; + case 'M': + reg_type = COMMAND_TYPE; + break; + case 'C': + break; + default: return reg_type; + } + *symbol = '\0'; + return reg_type; +} + +static inline unsigned char register_type_to_ascii(int type) +{ + unsigned char ascii; + switch (type) { + case QUERY_TYPE: + ascii = 'Q'; + break; + case DATA_TYPE: + ascii = 'D'; + break; + case COMMAND_TYPE: + ascii = 'M'; + break; + case CTRL_TYPE: + default: + ascii = 'C'; + } + return ascii; +} + +static struct synaptics_rmi4_func_packet_regs synaptics_cfg_regs[] = { + { + .f_number = SYNAPTICS_RMI4_F12, + .base_addr = 0, + .query_offset = 4, + .nr_regs = ARRAY_SIZE(f12_ctrl_reg_array), + .regs = f12_ctrl_reg_array, + }, + { + .f_number = SYNAPTICS_RMI4_F01, + .base_addr = 0, + .query_offset = 0, /* does not matter */ + .nr_regs = ARRAY_SIZE(f01_ctrl_reg_array), + .regs = f01_ctrl_reg_array, + }, + { + .f_number = SYNAPTICS_RMI4_F12 | DATA_TYPE, + .base_addr = 0, + .query_offset = 7, +#define F12_D1_IDX 0 +#define F12_D4_IDX 1 +#define F12_D15_IDX 2 + .nr_regs = ARRAY_SIZE(f12_data_reg_array), + .regs = f12_data_reg_array, + }, + { + .f_number = SYNAPTICS_RMI4_F54, + .base_addr = 0, + .query_offset = 0, /* does not matter */ + .nr_regs = ARRAY_SIZE(f54_ctrl_reg_array), + .regs = f54_ctrl_reg_array, + }, + { + .f_number = SYNAPTICS_RMI4_F54 | COMMAND_TYPE, + .base_addr = 0, + .query_offset = 0, /* does not matter */ + .nr_regs = ARRAY_SIZE(f54_cmd_reg_array), + .regs = f54_cmd_reg_array, + }, + { + .f_number = SYNAPTICS_RMI4_F54 | DATA_TYPE, + .base_addr = 0, + .query_offset = 0, /* does not matter */ + .nr_regs = ARRAY_SIZE(f54_data_reg_array), + .regs = f54_data_reg_array, + }, + { + .f_number = SYNAPTICS_RMI4_F54 | QUERY_TYPE, + .base_addr = 0, + .query_offset = 0, /* does not matter */ + .nr_regs = ARRAY_SIZE(f54_query_reg_array), + .regs = f54_query_reg_array, + }, + { + .f_number = SYNAPTICS_RMI4_F51, + .base_addr = 0, + .query_offset = 0, /* does not matter */ + .nr_regs = ARRAY_SIZE(f51_ctrl_reg_array), + .regs = f51_ctrl_reg_array, + }, + { + .f_number = SYNAPTICS_RMI4_F51 | DATA_TYPE, + .base_addr = 0, + .query_offset = 0, /* does not matter */ + .nr_regs = ARRAY_SIZE(f51_data_reg_array), + .regs = f51_data_reg_array, + }, +}; + +static unsigned char tsb_buff_clean_flag = 1; + +static char *synaptics_dsx_find_patch(char *head, char *delimiters, char **next) +{ + char *patch = head; + for (; patch; patch = *next) { + *next = strpbrk(patch, delimiters); + if (*next) + *(*next)++ = '\0'; + patch = skip_spaces(patch); + if (!patch || !*patch || *patch == '#') + continue; + else + break; + } + return patch; +} + +static int parse_patch_data(char *value_p, u8 data[], long *bitmask_v) +{ + int i, error, num_of_bytes = 1; + long value_v; + char *bitmask_p; + + bitmask_p = strpbrk(value_p, "&"); + if (bitmask_p) { + *bitmask_p++ = '\0'; + error = kstrtol(bitmask_p, 16, bitmask_v); + if (error) + pr_err("bitmask conv error\n"); + } + + /* detect multiple bytes data */ + if ((strlen(value_p)%2) == 0) + num_of_bytes = strlen(value_p)/2; + + for (i = 0; i < num_of_bytes; i++) { + char hex_val[3] = {0}; + hex_val[0] = *value_p; + hex_val[1] = *(value_p + 1); + error = kstrtol(hex_val, 16, &value_v); + if (error) + pr_err("value conv error\n"); + data[i] = (u8)value_v; + value_p += 2; + } + + return num_of_bytes; +} + +static int synaptics_dsx_parse_patch(int func, char *query, + struct synaptics_dsx_patch *patch_ptr, bool expect_data) +{ + int i, error, rt_mod, function, num_of_bytes; + u8 data[64]; + char *next, *subpkt_p, *value_p, *pair = query; + long regstr_v, bitmask_v, subpkt_v; + struct synaptics_dsx_func_patch *patch; + + rt_mod = func & 0xf00; + function = func & 0xff; + for (i = 0; pair; pair = next, i++) { + num_of_bytes = 0; + bitmask_v = subpkt_v = 0L; + + pair = synaptics_dsx_find_patch(pair, ",", &next); + + value_p = strpbrk(pair, "="); + if (!value_p) { + pr_err("F%x[%d]: invalid syntax '%s'\n", + function, i, pair); + continue; + } + + /* make sure string is null terminated */ + *value_p = '\0'; + + subpkt_p = strpbrk(pair, ":"); + if (subpkt_p) { + *subpkt_p++ = '\0'; + error = kstrtol(subpkt_p, 10, &subpkt_v); + if (error) { + pr_err("F%x[%d]: subpacket conv error\n", + function, i); + continue; + } + } + + error = kstrtol(pair, 10, ®str_v); + if (error) { + pr_err("F%x[%d]: register conv error\n", + function, i); + continue; + } + + if (expect_data) + num_of_bytes = parse_patch_data( + ++value_p, data, &bitmask_v); + /* primitive data validation */ + if (function <= 0 || function > 255 || + regstr_v < 0 || regstr_v > 255) { + pr_err("F%x[%d]: invalid values\n", function, i); + continue; + } + + patch = kzalloc(sizeof(*patch), GFP_KERNEL); + if (!patch) { + pr_err("failed to alloc mem\n"); + return -ENOMEM; + } + + if (expect_data) { + patch->data = kzalloc(num_of_bytes, GFP_KERNEL); + if (!patch->data) { + pr_err("failed to alloc mem\n"); + return -ENOMEM; + } + memcpy(patch->data, data, num_of_bytes); + } + + patch->func = (u16)func; + patch->regstr = (u8)regstr_v; + patch->subpkt = (u8)subpkt_v; + patch->size = (u8)num_of_bytes; + patch->bitmask = (u8)bitmask_v; + + pr_debug("F%x[%d], register %d, subpkt %d, size %d, mask %x\n", + function, i, patch->regstr, patch->subpkt, + patch->size, patch->bitmask); + patch_ptr->cfg_num++; + list_add_tail(&patch->link, &patch_ptr->cfg_head); + } + + return 0; +} + +/* + * Special settings passed to the driver via device tree + * Example: "F12@11:2=1fa0;F1@1=1;" + * Where: + * F12 - decimal object number + * @ - delimits function number and following patch sets + * 11:2 - decimal register and subpacket numbers (0 offset) + * 26=a - register number and hex value to assign + * 2=1fa0 - 2 bytes of data to write to the register + */ +static void synaptics_dsx_parse_string(struct synaptics_rmi4_data *data, + const char *patch_ptr, + struct synaptics_dsx_patch *patch, + bool expect_data) +{ + struct device *dev = &data->i2c_client->dev; + long number_v; + char *patch_string; + char *config_p, *next, *patch_set; + int i, error, rt_mod = 0; + + patch_string = kstrdup(patch_ptr, GFP_KERNEL); + for (i = 0, patch_set = patch_string; patch_set; patch_set = next) { + patch_set = synaptics_dsx_find_patch(patch_set, ";\n", &next); + if (!patch_set) + break; + + dev_dbg(dev, "patch set %d: \"%s\"\n", i, patch_set); + config_p = strpbrk(patch_set, "@"); + + /* consider force update and power seetings here */ + if (!config_p) { + switch (*patch_set) { + case 'S': + patch->flags |= FLAG_POWER_SLEEP; + break; + case 'U': + patch->flags |= FLAG_FORCE_UPDATE; + break; + case 'W': + patch->flags |= FLAG_WAKEABLE; + break; + } + continue; + } + + if (*patch_set != 'F' && *patch_set != 'f') { + dev_err(dev, "invalid syntax '%s'\n", patch_set); + continue; + } + + rt_mod = register_ascii_to_type(config_p-1); + dev_dbg(dev, "register type modifier %d\n", rt_mod); + + /* strip non digits */ + *config_p++ = '\0'; + + error = kstrtol(++patch_set, 16, &number_v); + if (error) { + dev_err(dev, "kstrtol error %d\n", error); + continue; + } + + error = synaptics_dsx_parse_patch((int)number_v + rt_mod, + config_p, patch, expect_data); + if (error < 0) { + dev_err(dev, "invalid patch; parse error %d\n", error); + continue; + } + + i++; + } + kfree(patch_string); + if (patch->cfg_num) + dev_info(dev, "processed %d patch sets for %d functions\n", + patch->cfg_num, i); + else + dev_info(dev, "no valid patch sets found\n"); +} + +#define LAST_SUBPACKET_ROW_IND_MASK 0x80 +#define NR_SUBPKT_PRESENCE_BITS 7 +#ifdef CONFIG_TOUCHSCREEN_TOUCHX_BASE +struct touchxs touchxp; +EXPORT_SYMBOL(touchxp); +#endif + +static struct synaptics_rmi4_func_packet_regs *find_function(int f_number) +{ + struct synaptics_rmi4_func_packet_regs *regs = NULL; + int i; + for (i = 0; i < ARRAY_SIZE(synaptics_cfg_regs); i++) + if (synaptics_cfg_regs[i].f_number == f_number) { + regs = &synaptics_cfg_regs[i]; + break; + } + return regs; +} + +static struct synaptics_rmi4_packet_reg *find_packet_reg( + struct synaptics_rmi4_func_packet_regs *regs, int number) +{ + struct synaptics_rmi4_packet_reg *reg = NULL; + int r; + + for (r = 0; r < regs->nr_regs; r++) + if (number == regs->regs[r].r_number) { + reg = ®s->regs[r]; + break; + } + return reg; +} + +int synaptics_rmi4_scan_f12_reg_info( + struct synaptics_rmi4_data *rmi4_data, + unsigned short query_addr, + unsigned short regs_base_addr, + struct synaptics_rmi4_func_packet_regs *regs) +{ + unsigned char sz, mask; + int ii, jj, r, s, retval, max_reg_number; + unsigned short r_offset; + unsigned short addr = query_addr; + unsigned char data[255]; + bool r_exist[128] = {false}; + + for (r = 0; r < regs->nr_regs; ++r) { + regs->regs[r].offset = -1; + regs->regs[r].size = 0; + kfree(regs->regs[r].data); + for (s = 0; s < regs->regs[r].nr_subpkts; ++s) { + regs->regs[r].subpkt[s].present = 0; + if (regs->regs[r].subpkt[s].data && + regs->regs[r].subpkt[s].size) + memset(regs->regs[r].subpkt[s].data, 0, + regs->regs[r].subpkt[s].size); + } + } + + regs->base_addr = regs_base_addr; + retval = rmi4_data->i2c_read(rmi4_data, addr, &sz, 1); + pr_debug("size of reg presence = %d, addr 0x%x\n", sz, addr); + if (retval < 0) + return retval; + if (!sz) + return -EIO; + /* Scan register presence */ + retval = rmi4_data->i2c_read(rmi4_data, ++addr, data, sz); + if (retval < 0) + return retval; + if (!data[0]) { + pr_err("packet register size greater 255 bytes" + " not supported\n"); + return -ENOSYS; + } + ii = 1; + for (r = 0, r_offset = 0; ii < sz; ++ii) { + pr_debug("reg presence [%d] = 0x%02x\n", ii, data[ii]); + for (jj = 0, mask = 1; jj < 8; ++jj, ++r, mask <<= 1) { + struct synaptics_rmi4_packet_reg *reg = + find_packet_reg(regs, r); + bool present = (data[ii] & mask) != 0; + bool allocated = reg ? true : false; + if (present != allocated) + pr_debug(" reg: r%d is%s present, but was%s" + " expected\n", r, + present ? "" : " NOT", + allocated ? "" : " NOT"); + if (!present) { + r_exist[r] = false; + continue; + } + r_exist[r] = true; + if (allocated) + reg->offset = r_offset; + pr_debug(" r%d offset = %d\n", r, r_offset); + r_offset++; + } + } + + max_reg_number = r; + pr_debug("max register number = %d\n", max_reg_number); + + /* Scan register size and subpacket presence*/ + sz = data[0]; + pr_debug("subpacket presence sz = %d, addr 0x%x\n", sz, addr+1); + retval = rmi4_data->i2c_read(rmi4_data, ++addr, data, sz); + if (retval < 0) + return retval; + for (r = 0, ii = 0; r < max_reg_number && ii < sz; ++r) { + unsigned int reg_size, expected_reg_size; + struct synaptics_rmi4_packet_reg *reg = + find_packet_reg(regs, r); + if (!r_exist[r]) { + pr_debug("r%d does not exist; go to the next...\n", r); + continue; + } + reg_size = data[ii++]; + if (!reg_size) { + pr_err("packet register size greater 255 bytes" + " not supported\n"); + return -ENOSYS; + } + if (reg && reg->offset != -1) { + reg->data = kzalloc(reg_size, GFP_KERNEL); + if (reg->data) { + reg->size = reg_size; + pr_debug("r%d allocated %d bytes buffer @%p\n", + reg->r_number, reg->size, reg->data); + } else + pr_err("cannot alloc register data buffer\n"); + } + pr_debug("r%d sz = %d\n", r, reg_size); + expected_reg_size = 0; + for (s = 0; ii < sz;) { + pr_debug(" subpkt presence [%d] = 0x%02x\n", + ii, data[ii]); + for (jj = 0, mask = 1; jj < NR_SUBPKT_PRESENCE_BITS; + ++jj, ++s, mask <<= 1) { + struct synaptics_rmi4_subpkt *subpkt = NULL; + bool present = (data[ii] & mask) != 0; + bool expected = false; + if (reg && s < reg->nr_subpkts) { + subpkt = reg->subpkt + s; + expected = subpkt->expected; + } + if (!present || !expected) { + if (present != expected) + pr_debug(" subpacket:" + " r%d s%d is%s present," + " but was%s expected\n", + r, s, + present ? "" : " NOT", + expected ? "" : " NOT"); + continue; + } + pr_debug(" r%d.s%d is present\n", r, s); + if (subpkt) { + subpkt->present = true; + expected_reg_size += subpkt->size; + } + } + if ((data[ii++] & LAST_SUBPACKET_ROW_IND_MASK) == 0) { + pr_debug("no more subpackets\n"); + break; + } + } + if (reg && reg->expected && reg->size != expected_reg_size) { + pr_debug(" r%d size error:" + " expected %d actual is %d\n", + r, expected_reg_size, reg->size); + } + } + return 0; +} + +int synaptics_rmi4_read_packet_reg( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs, int r_number) +{ + int s, retval, offset; + struct synaptics_rmi4_packet_reg *reg = find_packet_reg(regs, r_number); + + if (!reg || !reg->size) { + pr_err("register %d not found or zero size\n", r_number); + return -EINVAL; + } + + if (reg->offset == -1) { + pr_err("touch register error: can't read r%d - not present\n", + r_number); + return -ENOENT; + } + + pr_debug("F%02x%c reading r%d{%X}, size %d to @%p\n", + regs->f_number & 0xff, + register_type_to_ascii(regs->f_number & 0xf00), + reg->r_number, regs->base_addr + reg->offset, + reg->size, reg->data); + + retval = rmi4_data->i2c_read(rmi4_data, + regs->base_addr + reg->offset, + reg->data, reg->size); + if (retval < 0) + return retval; + + for (s = 0, offset = 0; s < reg->nr_subpkts; ++s) { + struct synaptics_rmi4_subpkt *subpkt = reg->subpkt + s; + if (!subpkt->present) + continue; + + if ((reg->size - offset) < subpkt->size) { + if (subpkt->data != &dummy_subpkt) + pr_err("subpkt size error: expected %d bytes," + " only %d present\n", subpkt->size, + (reg->size - offset)); + break; + } + + memcpy(subpkt->data, reg->data+offset, subpkt->size); + offset += subpkt->size; +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + { + int kk; + pr_debug("read r%d.s%d =\n", reg->r_number, s); + for (kk = 0; kk < subpkt->size; ++kk) + pr_debug("%02x\n", + ((unsigned char *)subpkt->data)[kk]); + } +#endif + } + return retval; +} + +int synaptics_rmi4_read_packet_regs( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs) +{ + int r; + int retval = 0; + + for (r = 0; r < regs->nr_regs; ++r) { + pr_debug("about to read F%02x%c register %d [%d/%d]\n", + regs->f_number & 0xff, + register_type_to_ascii(regs->f_number & 0xf00), + regs->regs[r].r_number, + regs->regs[r].expected, + regs->regs[r].offset >= 0); + if (regs->regs[r].expected && regs->regs[r].offset >= 0) { + retval = synaptics_rmi4_read_packet_reg( + rmi4_data, regs, regs->regs[r].r_number); + if (retval < 0) + break; + } + } + return retval; +} + +static int synaptics_rmi4_write_packet_reg( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs, int r_number) +{ + struct synaptics_rmi4_packet_reg *reg = find_packet_reg(regs, r_number); + int sz, ii, offset, retval; + + if (!reg || !reg->size) { + pr_err("r%d does not exist or zero size\n", r_number); + return -EINVAL; + } + if (reg->offset == -1) { + pr_err("touch register error: writing to absent register r%d\n", + r_number); + return -ENOENT; + } + + for (ii = 0, sz = 0, offset = 0; ii < reg->nr_subpkts; ++ii) { + struct synaptics_rmi4_subpkt *subpkt = reg->subpkt + ii; + if (!subpkt->present) + continue; + + if (subpkt->data && subpkt->size && + (offset + subpkt->size) <= reg->size) { + if ((reg->size - offset) >= subpkt->size) { + memcpy(reg->data + sz, subpkt->data, + subpkt->size); + sz += subpkt->size; + } else { + pr_err("expected %d bytes, only %d present\n", + offset + subpkt->size, reg->size); + break; + } + } else { + retval = -EINVAL; + pr_err("cannot update subpacket %d: "\ + "sz=%d, offset=%d, data=%p, reg_sz=%d\n", + ii, subpkt->size, offset, + subpkt->data, reg->size); + goto out; + } + } + + pr_debug("writing r%d{%X}, size %d to @%p\n", reg->r_number, + regs->base_addr + reg->offset, reg->size, reg->data); + + retval = rmi4_data->i2c_write(rmi4_data, + regs->base_addr + reg->offset, + reg->data, sz); +out: + return retval; +} + +static int synaptics_convert_gesture_flag(int value) +{ + switch (value) { + case 0x03: + value = 0x24; + break; + default: + pr_err("%s, Can't not recognize gesture flag = 0x%02x.\n", __func__, value); + break; + } + return value; +} + +int get_array_flag(void) +{ + return letter; +} +EXPORT_SYMBOL(get_array_flag); + +void synaptics_set_gesture_ctrl(unsigned int val) +{ + if(val == 1) + { + if(atomic_cmpxchg(&wakeup_gesture, 0, 1) == 0) { + pr_err("%s, setting gesture flag is = %d.\n", __func__, atomic_read(&wakeup_gesture)); + }else { + pr_err("%s, setting gesture has been opened = %d.\n", __func__, atomic_read(&wakeup_gesture)); + return; + } + }else + { + if(atomic_cmpxchg(&wakeup_gesture, 1, 0) == 1) + { + pr_err("%s, setting gesture flag is = %d.\n", __func__, atomic_read(&wakeup_gesture)); + }else { + pr_err("%s, setting gesture has been closed = %d.\n", __func__, atomic_read(&wakeup_gesture)); + return; + } + } +} +EXPORT_SYMBOL(synaptics_set_gesture_ctrl); + +#ifdef CONFIG_OF +static int synaptics_dsx_gpio_config( + struct synaptics_dsx_platform_data *pdata, bool enable) +{ + int retval = 0; + +// unsigned vio_en_pin = 33; + unsigned vci_en_pin = 36; + pr_err("[wj]request irq %s [%d]\n", + IRQ_GPIO_NAME, pdata->irq_gpio); + pr_err("[wj]request reset %s [%d]\n", + RESET_GPIO_NAME, pdata->reset_gpio); +/* + retval = gpio_request(vio_en_pin, VIO_EN_GPIO_NAME); + if (retval) { + pr_err("unable to request[%d]: rc=%d\n", + vio_en_pin, retval); + goto err_gpio; + } + retval = gpio_direction_output(vio_en_pin, 1); + if (retval) { + pr_err("unable to set [%d] dir: rc=%d\n", + vio_en_pin, retval); + goto err_gpio; + } + msleep(10); +*/ + retval = gpio_request(vci_en_pin, VCI_EN_GPIO_NAME); + if (retval) { + pr_err("unable to request[%d]: rc=%d\n", + vci_en_pin, retval); + //goto err_gpio; + } + retval = gpio_direction_output(vci_en_pin, 1); + if (retval) { + pr_err("unable to set [%d] dir: rc=%d\n", + vci_en_pin, retval); + //goto err_gpio; + } + msleep(10); + + if (enable) { + if (!gpio_is_valid(pdata->irq_gpio)) { + pr_err("invalid %s\n", IRQ_GPIO_NAME); + retval = -EINVAL; + } + retval = gpio_request(pdata->irq_gpio, IRQ_GPIO_NAME); + if (retval) { + pr_err("unable to request %s [%d]: rc=%d\n", + IRQ_GPIO_NAME, pdata->irq_gpio, retval); + goto err_gpio; + } + retval = gpio_direction_input(pdata->irq_gpio); + if (retval) { + pr_err("unable to set %s [%d] dir: rc=%d\n", + IRQ_GPIO_NAME, pdata->irq_gpio, retval); + goto err_gpio; + } + + if (!gpio_is_valid(pdata->reset_gpio)) { + pr_err("invalid %s\n", RESET_GPIO_NAME); + retval = -EINVAL; + } + retval = gpio_request(pdata->reset_gpio, RESET_GPIO_NAME); + if (retval) { + pr_err("unable to request %s [%d]: rc=%d\n", + RESET_GPIO_NAME, pdata->reset_gpio, retval); + goto err_gpio; + } + retval = gpio_direction_output(pdata->reset_gpio, 1); + if (retval) { + pr_err("unable to set %s [%d] dir: rc=%d\n", + RESET_GPIO_NAME, pdata->reset_gpio, retval); + goto err_gpio; + } + } else { + gpio_free(pdata->irq_gpio); + gpio_free(pdata->reset_gpio); + } + +err_gpio: + return retval; +} + +static void synaptics_dsx_dt_parse_modifier(struct synaptics_rmi4_data *data, + struct device_node *parent, struct config_modifier *config, + const char *modifier_name, bool active) +{ + struct device *dev = &data->i2c_client->dev; + const char *patch_string; + char node_name[64]; + struct synaptics_clip_area clipa; + struct device_node *np_config; + int err; + + scnprintf(node_name, 63, "%s-%s", modifier_name, + active ? "active" : "suspended"); + np_config = of_find_node_by_name(parent, node_name); + if (!np_config) { + dev_dbg(dev, "%s: node does not exist\n", node_name); + return; + } + + err = of_property_read_string(np_config, "patch-data", + (const char **)&patch_string); + if (!err) { + struct synaptics_dsx_patch *p_data = + synaptics_dsx_init_patch(modifier_name); + + if (!p_data) { + dev_err(dev, "%s: alloc error\n", node_name); + goto clip_area; + } + synaptics_dsx_parse_string(data, patch_string, p_data, true); + if (active) + config->active = p_data; + else + config->suspended = p_data; + } + +clip_area: + + err = of_property_read_u32_array(np_config, "touch-clip-area", + (unsigned int *)&clipa, sizeof(clipa)/sizeof(unsigned int)); + if (!err) { + config->clipa = kzalloc(sizeof(clipa), GFP_KERNEL); + if (!config->clipa) { + dev_err(dev, "clip area allocation failure\n"); + return; + } + memcpy(config->clipa, &clipa, sizeof(clipa)); + pr_notice("using touch clip area in %s\n", node_name); + } + + of_node_put(np_config); +} + +/* ASCII names order MUST match enum */ +static const char const *ascii_names[] = { "aod", "stats", "folio", + "charger", "wakeup", "fps", "query", "runtime", "na" +}; + +static int modifier_name2id(const char *name) +{ + int i, len2cmp, chosen = -1; + + for (i = 0; i < SYNA_MOD_MAX; i++) { + len2cmp = min_t(int, strlen(name), strlen(ascii_names[i])); + if (!strncmp(name, ascii_names[i], len2cmp)) { + chosen = i; + break; + } + } + return chosen; +} + +static inline const char *modifier_id2name(int id) +{ + return (id >= 0 && id < SYNA_MOD_MAX) ? + ascii_names[id] : ascii_names[SYNA_MOD_MAX]; +} + +static struct config_modifier *modifier_by_id( + struct synaptics_rmi4_data *data, int id) +{ + struct config_modifier *cm, *found = NULL; + + down(&data->modifiers.list_sema); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + pr_debug("walk-thru: ptr=%p modifier[%s] id=%d\n", + cm, cm->name, cm->id); + if (cm->id == id) { + found = cm; + break; + } + } + up(&data->modifiers.list_sema); + pr_debug("returning modifier id=%d[%d]\n", found ? found->id : -1, id); + return found; +} + +static int synaptics_dsx_dt_parse_modifiers(struct synaptics_rmi4_data *data) +{ + struct device *dev = &data->i2c_client->dev; + struct device_node *np = dev->of_node; + struct device_node *np_mod; + int i, num_names, ret = 0; + char node_name[64]; + static const char **modifiers_names; + + sema_init(&data->modifiers.list_sema, 1); + INIT_LIST_HEAD(&data->modifiers.mod_head); + data->modifiers.mods_num = 0; + + num_names = of_property_count_strings(np, "config_modifier-names"); + if (num_names < 0) { + dev_err(dev, "Cannot parse config_modifier-names: %d\n", + num_names); + return -ENODEV; + } + + modifiers_names = devm_kzalloc(dev, + sizeof(*modifiers_names) * num_names, GFP_KERNEL); + if (!modifiers_names) + return -ENOMEM; + + for (i = 0; i < num_names; i++) { + ret = of_property_read_string_index(np, "config_modifier-names", + i, &modifiers_names[i]); + if (ret < 0) { + dev_err(dev, "Cannot parse modifier-names: %d\n", ret); + return ret; + } + } + + data->modifiers.mods_num = num_names; + + for (i = 0; i < num_names; i++) { + int id; + struct config_modifier *cm, *config; + + scnprintf(node_name, 63, "config_modifier-%s", + modifiers_names[i]); + np_mod = of_find_node_by_name(np, node_name); + if (!np_mod) { + dev_warn(dev, "cannot find modifier node %s\n", + node_name); + continue; + } + + /* check for duplicate nodes in devtree */ + id = modifier_name2id(modifiers_names[i]); + pr_err("processing modifier %s[%d]\n", node_name, id); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + if (cm->id == id) { + dev_err(dev, "duplicate modifier node %s\n", + node_name); + return -EFAULT; + } + } + /* allocate modifier's structure */ + config = kzalloc(sizeof(*config), GFP_KERNEL); + if (!config) + return -ENOMEM; + + list_add_tail(&config->link, &data->modifiers.mod_head); + config->name = modifiers_names[i]; + config->id = id; + + if (of_property_read_bool(np_mod, "enable-notification")) { + switch (id) { + case SYNA_MOD_FOLIO: + pr_notice("using folio detection\n"); + data->folio_detection_enabled = true; + break; + case SYNA_MOD_CHARGER: + pr_notice("using charger detection\n"); + data->charger_detection_enabled = true; + break; + case SYNA_MOD_FPS: + pr_notice("using fingerprint sensor detection\n"); + data->fps_detection_enabled = true; + break; + case SYNA_MOD_WAKEUP: + pr_notice("using wakeup detection\n"); + data->wakeup_detection_enabled = true; + break; + case SYNA_MOD_AOD: + case SYNA_MOD_STATS: + break; + + } + } else { + config->effective = true; + dev_dbg(dev, "modifier %s enabled unconditionally\n", + node_name); + } + + dev_dbg(dev, "processing modifier %s[%d]\n", + node_name, config->id); + + synaptics_dsx_dt_parse_modifier(data, np_mod, config, + modifiers_names[i], true); + synaptics_dsx_dt_parse_modifier(data, np_mod, config, + modifiers_names[i], false); + of_node_put(np_mod); + } + + return 0; +} + +static struct synaptics_dsx_platform_data * + synaptics_dsx_of_init(struct i2c_client *client, + struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned int u32_data, key_codes[SYN_MAX_BUTTONS]; + struct synaptics_dsx_platform_data *pdata; + struct device_node *np = client->dev.of_node; + struct synaptics_dsx_cap_button_map *button_map = NULL; + + rmi4_data->patching_enabled = 1; + retval = synaptics_dsx_dt_parse_modifiers(rmi4_data); + if (retval) + rmi4_data->patching_enabled = 0; + else { + force_update_patch = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_patch), GFP_KERNEL); + power_sleep_patch = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_patch), GFP_KERNEL); + + if (!force_update_patch || !power_sleep_patch) + return NULL; + + force_update_patch->name = "force-update"; + force_update_patch->cfg_num = 1; + sema_init(&force_update_patch->list_sema, 1); + INIT_LIST_HEAD(&force_update_patch->cfg_head); + list_add(&force_update_func_patch.link, + &force_update_patch->cfg_head); + + power_sleep_patch->name = "power-sleep"; + power_sleep_patch->cfg_num = 1; + sema_init(&power_sleep_patch->list_sema, 1); + INIT_LIST_HEAD(&power_sleep_patch->cfg_head); + list_add(&power_sleep_func_patch.link, + &power_sleep_patch->cfg_head); + } + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&client->dev, "pdata allocation failure\n"); + return NULL; + } + + pdata->irq_gpio = of_get_gpio(np, 0); + pdata->reset_gpio = of_get_gpio(np, 1); + + memset(key_codes, 0, sizeof(key_codes)); + retval = of_property_read_u32_array(np, "synaptics,key-buttons", + key_codes, SYN_MAX_BUTTONS); + if (!retval) { + int ii; + unsigned char *button_codes; + + button_map = kzalloc(sizeof(*button_map), GFP_KERNEL); + if (IS_ERR_OR_NULL(button_map)) { + dev_err(&client->dev, "button allocation failure\n"); + return NULL; + } + + for (ii = 0; ii < SYN_MAX_BUTTONS; ii++) + if (key_codes[ii]) + button_map->nbuttons++; + + button_codes = kzalloc(button_map->nbuttons, GFP_KERNEL); + if (IS_ERR_OR_NULL(button_codes)) { + dev_err(&client->dev, "button allocation failure\n"); + kfree(button_map); + return NULL; + } + + for (ii = 0; ii < button_map->nbuttons; ii++) + *(button_codes + ii) = (unsigned char)key_codes[ii]; + + button_map->map = button_codes; + } + + pdata->irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + pdata->cap_button_map = button_map; + + if (of_property_read_bool(np, "synaptics,gpio-config")) { + pr_notice("using gpio config\n"); + pdata->gpio_config = synaptics_dsx_gpio_config; + } + + if (of_property_read_bool(np, "synaptics,x-flip")) { + pr_notice("using flipped X axis\n"); + pdata->x_flip = true; + } + + if (of_property_read_bool(np, "synaptics,y-flip")) { + pr_notice("using flipped Y axis\n"); + pdata->y_flip = true; + } + + if (of_property_read_bool(np, "synaptics,purge-enabled")) { + pr_notice("using purge\n"); + rmi4_data->purge_enabled = true; + } + + retval = of_property_read_u32(np, + "synaptics,aod-multi-touch", &u32_data); + if (!retval) { + pr_notice("using multi touch in aod\n"); + rmi4_data->aod_mt = (unsigned char)u32_data; + } else { + pr_notice("using single touch in aod\n"); + rmi4_data->aod_mt = 1; + } + + if (of_property_read_bool(np, "synaptics,use-in-progress-event-blank")) { + pr_notice("using in progress event blank\n"); + rmi4_data->event_blank = FB_IN_PROGRESS_EVENT_BLANK; + } + + return pdata; +} +#else +static inline struct synaptics_dsx_platform_data * + synaptics_dsx_of_init(struct i2c_client *client) +{ + return NULL; +} +#endif + +static void synaptics_dsx_validate_product_string(unsigned char *id) +{ + unsigned char *s, *wc; + bool do_once = false, do_all = false; + + for (s = wc = id; *s; s++) { + if (*s == '-') { + do_all = true; + continue; + } + if (!isdigit(*s) && isupper(*s)) { + *s = tolower(*s); + do_once = true; + } + if (do_all || do_once) { + *wc = *s; + do_once = false; + } + wc++; + } + *wc = 0; +} + +#define NANO_SEC 1000000000 +#define SEC_TO_MSEC 1000 +#define NANO_TO_MSEC 1000000 + +#define CIRCULAR_BUFFER 1 +#define ONE_BASED_INDEX 2 + +static struct synaptics_dsx_stats gStat; + +static inline unsigned long long timediff_ms( + struct timespec start, struct timespec end) +{ + struct timespec temp; + + if ((end.tv_nsec - start.tv_nsec) < 0) { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = NANO_SEC + end.tv_nsec - start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return (temp.tv_sec * SEC_TO_MSEC) + (temp.tv_nsec / NANO_TO_MSEC); +} + +static inline void timekeeping_reset(struct statistics *sp) +{ + int ii; + + sp->active = -1; + sp->clk_run = false; + for (ii = 0; ii < sp->max; ii++) { + sp->keeper[ii].id = -1; + sp->keeper[ii].duration = (unsigned long long)0; + } +} + +static void statistics_reset(void) +{ + gStat.uptime_dur = (unsigned long long)0; + gStat.uptime_run = false; + timekeeping_reset(gStat.dur); + timekeeping_reset(gStat.hop); + timekeeping_reset(gStat.nms); +} + +static struct statistics *timekeeping_alloc(int max, + unsigned char abbr, unsigned int flags) +{ + ssize_t data_size = sizeof(struct time_keeping) * max; + struct statistics *stats; + + tk_debug("allocating [%zu/%zu] for %c\n", + sizeof(struct statistics), data_size, abbr); + stats = kzalloc(sizeof(struct statistics) + data_size, GFP_KERNEL); + if (!stats) + return NULL; + stats->active = -1; + stats->max = max; + stats->flags = flags; + stats->abbr = abbr; + timekeeping_reset(stats); + return stats; +} + +static int statistics_alloc(int max_gear, int max_hop, int max_nms, int max_mpd) +{ + gStat.dur = timekeeping_alloc(max_gear, 'G', 0); + if (!gStat.dur) + return -ENOMEM; + gStat.hop = timekeeping_alloc(max_hop, 'H', CIRCULAR_BUFFER); + if (!gStat.hop) { + kfree(gStat.hop); + return -ENOMEM; + } + gStat.nms = timekeeping_alloc(max_nms, 'N', ONE_BASED_INDEX); + if (!gStat.nms) { + kfree(gStat.dur); + kfree(gStat.hop); + return -ENOMEM; + } + gStat.mpd = timekeeping_alloc(max_mpd, 'M', 0); + if (!gStat.nms) { + kfree(gStat.dur); + kfree(gStat.hop); + kfree(gStat.nms); + return -ENOMEM; + } + return 0; +} + +static void statistics_log_time_end(struct statistics *stats, ktime_t end) +{ + struct time_keeping *keeper = NULL; + unsigned long long duration; + int c_idx; + + if (stats->active == -1) + return; + + keeper = &stats->keeper[stats->active]; + c_idx = keeper->id; + duration = timediff_ms(ktime_to_timespec(stats->start), + ktime_to_timespec(end)); + keeper->duration += duration; + tk_debug("%c[%d].%d logged +%llu/%llu\n", stats->abbr, stats->active, + c_idx, duration, keeper->duration); + stats->clk_run = false; +} + +static void statistics_log_time(struct statistics *stats, int idx, ktime_t log) +{ + struct time_keeping *keeper = NULL; + int c_idx = stats->active; + + /* c_idx stays -1 or gets set to id of the current cell */ + if (stats->active != -1) { + keeper = &stats->keeper[stats->active]; + c_idx = keeper->id; + } + + /* new idx and timer was running */ + if (keeper && c_idx != idx && stats->clk_run) { + unsigned long long duration; + + duration = timediff_ms(ktime_to_timespec(stats->start), + ktime_to_timespec(log)); + keeper->duration += duration; + /* clock is ticking already, just update time */ + stats->start = log; + tk_debug("%c[%d].%d logged +%llu/%llu\n", stats->abbr, + stats->active, c_idx, duration, keeper->duration); + } + + /* move to the next cell if idx is changing */ + if (c_idx != idx) { + /* shift index with overflow control */ + if (stats->flags & CIRCULAR_BUFFER) { + if (++stats->active == stats->max) + stats->active = 0; + } else if (stats->flags & ONE_BASED_INDEX) { + stats->active = idx - 1; + } else + stats->active = idx; + + /* store index in the cell */ + stats->keeper[stats->active].id = idx; + tk_debug("%c new %sindex %d\n", stats->abbr, + stats->flags & CIRCULAR_BUFFER ? "RR_" : + stats->flags & ONE_BASED_INDEX ? "1B_" : "", + stats->active); + } + + if (!stats->clk_run) { + stats->start = log; + stats->clk_run = true; + } +} + +static int statistics_start_timekeeping( + struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg, *reg_17, *reg_10; + ktime_t log; + int ii, error; + + if (gStat.uptime_run) { + pr_debug("timekeeping already running\n"); + return 1; + } + + regs = find_function(SYNAPTICS_RMI4_F51 | DATA_TYPE); + if (!regs) { + pr_err("F51 data: not found\n"); + return -ENOENT; + } + + reg = ®s->regs[0]; + if (!reg || reg->offset < 0) { + pr_err("F51 data: invalid offset\n"); + return -ENOENT; + } + + error = synaptics_rmi4_read_packet_reg(rmi4_data, regs, reg->r_number); + if (error < 0) { + pr_err("F51 data[%d]: read error\n", reg->r_number); + return -EIO; + } + + regs = find_function(SYNAPTICS_RMI4_F54 | DATA_TYPE); + if (!regs) { + pr_err("F54 data not found\n"); + return -ENOENT; + } + + reg_17 = find_packet_reg(regs, 17); + reg_10 = find_packet_reg(regs, 10); + if ((!reg_17 || reg_17->offset < 0) || + (!reg_10 || reg_10->offset < 0)) { + pr_err("F54 data: invalid offset\n"); + return -ENOENT; + } + + for (ii = 0; ii < 10; ii++) { + error = synaptics_rmi4_read_packet_reg(rmi4_data, + regs, reg_17->r_number); + if (error < 0) { + pr_err("F51 data[%d]: read error\n", reg_17->r_number); + return -EIO; + } + + error = synaptics_rmi4_read_packet_reg(rmi4_data, + regs, reg_10->r_number); + if (error < 0) { + pr_err("F51 data[%d]: read error\n", reg_10->r_number); + return -EIO; + } + + /* FIXME: it takes several attempts to read "real" value */ + /* noise state cannot be 0, thus use it as exit condition */ + if (!(int)f54_d10_0.noise_state) + msleep(50); + else + break; + } + + log = ktime_get(); + gStat.uptime = log; + gStat.uptime_run = true; + statistics_log_time(gStat.dur, (int)f54_d17_0.freq, log); + statistics_log_time(gStat.hop, (int)f54_d17_0.freq, log); + statistics_log_time(gStat.nms, (int)f54_d10_0.noise_state, log); + statistics_log_time(gStat.mpd, (int)f51_d0_0.md_present, log); + tk_debug("uptime notch; G%d:N%d:M%d\n", (int)f54_d17_0.freq, + (int)f54_d10_0.noise_state, (int)f51_d0_0.md_present); + return 0; +} + +static int statistics_init(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + int error; + + regs = find_function(SYNAPTICS_RMI4_F54 | QUERY_TYPE); + if (!regs) + return -ENOENT; + + reg = find_packet_reg(regs, 12); + if (!reg || reg->offset < 0) + return -ENOENT; + + error = synaptics_rmi4_read_packet_reg(rmi4_data, regs, reg->r_number); + if (error < 0) + return -EIO; + + /* allocate frequencies by the number of gears in gear table */ + error = statistics_alloc( + (int)f54_q12_0.num_of_scan_freq & 0xf, + 100, /* 100 last gear hops */ + 2, /* 2 NMS modes */ + 2); /* 2 guard presence modes */ + if (error < 0) + return -ENOMEM; + + error = statistics_start_timekeeping(rmi4_data); + if (error < 0) + pr_err("statistics init failed\n"); + else { + pr_notice("touch statistics enabled\n"); + gStat.enabled = true; + } + + return error; +} + +static inline void statistics_stop_timekeeping(void) +{ + /* avoid being executed twice on firmware reflash */ + if (gStat.uptime_run) { + ktime_t end = ktime_get(); + gStat.uptime_dur += timediff_ms( + ktime_to_timespec(gStat.uptime), + ktime_to_timespec(end)); + gStat.uptime_run = false; + statistics_log_time_end(gStat.dur, end); + statistics_log_time_end(gStat.hop, end); + statistics_log_time_end(gStat.nms, end); + statistics_log_time_end(gStat.mpd, end); + tk_debug("uptime %llu\n", gStat.uptime_dur); + } else + pr_debug("timekeeping already stopped\n"); + +} + +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, + unsigned short length); + +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, + unsigned short length); + +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data); + +static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable); + +static void synaptics_dsx_sensor_state(struct synaptics_rmi4_data *rmi4_data, + int state); + +static void synaptics_dsx_release_all(struct synaptics_rmi4_data *rmi4_data); + +#if defined(CONFIG_FB) && !defined(CONFIG_MMI_PANEL_NOTIFICATIONS) +static int synaptics_dsx_panel_cb(struct notifier_block *nb, + unsigned long event, void *data); +#endif + +static int synaptics_rmi4_suspend(struct device *dev); + +static int synaptics_rmi4_resume(struct device *dev); + +static ssize_t synaptics_rmi4_ud_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_resume_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_irqtimes_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_hw_irqstat_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_mod_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_mod_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_mod_sw_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_mod_sw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_query_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_query_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_reporting_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_reporting_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_stats_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_stats_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_recovery_resume_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +struct synaptics_rmi4_f01_device_status { + union { + struct { + unsigned char status_code:4; + unsigned char reserved:2; + unsigned char flash_prog:1; + unsigned char unconfigured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f01_query1 { + union { + struct { + unsigned char custom_map:1; + unsigned char non_compliant:1; + unsigned char reserved:1; + unsigned char has_sensor_id:1; + unsigned char has_charger_input:1; + unsigned char has_adjustable_doze:1; + unsigned char has_doze_holdoff:1; + unsigned char has_query42:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f01_query42 { + union { + struct { + unsigned char has_ds4_queries:1; + unsigned char has_multi_physical:1; + unsigned char has_guest:1; + unsigned char has_swr:1; + unsigned char has_report_rate:1; + unsigned char has_recalibration:1; + unsigned char reserved:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_query { + union { + struct { + unsigned char max_button_count:3; + unsigned char reserved:5; + unsigned char has_general_control:1; + unsigned char has_interrupt_enable:1; + unsigned char has_multibutton_select:1; + unsigned char has_tx_rx_map:1; + unsigned char has_perbutton_threshold:1; + unsigned char has_release_threshold:1; + unsigned char has_strongestbtn_hysteresis:1; + unsigned char has_filter_strength:1; + } __packed; + unsigned char data[2]; + }; +}; + +struct synaptics_rmi4_f1a_control_0 { + union { + struct { + unsigned char multibutton_report:2; + unsigned char filter_mode:2; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_control_3_4 { + unsigned char transmitterbutton; + unsigned char receiverbutton; +}; + +struct synaptics_rmi4_f1a_control { + struct synaptics_rmi4_f1a_control_0 general_control; + unsigned char *button_int_enable; + unsigned char *multi_button; + struct synaptics_rmi4_f1a_control_3_4 *electrode_map; + unsigned char *button_threshold; + unsigned char button_release_threshold; + unsigned char strongest_button_hysteresis; + unsigned char filter_strength; +}; + +struct synaptics_rmi4_f1a_handle { + int button_bitmask_size; + unsigned char button_count; + unsigned char valid_button_count; + unsigned char *button_data_buffer; + unsigned char *button_map; + struct synaptics_rmi4_f1a_query button_query; + struct synaptics_rmi4_f1a_control button_control; +}; + +struct synaptics_rmi4_exp_fn { + enum exp_fn fn_type; + enum ic_modes mode; + bool inserted; + int (*func_init)(struct synaptics_rmi4_data *rmi4_data); + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data); + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask); + struct list_head link; +}; + +static struct device_attribute attrs[] = { + __ATTR(reset, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + synaptics_rmi4_f01_reset_store), + __ATTR(productinfo, S_IRUGO, + synaptics_rmi4_f01_productinfo_show, + synaptics_rmi4_store_error), + __ATTR(buildid, S_IRUGO, + synaptics_rmi4_f01_buildid_show, + synaptics_rmi4_store_error), + __ATTR(flashprog, S_IRUSR | S_IRGRP, + synaptics_rmi4_f01_flashprog_show, + synaptics_rmi4_store_error), + __ATTR(0dbutton, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_0dbutton_show, + synaptics_rmi4_0dbutton_store), + __ATTR(resumeinfo, S_IRUSR | S_IRGRP, + synaptics_rmi4_resume_show, + synaptics_rmi4_store_error), + __ATTR(drv_irq, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_drv_irq_show, + synaptics_rmi4_drv_irq_store), + __ATTR(reporting, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_reporting_show, + synaptics_rmi4_reporting_store), + __ATTR(stats, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_stats_show, + synaptics_rmi4_stats_store), + __ATTR(hw_irqstat, S_IRUSR | S_IRGRP, + synaptics_rmi4_hw_irqstat_show, + synaptics_rmi4_store_error), + __ATTR(ic_ver, S_IRUGO, + synaptics_rmi4_ic_ver_show, + synaptics_rmi4_store_error), + __ATTR(irqinfo, S_IRUSR | S_IRGRP, + synaptics_rmi4_irqtimes_show, + synaptics_rmi4_store_error), + __ATTR(poweron, S_IRUSR | S_IRGRP, + synaptics_rmi4_poweron_show, + synaptics_rmi4_store_error), + __ATTR(mod, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_mod_show, + synaptics_rmi4_mod_store), + __ATTR(mod_sw, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_mod_sw_show, + synaptics_rmi4_mod_sw_store), + __ATTR(query, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_query_show, + synaptics_rmi4_query_store), + __ATTR(tsi, S_IRUSR | S_IRGRP, + synaptics_rmi4_ud_show, + synaptics_rmi4_store_error), + __ATTR(recovery_resume, (S_IWUSR | S_IWGRP), + synaptics_rmi4_show_error, + synaptics_rmi4_recovery_resume_store), +}; + +struct synaptics_exp_fn_ctrl { + bool inited; + struct mutex list_mutex; + struct list_head fn_list; + struct delayed_work det_work; + struct workqueue_struct *det_workqueue; + struct synaptics_rmi4_data *rmi4_data_ptr; +}; + +DEFINE_MUTEX(exp_fn_ctrl_mutex); +static struct synaptics_exp_fn_ctrl exp_fn_ctrl; +static struct semaphore reset_semaphore; + +static int tpd_gloved_control(unsigned int on) +{ + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + int retval; + + mutex_lock(&exp_fn_ctrl_mutex); + rmi4_data = exp_fn_ctrl.rmi4_data_ptr; + mutex_unlock(&exp_fn_ctrl_mutex); + + regs = find_function(SYNAPTICS_RMI4_F12); + if (!regs) + return -ENOENT; + + reg = find_packet_reg(regs, 23); + if (!reg) + return -ENOENT; + + if (!reg || !reg->size) { + pr_err("register %d not found or zero size\n", reg->r_number); + return -EINVAL; + } + + if (reg->offset == -1) { + pr_err("touch register error: can't read r%d - not present\n", + reg->r_number); + return -ENOENT; + } + + retval = rmi4_data->i2c_read(rmi4_data, + regs->base_addr + reg->offset, + reg->data, 1); + pr_debug("read value 0x%02x.\n",*(reg->data)); + + if(on) { + *(reg->data) |= 0x20; //bit 5 + }else { + *(reg->data) &= 0xDF; + } + retval = rmi4_data->i2c_write(rmi4_data, + regs->base_addr + reg->offset, + reg->data , 1); + + pr_debug("F%02x%c reading r%d{%X}, size %d, after value 0x%02x.\n", + regs->f_number & 0xff, + register_type_to_ascii(regs->f_number & 0xf00), + reg->r_number, regs->base_addr + reg->offset, + reg->size, *(reg->data)); + if (retval < 0) + return retval; + + return retval; +} + +int get_tpd_info(void) +{ + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_rmi4_device_info *rmi; + unsigned int build_id, config_id; + + mutex_lock(&exp_fn_ctrl_mutex); + rmi4_data = exp_fn_ctrl.rmi4_data_ptr; + mutex_unlock(&exp_fn_ctrl_mutex); + if(!tpd_info_t) + return -1; + + rmi = &(rmi4_data->rmi4_mod_info); + batohui(&build_id, rmi->build_id, sizeof(rmi->build_id)); + batohui(&config_id, rmi->config_id, sizeof(rmi->config_id)); + + + pr_err( + "%s%s\n%s%x\n%s%x\n", + "Product ID: ", rmi->product_id_string, + "Build ID: ", build_id, + "Config ID: ", config_id); + + tpd_info_t->product_id = rmi->product_id_string; + tpd_info_t->build_id = build_id; + tpd_info_t->config_id = config_id; + + have_correct_setting = 1; + return 0; +} +EXPORT_SYMBOL(get_tpd_info); + +unsigned int get_glove_ctrl(void) +{ + return atomic_read(&glove_ctrl); +} +EXPORT_SYMBOL(get_glove_ctrl); + +void set_glove_ctrl(unsigned int val) +{ + if(val == 1) { + if(atomic_cmpxchg(&glove_ctrl, 0, 1) == 0) { + pr_err("%s, setting glove flag is = %d.\n", + __func__, atomic_read(&glove_ctrl)); + tpd_gloved_control(val); + }else { + pr_err("%s, setting glove function has been opened = %d.\n", + __func__, atomic_read(&glove_ctrl)); + return; + } + } else { + if(atomic_cmpxchg(&glove_ctrl, 1, 0) == 1) + { + pr_err("%s, setting glove flag is = %d.\n", + __func__, atomic_read(&glove_ctrl)); + tpd_gloved_control(val); + }else { + pr_err("%s, setting glove function has been closed = %d.\n", + __func__, atomic_read(&glove_ctrl)); + return; + } + } +} +EXPORT_SYMBOL(set_glove_ctrl); + +static irqreturn_t synaptics_dsx_reset_irq(int irq, void *data) +{ + struct semaphore *sema = data; + up(sema); + return IRQ_HANDLED; +} + +int synaptics_rmi4_sw_reset(struct synaptics_rmi4_data *rmi4_data, + bool requery) { + int retval; + unsigned char page_number; + unsigned short pdt_entry_addr; + struct synaptics_rmi4_fn_desc rmi_fd; + unsigned short cmd_reg_addr; + bool cmd_reg_found = false; + + if (!requery) { + cmd_reg_addr = rmi4_data->f01_cmd_base_addr; + cmd_reg_found = true; + goto sw_reset; + } + + for (page_number = 0; !cmd_reg_found && page_number < PAGES_TO_SERVICE; + page_number++) { + for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END; + pdt_entry_addr -= PDT_ENTRY_SIZE) { + pdt_entry_addr |= (page_number << 8); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + pdt_entry_addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + if (rmi_fd.fn_number == 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Reached end of PDT\n", + __func__); + break; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: F%02x found (page %d)\n", + __func__, rmi_fd.fn_number, + page_number); + + if (rmi_fd.fn_number == SYNAPTICS_RMI4_F01) { + cmd_reg_addr = rmi_fd.cmd_base_addr; + cmd_reg_found = true; + break; + } + } + } + +sw_reset: + if (cmd_reg_found) { + uint8_t command = 1; + retval = synaptics_rmi4_i2c_write(rmi4_data, + cmd_reg_addr, + &command, sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to issue SW reset command, error = %d\n", + __func__, retval); + return retval; + } + return 0; + } else + return -ENOENT; + +} + + +static int synaptics_dsx_ic_reset( + struct synaptics_rmi4_data *rmi4_data, int reset) +{ + int retval; + unsigned long start = jiffies; + const struct synaptics_dsx_platform_data *platform_data = + rmi4_data->board; + + sema_init(&reset_semaphore, 0); + + if (reset == RMI4_HW_RESET) { + gpio_set_value(platform_data->reset_gpio, 0); + udelay(1500); + } else if (reset == RMI4_SW_RESET) { + retval = synaptics_rmi4_sw_reset(rmi4_data, true); + if (retval < 0) + return retval; + } + + retval = request_irq(rmi4_data->irq, synaptics_dsx_reset_irq, + IRQF_TRIGGER_RISING, "synaptics_reset", + &reset_semaphore); + if (retval < 0) + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to request irq: %d\n", + __func__, retval); + + if (reset == RMI4_HW_RESET) + gpio_set_value(platform_data->reset_gpio, 1); + + retval = down_timeout(&reset_semaphore, msecs_to_jiffies(100)); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "timed out waiting for reset to complete\n"); + retval = -ETIMEDOUT; + } else { + retval = (int)jiffies_to_msecs(jiffies-start); + /* insert delay to ensure 1st i2c bus access succeeds */ + udelay(1000); + } + + free_irq(rmi4_data->irq, &reset_semaphore); + + /* perform SW reset if HW reset failed */ + if (reset != RMI4_SW_RESET && retval == -ETIMEDOUT) + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_SW_RESET); + + return retval; +} + +static int synaptics_dsx_alloc_input(struct synaptics_rmi4_data *rmi4_data) +{ + rmi4_data->input_dev = input_allocate_device(); + if (IS_ERR_OR_NULL(rmi4_data->input_dev)) + return PTR_ERR(rmi4_data->input_dev); + + rmi4_data->input_dev->name = DRIVER_NAME; + rmi4_data->input_dev->phys = INPUT_PHYS_NAME; + rmi4_data->input_dev->id.bustype = BUS_I2C; + rmi4_data->input_dev->dev.parent = &rmi4_data->i2c_client->dev; + + set_bit(EV_SYN, rmi4_data->input_dev->evbit); + /* enable power key injection */ + set_bit(EV_KEY, rmi4_data->input_dev->evbit); + input_set_capability(rmi4_data->input_dev, EV_KEY, KEY_SLIDE); + + pr_debug("allocated input device\n"); + + return 0; +} + +static void synaptics_copy_multiple_subpkts( + struct synaptics_rmi4_packet_reg *reg, + struct synaptics_dsx_func_patch *fp) +{ + int i, b2c, leftover = fp->size; + unsigned char *data_ptr = fp->data; + struct synaptics_rmi4_subpkt *subpkt; + + for (i = fp->subpkt; i < reg->nr_subpkts; i++) { + subpkt = reg->subpkt + i; + if (!subpkt->present || !subpkt->data) + continue; + b2c = min(leftover, (int)subpkt->size); + memcpy(subpkt->data, data_ptr, b2c); + pr_debug("copied %d bytes to subpkt %d\n", b2c, i); + data_ptr += subpkt->size; + leftover -= subpkt->size; + } + pr_debug("%s misalignement detected\n", !leftover ? "no" : ""); +} + +static void synaptics_dsx_patch_function( + struct synaptics_rmi4_data *rmi4_data, int f_number, + struct synaptics_dsx_patch *patch) +{ + int r, error, function; + unsigned char *destination; + unsigned char *value, rt_mod; + struct device *dev = &rmi4_data->i2c_client->dev; + struct synaptics_rmi4_subpkt *subpkt; + struct synaptics_dsx_func_patch *fp; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_func_packet_regs *regs = find_function(f_number); + + function = regs->f_number & 0xff; + rt_mod = register_type_to_ascii(regs->f_number & 0xf00); + pr_debug("patching F%x%c\n", function, rt_mod); + down(&patch->list_sema); + list_for_each_entry(fp, &patch->cfg_head, link) { + if (fp->func != f_number) + continue; + reg = find_packet_reg(regs, fp->regstr); + if (!reg || reg->offset < 0) { + pr_err("F%x%c@%d not present\n", + function, rt_mod, fp->regstr); + continue; + } + if (fp->subpkt >= reg->nr_subpkts) { + pr_err("F%x%c@%d:%d not present\n", + function, rt_mod, fp->regstr, fp->subpkt); + continue; + } + + /* read register only once */ + if (!reg->updated) { + error = synaptics_rmi4_read_packet_reg( + rmi4_data, regs, reg->r_number); + if (error < 0) + goto unlock_and_leave; +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + { + int ss, kk; + pr_debug("F%x%c@%d before patching:\n", + function, rt_mod, reg->r_number); + for (ss = 0; ss < reg->nr_subpkts; ss++) { + subpkt = reg->subpkt + ss; + for (kk = 0; kk < subpkt->size; ++kk) + pr_debug("%02x\n", + ((unsigned char *)subpkt->data)[kk]); + } + } +#endif + /* value has been updated */ + reg->updated = true; + } + + subpkt = reg->subpkt + fp->subpkt; + destination = (unsigned char *)subpkt->data; + if (!subpkt->present || !subpkt->data || + subpkt->size < fp->size) { + pr_debug("F%x%c@%d:%d subpkt: size=%d, offset=%d; reg-size=%d; fp-size=%d\n", + function, rt_mod, + fp->regstr, fp->subpkt, + subpkt->size, subpkt->offset, + reg->size, fp->size); + /* If more than one consequitive subpacket within */ + /* the same packet register needs to be updated, */ + /* then patch might include data for all subpackets */ + /* combined into a single array. Then its size will */ + /* be more than individual subpacket size. It should */ + /* not be a problem as packet register is updated */ + /* all in once. And as long as size of data array */ + /* fits the register, update should be allowed */ + if (reg->size <= (subpkt->offset + fp->size)) { + pr_debug("F%x%c@%d:%d improperly allocated\n", + function, rt_mod, + fp->regstr, fp->subpkt); + continue; + } else { + synaptics_copy_multiple_subpkts(reg, fp); + /* whole patch has been applied already */ + destination = NULL; + } + } + if (fp->bitmask && fp->size == 1) { + value = (unsigned char *)subpkt->data; + pr_debug("patching by mask: source 0x%x\n", *value); + *value &= ~(fp->bitmask); + pr_debug("patching by mask: clear-ed 0x%x\n", *value); + *value |= *fp->data; + pr_debug("patching by mask: result 0x%x\n", *value); + /* value has been modified already */ + destination = NULL; + } + + if (destination) + memcpy(destination, fp->data, fp->size); + + /* value has been changed */ + reg->modified = true; + + dev_dbg(dev, "patched F%x%c@%d:%d, sz=%d\n", + function, rt_mod, fp->regstr, fp->subpkt, fp->size); + } + + /* update loop */ + for (r = 0; r < regs->nr_regs; r++) { + reg = ®s->regs[r]; + if (reg->updated && reg->modified) { +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + { + int ss, kk; + pr_debug("F%x%c@%d after patching:\n", + function, rt_mod, reg->r_number); + for (ss = 0; ss < reg->nr_subpkts; ss++) { + subpkt = reg->subpkt + ss; + for (kk = 0; kk < subpkt->size; ++kk) + pr_debug("%02x\n", + ((unsigned char *)subpkt->data)[kk]); + } + } +#endif + error = synaptics_rmi4_write_packet_reg(rmi4_data, + regs, reg->r_number); + if (error < 0) + dev_warn(dev, "F%x%c@%d patch write failed\n", + function, rt_mod, reg->r_number); + reg->modified = false; + } + reg->updated = false; + } + +unlock_and_leave: + up(&patch->list_sema); +} + +static void synaptics_dsx_enable_wakeup_source( + struct synaptics_rmi4_data *rmi4_data, bool enable) +{ + int error; + error = irq_set_irq_wake(rmi4_data->irq, (int)enable); + pr_debug("%s wakeup; rc=%d\n", enable ? "enabled" : "disabled", error); +} + +static const char * const synaptics_state_names[] = {"UNKNOWN", + "ACTIVE", "SUSPEND", "UNUSED", "STANDBY", "BL", "INIT", + "FLASH", "QUERY", "INVALID" }; + +static const char *synaptics_dsx_state_name(int state) +{ + int index = state < 0 || state > STATE_INVALID ? STATE_INVALID : state; + return synaptics_state_names[index]; +} + +static void synaptics_dsx_apply_modifiers( + struct synaptics_rmi4_data *rmi4_data, int state) +{ + bool wakeup = false, update = false, sleep = false; + int i; + + if (!rmi4_data->modifiers.mods_num) { + pr_debug("patchset is empty!\n"); + goto no_modifiers; + } + + down(&rmi4_data->modifiers.list_sema); + for (i = 0; i < ARRAY_SIZE(synaptics_cfg_regs); i++) { + struct config_modifier *cm; + + /* skip query registers */ + if (synaptics_cfg_regs[i].f_number & QUERY_TYPE) + continue; + + list_for_each_entry(cm, &rmi4_data->modifiers.mod_head, link) { + struct synaptics_dsx_patch *patch = NULL; + + /* skip disabled modifiers */ + if (!cm->effective) + continue; + + pr_debug("cm->name = %s.\n", cm->name); + if (!strncmp(cm->name, "wakeup", 6)) { + if (!atomic_read(&wakeup_gesture)) { + pr_debug("No enable wakeup function, ignore!\n"); + continue; + } + } + + if (state == STATE_ACTIVE) + patch = cm->active; + else if (state == STATE_SUSPEND) + patch = cm->suspended; + + /* skip effective modifier without patch */ + if (!patch) + continue; + + /* keep track of flags among effective modifiers */ + if (!update && (patch->flags & FLAG_FORCE_UPDATE)) + update = true; + if (!wakeup && (patch->flags & FLAG_WAKEABLE) && atomic_read(&wakeup_gesture)) + wakeup = true; + if (!sleep && (patch->flags & FLAG_POWER_SLEEP)) + sleep = true; + /* finally apply patch */ + synaptics_dsx_patch_function(rmi4_data, + synaptics_cfg_regs[i].f_number, patch); + } + } + up(&rmi4_data->modifiers.list_sema); + + /* force update regardless the current state */ + if (update && force_update_patch) + synaptics_dsx_patch_function(rmi4_data, + SYNAPTICS_RMI4_F54 | COMMAND_TYPE, force_update_patch); + + /* power mode and wakeability only on entering suspend */ + if (state == STATE_SUSPEND) { + if (wakeup) { + rmi4_data->suspend_is_wakeable = true; + synaptics_dsx_enable_wakeup_source(rmi4_data, true); + } else if (sleep && power_sleep_patch) + synaptics_dsx_patch_function(rmi4_data, + SYNAPTICS_RMI4_F01, power_sleep_patch); + } + + if (!wakeup && rmi4_data->suspend_is_wakeable) { + rmi4_data->suspend_is_wakeable = false; + synaptics_dsx_enable_wakeup_source(rmi4_data, false); + } + +no_modifiers: + /* keep page 0 active */ + synaptics_rmi4_set_page(rmi4_data, 0); +} + +static int synaptics_dsx_get_state_safe(struct synaptics_rmi4_data *rmi4_data) +{ + int state; + mutex_lock(&(rmi4_data->state_mutex)); + state = rmi4_data->state; + mutex_unlock(&(rmi4_data->state_mutex)); + return state; +} + +static void synaptics_dsx_set_state_safe(struct synaptics_rmi4_data *rmi4_data, + int state) +{ + mutex_lock(&(rmi4_data->state_mutex)); + rmi4_data->state = state; + mutex_unlock(&(rmi4_data->state_mutex)); +} + +static int synaptics_dsx_wait_for_idle(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned long start_wait_jiffies = jiffies; + + do { + int current_state; + + current_state = synaptics_dsx_get_state_safe(rmi4_data); + if (!(current_state == STATE_INIT || + current_state == STATE_FLASH || + current_state == STATE_UNKNOWN)) + break; + + usleep_range(1000, 1000); + + } while (1); + + if ((jiffies - start_wait_jiffies)) + pr_info("entering suspend delayed for %ums\n", + jiffies_to_msecs(jiffies - start_wait_jiffies)); + return 0; +} + +static int synaptics_dsx_sensor_ready_state( + struct synaptics_rmi4_data *rmi4_data, bool standby) +{ + bool ui_mode; + int retval, state, i; + struct synaptics_rmi4_f01_device_status status; + + for (i = 0; i < 10; ++i) { + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) { + pr_err("(%d) Failed to query touch ic status\n", i); + return retval; + } + + ui_mode = status.flash_prog == 0; + pr_debug("(%d) UI mode: %s\n", i, ui_mode ? "true" : "false"); + + if (ui_mode) + break; + + msleep(20); + } + + state = synaptics_dsx_get_state_safe(rmi4_data); + if (!ui_mode && state == STATE_SUSPEND && rmi4_data->input_registered) { + /* expecting touch IC to enter UI mode based on */ + /* its previous known good state */ + pr_err("Timed out waiting for UI mode - UI mode forced\n"); + ui_mode = 1; + } + + if (ui_mode) { + state = standby ? STATE_STANDBY : STATE_ACTIVE; + } else + if (!(state == STATE_INIT || state == STATE_FLASH)) + state = STATE_BL; + + synaptics_dsx_sensor_state(rmi4_data, state); + + return 0; +} + +static void synaptics_dsx_enforce_modifiers( + struct synaptics_rmi4_data *rmi4_data, + struct config_modifier *modifier) +{ + int error, state = synaptics_dsx_get_state_safe(rmi4_data); + + if (state == STATE_ACTIVE) { + if (!modifier->active) + return; + /* set unknown state to ensure IRQ gets */ + /* enabled on state transition to active */ + synaptics_dsx_sensor_state(rmi4_data, STATE_UNKNOWN); + /* disable IRQ to handle reset */ + synaptics_rmi4_irq_enable(rmi4_data, false); + /* perform SW reset to restore defaults */ + error = synaptics_dsx_ic_reset(rmi4_data, RMI4_SW_RESET); + if (error < 0) + dev_err(&rmi4_data->i2c_client->dev, + "folio: sw reset failed %d\n", error); + } + synaptics_dsx_sensor_ready_state(rmi4_data, false); +} + +static void synaptics_dsx_sensor_state(struct synaptics_rmi4_data *rmi4_data, + int state) +{ + if (synaptics_dsx_get_state_safe(rmi4_data) == state) + return; + + switch (state) { + case STATE_UNKNOWN: + case STATE_FLASH: + /* no special handling for these states */ + break; + + case STATE_SUSPEND: + synaptics_dsx_wait_for_idle(rmi4_data); + + if (!rmi4_data->in_bootloader) + synaptics_dsx_apply_modifiers(rmi4_data, STATE_SUSPEND); + + if (!rmi4_data->suspend_is_wakeable) + synaptics_rmi4_irq_enable(rmi4_data, false); + break; + + case STATE_ACTIVE: + if (!rmi4_data->in_bootloader) + synaptics_dsx_apply_modifiers(rmi4_data, STATE_ACTIVE); + + if (rmi4_data->input_registered) + synaptics_rmi4_irq_enable(rmi4_data, true); + else { + synaptics_rmi4_irq_enable(rmi4_data, false); + pr_err("Active state without input device\n"); + } + + if (gStat.enabled) + statistics_start_timekeeping(rmi4_data); + break; + + case STATE_STANDBY: + synaptics_rmi4_irq_enable(rmi4_data, false); + break; + + case STATE_BL: + if (!rmi4_data->in_bootloader) + rmi4_data->in_bootloader = true; + case STATE_INIT: + /* de-allocate input device earlier to allow */ + /* EventHub become notified of input removal */ + if (rmi4_data->input_registered) { + input_unregister_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + rmi4_data->input_registered = false; + + pr_debug("de-allocated input device\n"); + } + + synaptics_rmi4_irq_enable(rmi4_data, false); + + if (gStat.enabled) + statistics_stop_timekeeping(); + break; + } + + pr_info("state change %s -> %s\n", + synaptics_dsx_state_name(rmi4_data->state), + synaptics_dsx_state_name(state)); + + synaptics_dsx_set_state_safe(rmi4_data, state); +} + +static struct touch_up_down display_ud[20]; +static struct touch_area_stats display_ud_stats = { + .ud = display_ud, + .ud_len = ARRAY_SIZE(display_ud), + .name = "ts" +}; + +static struct touch_up_down button_ud[10]; +static struct touch_area_stats button_ud_stats = { + .ud = button_ud, + .ud_len = ARRAY_SIZE(button_ud), + .name = "btn" +}; + +static void ud_set_id(struct touch_area_stats *tas, int id) +{ + tas->ud_id = id; +} + +static void ud_log_status(struct touch_area_stats *tas, bool down) +{ + struct touch_up_down *ud = tas->ud; + ssize_t id = tas->ud_id; + + if (id >= tas->ud_len) + tas->unknown_counter++; + + if (!down) { /* up */ + if (ud[id].up_down == 0x10) { + pr_debug("%s UP[%zu]\n", tas->name, id); + ud[id].up_down |= 1; + ud[id].mismatch--; + } + } else if (down) { /* down */ + if (ud[id].up_down == 0) { + ud[id].up_down |= (1 << 4); + pr_debug("%s DOWN[%zu]\n", tas->name, id); + ud[id].mismatch++; + } else if (ud[id].up_down == 0x10) + return; + } + + if (ud[id].up_down == 0x11) { + pr_debug("%s CLEAR[%zu]\n", tas->name, id); + ud[id].up_down = 0; + ud[id].counter++; + } +} + +static void TSI_state(struct input_dev *dev, unsigned int tool, bool status) +{ + ud_log_status(&display_ud_stats, status); + input_mt_report_slot_state(dev, tool, status); +} + +static void TSI_id(struct input_dev *dev, int id) +{ + ud_set_id(&display_ud_stats, id); + input_mt_slot(dev, id); +} + +#define input_mt_report_slot_state TSI_state +#define input_mt_slot TSI_id + +static ssize_t synaptics_rmi4_ud_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + ssize_t total = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + total += scnprintf(buf + total, PAGE_SIZE - total, "display:\n"); + for (i = 0; i < display_ud_stats.ud_len; i++) + total += scnprintf(buf + total, PAGE_SIZE - total, + "[%d]: full cycles-%u, mismatch-%d\n", i, + display_ud[i].counter, + display_ud[i].mismatch); + + if (rmi4_data->button_0d_enabled) { + total += scnprintf(buf + total, PAGE_SIZE - total, + "buttons:\n"); + for (i = 0; i < button_ud_stats.ud_len; i++) + total += scnprintf(buf + total, PAGE_SIZE - total, + "[%d]: full cycles-%u, mismatch-%d\n", i, + button_ud[i].counter, button_ud[i].mismatch); + } + return total; +} + +static ssize_t synaptics_dsx_ud_stat(struct synaptics_rmi4_data *rmi4_data, + char *buf, ssize_t size) +{ + int i; + ssize_t total = 0; + + total += scnprintf(buf + total, size - total, "screen: "); + for (i = 0; i < display_ud_stats.ud_len; i++) + if (display_ud[i].mismatch) + total += scnprintf(buf + total, size - total, + "%d)%u,%d ", i, + display_ud[i].counter, + display_ud[i].mismatch); + else if (display_ud[i].counter) + total += scnprintf(buf + total, size - total, + "%d)%u ", i, + display_ud[i].counter); + + if (rmi4_data->button_0d_enabled) { + total += scnprintf(buf + total, size - total, "buttons: "); + for (i = 0; i < button_ud_stats.ud_len; i++) + if (button_ud[i].mismatch) + total += scnprintf(buf + total, size - total, + "%d)%u,%d ", i, + button_ud[i].counter, + button_ud[i].mismatch); + else if (button_ud[i].counter) + total += scnprintf(buf + total, size - total, + "%d)%u ", i, + button_ud[i].counter); + } + return total; +} + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int reset; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + + if (sscanf(buf, "%u", &reset) != 1) + return -EINVAL; + + if (reset != 1) + return -EINVAL; + + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_HW_RESET); + if (retval > 0) + pr_debug("successful reset took %dms\n", retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + return count; +} + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%s\n", + rmi4_data->rmi4_mod_info.product_id_string); +} + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int firmware_id; + unsigned int config_id; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + rmi = &(rmi4_data->rmi4_mod_info); + + batohui(&firmware_id, rmi->build_id, sizeof(rmi->build_id)); + batohui(&config_id, rmi->config_id, sizeof(rmi->config_id)); + + return scnprintf(buf, PAGE_SIZE, "%x-%08x\n", firmware_id, config_id); +} + +static ssize_t synaptics_rmi4_resume_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + int c_res; + int offset = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + c_res = rmi4_data->last_resume; + /* Resume buffer not allocated or there were no resumes yet */ + if (rmi4_data->number_resumes <= 0 || c_res < 0) + return scnprintf(buf, PAGE_SIZE, + "No resume information found.\n"); + + offset += scnprintf(buf + offset, PAGE_SIZE - offset, + "Count\tStart\t\tFinish\t# ignored\t" + "ISR\t\tpurge off\tsendevent\n"); + + for (i = 0; i < rmi4_data->number_resumes; i++) { + offset += scnprintf(buf + offset, PAGE_SIZE - offset, + "%d\t%4ld.%03ld\t%4ld.%03ld\t%d\t%4ld.%03ld\t" + "%4ld.%03ld\t%4ld.%03ld\n", + i+1, + rmi4_data->resume_info[c_res].start.tv_sec%1000, + rmi4_data->resume_info[c_res].start.tv_nsec/1000000, + rmi4_data->resume_info[c_res].finish.tv_sec%1000, + rmi4_data->resume_info[c_res].finish.tv_nsec/1000000, + rmi4_data->resume_info[c_res].ignored_events, + rmi4_data->resume_info[c_res].isr.tv_sec%1000, + rmi4_data->resume_info[c_res].isr.tv_nsec/1000000, + rmi4_data->resume_info[c_res].purge_off.tv_sec%1000, + rmi4_data->resume_info[c_res].purge_off.tv_nsec/1000000, + rmi4_data->resume_info[c_res].send_touch.tv_sec%1000, + rmi4_data->resume_info[c_res].send_touch.tv_nsec/1000000); + + if (c_res <= 0) + c_res = rmi4_data->number_resumes-1; + else + c_res--; + } + return offset; +} + +static ssize_t synaptics_rmi4_irqtimes_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + int c_res; + int offset = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + c_res = rmi4_data->last_irq; + /* Resume buffer not allocated or there were no irq data collected yet*/ + if (rmi4_data->number_irq <= 0 || c_res < 0) + return scnprintf(buf, PAGE_SIZE, + "No resume information found.\n"); + + offset += scnprintf(buf + offset, PAGE_SIZE - offset, + "Count\tIRQ Start\n"); + + for (i = 0; i < rmi4_data->number_irq; i++) { + offset += scnprintf(buf + offset, PAGE_SIZE - offset, + "%d\t%4ld.%03ld\n", + i+1, + rmi4_data->irq_info[i].irq_time.tv_sec%1000, + rmi4_data->irq_info[i].irq_time.tv_nsec/1000000); + + if (c_res <= 0) + c_res = rmi4_data->number_irq-1; + else + c_res--; + } + return offset; +} + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->in_bootloader); +} + +static ssize_t synaptics_rmi4_hw_irqstat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + switch (gpio_get_value(rmi4_data->board->irq_gpio)) { + case 0: + return scnprintf(buf, PAGE_SIZE, "Low\n"); + case 1: + return scnprintf(buf, PAGE_SIZE, "High\n"); + default: + pr_err("Failed to get GPIO for irq %d\n", rmi4_data->irq); + return scnprintf(buf, PAGE_SIZE, "Unknown\n"); + } +} + +#define SPRINTF_PAGE(fmt, args...) {\ + blen += scnprintf(buf + blen, PAGE_SIZE - blen, fmt, ##args);\ + } + +#include <asm/div64.h> + +static ssize_t synaptics_rmi4_stats_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ii, gear, state, percentage; + bool count_time_since; + ssize_t blen = 0; + unsigned long long gear_time, check_point, perc_u64; + unsigned long long total = gStat.uptime_dur; + unsigned long long duration = (unsigned long long)0; + struct statistics *stats; + struct timespec end = ktime_to_timespec(ktime_get()); + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + if (gStat.enabled == false) { + pr_warn("statistics is not enabled\n"); + return blen; + } + + /* if statistics fetched while driver in suspend, time */ + /* since phone has entered suspend should not be added */ + count_time_since = atomic_read(&rmi4_data->touch_stopped) == 0; + if (count_time_since) { + duration = timediff_ms(ktime_to_timespec(gStat.uptime), end); + tk_debug("uptime since last resume %llu\n", duration); + total += duration; + } + tk_debug("total sweep time %llums\n", total); + + /* currently active gear */ + gear = gStat.dur->active; + stats = gStat.dur; + check_point = (unsigned long long)0; + if (count_time_since) { + duration = timediff_ms(ktime_to_timespec(stats->start), end); + pr_debug("%c time since last resume %llu\n", + stats->abbr, duration); + } + for (ii = 0; ii < stats->max; ii++) { + gear_time = stats->keeper[ii].duration; + if (ii == gear) + gear_time += duration; + perc_u64 = 100000*gear_time; + do_div(perc_u64, total); + percentage = (int)perc_u64; + SPRINTF_PAGE("%c%d: %d%%\n", stats->abbr, ii, + percentage/1000); + check_point += gear_time; + tk_debug("%c%d: %d%% %llu/%llu\n", stats->abbr, + (stats->flags & ONE_BASED_INDEX) ? ii+1 : ii, + percentage/1000, gear_time, total); + } + tk_debug("gear time check point: %llu <-> %llu\n", check_point, total); + + /* currently active NSM state */ + state = gStat.nms->active; + stats = gStat.nms; + check_point = (unsigned long long)0; + if (count_time_since) { + duration = timediff_ms(ktime_to_timespec(stats->start), end); + pr_debug("%c time since last resume %llu\n", + stats->abbr, duration); + } + for (ii = 0; ii < stats->max; ii++) { + gear_time = stats->keeper[ii].duration; + if (ii == state) + gear_time += duration; + perc_u64 = 100000*gear_time; + do_div(perc_u64, total); + percentage = (int)perc_u64; + SPRINTF_PAGE("%c%d: %d%%\n", stats->abbr, + (stats->flags & ONE_BASED_INDEX) ? ii+1 : ii, + percentage/1000); + check_point += gear_time; + tk_debug("%c%d: %d%% %llu/%llu\n", stats->abbr, ii, + percentage/1000, gear_time, total); + } + tk_debug("NMS time check point: %llu <-> %llu\n", check_point, total); + + /* current guard state */ + state = gStat.mpd->active; + stats = gStat.mpd; + check_point = (unsigned long long)0; + if (count_time_since) { + duration = timediff_ms(ktime_to_timespec(stats->start), end); + pr_debug("%c time since last resume %llu\n", + stats->abbr, duration); + } + for (ii = 0; ii < stats->max; ii++) { + gear_time = stats->keeper[ii].duration; + if (ii == state) + gear_time += duration; + perc_u64 = 100000*gear_time; + do_div(perc_u64, total); + percentage = (int)perc_u64; + SPRINTF_PAGE("%c%d: %d%%\n", stats->abbr, + (stats->flags & ONE_BASED_INDEX) ? ii+1 : ii, + percentage/1000); + check_point += gear_time; + tk_debug("%c%d: %d%% %llu/%llu\n", stats->abbr, ii, + percentage/1000, gear_time, total); + } + tk_debug("MPG time check point: %llu <-> %llu\n", check_point, total); + + stats = gStat.hop; + check_point = (unsigned long long)0; + if (count_time_since) { + duration = timediff_ms(ktime_to_timespec(stats->start), end); + pr_debug("%c time since last resume %llu\n", + stats->abbr, duration); + } + /* show starting from the oldest hops */ + for (ii = 1; ii <= stats->max; ii++) { + int idx = stats->active + ii; + + if (idx >= stats->max) + idx -= stats->max; + if (idx < 0 || idx > stats->max) { + pr_warn("idx outside the range\n"); + break; + } + pr_debug("idx[%d], id[%d]\n", idx, stats->keeper[idx].id); + if (stats->keeper[idx].id != -1) { + gear_time = stats->keeper[idx].duration; + /* add extra time to the last hop record only */ + if (ii == stats->max && stats->keeper[idx].id == gear) + gear_time += duration; + SPRINTF_PAGE("%c%d: %llums\n", stats->abbr, + stats->keeper[idx].id, gear_time); + check_point += gear_time; + } + } + tk_debug("uptime check point: %llu <-> %llu\n", check_point, total); + return blen; +} + +static ssize_t synaptics_rmi4_stats_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + if (gStat.enabled) { + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + statistics_reset(); + pr_info("statistics reset\n"); + statistics_start_timekeeping(rmi4_data); + } + return count; +} + +static bool reporting_stopped; +static unsigned int events_cnt; + +static ssize_t synaptics_rmi4_reporting_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + if (reporting_stopped) + length = scnprintf(buf, PAGE_SIZE, "STOPPED(%u)\n", events_cnt); + else + length = scnprintf(buf, PAGE_SIZE, "RUNNING\n"); + return length; +} + +static ssize_t synaptics_rmi4_reporting_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + int err = 0; + + if (*buf == 's' || *buf == 'S') { + reporting_stopped = true; + } else if (*buf == 'r' || *buf == 'R') { + reporting_stopped = false; + } else { + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + reporting_stopped = value == 0; + } + if (reporting_stopped) + events_cnt = 0; + return count; +} + +static ssize_t synaptics_rmi4_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%s\n", + rmi4_data->irq_enabled ? "ENABLED" : "DISABLED"); +} + +static ssize_t synaptics_rmi4_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + int err = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + switch (value) { + case 0: + /* Disable irq */ + synaptics_rmi4_irq_enable(rmi4_data, false); + break; + case 1: + /* Enable irq */ + synaptics_rmi4_irq_enable(rmi4_data, true); + break; + default: + pr_err("Invalid value\n"); + return -EINVAL; + } + return count; +} + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->button_0d_enabled); +} + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + unsigned char ii; + unsigned char intr_enable; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + rmi = &(rmi4_data->rmi4_mod_info); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + input = input > 0 ? 1 : 0; + + if (rmi4_data->button_0d_enabled == input) + return count; + + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) { + ii = fhandler->intr_reg_num; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + + if (input == 1) + intr_enable |= fhandler->intr_mask; + else + intr_enable &= ~fhandler->intr_mask; + + retval = synaptics_rmi4_i2c_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + } + } + + rmi4_data->button_0d_enabled = input; + + return count; +} + +static ssize_t synaptics_rmi4_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int build_id, config_id; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + rmi = &(rmi4_data->rmi4_mod_info); + batohui(&build_id, rmi->build_id, sizeof(rmi->build_id)); + batohui(&config_id, rmi->config_id, sizeof(rmi->config_id)); + return scnprintf(buf, PAGE_SIZE, + "%s%s\n%s%x\n%s%x\n", + "Product ID: ", rmi->product_id_string, + "Build ID: ", build_id, + "Config ID: ", config_id); +} + +static ssize_t synaptics_rmi4_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(&rmi4_data->touch_stopped) == 0 && + rmi4_data->flash_enabled); +} + +static ssize_t synaptics_dsx_patch_dump( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_dsx_patch *patch, + char *pout, ssize_t msize) +{ + struct synaptics_dsx_func_patch *fp; + ssize_t blen = 0; + + down(&patch->list_sema); + list_for_each_entry(fp, &patch->cfg_head, link) { + int i; + unsigned int data_size = fp->size; + unsigned char *value = fp->data; + + blen += scnprintf(pout + blen, msize - blen, + "F%02x%c@%d:%d=", fp->func & 0xff, + register_type_to_ascii(fp->func & 0xf00), + fp->regstr, fp->subpkt); + + for (i = 0; i < data_size; i++) + blen += scnprintf(pout + blen, msize - blen, + "%02x", *value++); + if (fp->bitmask) + blen += scnprintf(pout + blen, msize - blen, + "&%02x", fp->bitmask); + blen += scnprintf(pout + blen, msize - blen, "; "); + } + up(&patch->list_sema); + + if (patch->flags & FLAG_FORCE_UPDATE) + blen += scnprintf(pout + blen, msize - blen, "U; "); + if (patch->flags & FLAG_WAKEABLE) + blen += scnprintf(pout + blen, msize - blen, "W; "); + if (patch->flags & FLAG_POWER_SLEEP) + blen += scnprintf(pout + blen, msize - blen, "S; "); + blen += scnprintf(pout + blen, msize - blen, "\n"); + + return blen; +} + +static ssize_t synaptics_dsx_patch_query( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_dsx_patch *patch, + char *pout, ssize_t msize) +{ + struct synaptics_dsx_func_patch *fp; + ssize_t blen = 0; + + down(&patch->list_sema); + list_for_each_entry(fp, &patch->cfg_head, link) { + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + int i, error, function; + unsigned short data_addr; + unsigned int data_size; + unsigned char *value, rt_mod; + + regs = find_function(fp->func); + if (!regs) + continue; + reg = find_packet_reg(regs, fp->regstr); + if (!reg || reg->offset < 0) { + pr_err("F%x@%d not present\n", fp->func, fp->regstr); + continue; + } + + rt_mod = register_type_to_ascii(fp->func & 0xf00); + function = fp->func & 0xff; + + error = synaptics_rmi4_read_packet_reg(rmi4_data, + regs, reg->r_number); + if (error < 0) { + pr_err("F%x@%d register read failed\n", + function, fp->regstr); + continue; + } + /* calculate register address */ + data_addr = regs->base_addr + reg->offset; + /* dump whole register if subpacket is 255 */ + if (fp->subpkt == 0xff) { + value = reg->data; + data_size = reg->size; + blen += scnprintf(pout + blen, msize - blen, + "F%x%c@%d{%X}=", + function, rt_mod, fp->regstr, data_addr); + } else { + struct synaptics_rmi4_subpkt *subpkt; + + subpkt = reg->subpkt + fp->subpkt; + value = (unsigned char *)subpkt->data; + data_size = subpkt->size; + data_addr += subpkt->offset; + blen += scnprintf(pout + blen, msize - blen, + "F%x%c@%d:%d{%X}=", + function, rt_mod, fp->regstr, + fp->subpkt, data_addr); + } + + for (i = 0; i < data_size; i++) + blen += scnprintf(pout + blen, msize - blen, + "%02x", *value++); + blen += scnprintf(pout + blen, msize - blen, "; "); + } + up(&patch->list_sema); + blen += scnprintf(pout + blen, msize - blen, "\n"); + return blen; +} + +static ssize_t modifiers_show(struct synaptics_rmi4_data *rmi4_data, + char *buf, size_t count, bool show_data, bool show_query) +{ + struct config_modifier *cm; + ssize_t (*handler)(struct synaptics_rmi4_data *, + struct synaptics_dsx_patch *, + char *, ssize_t); + ssize_t added, blen = 0; + + if (!rmi4_data->modifiers.mods_num) { + pr_debug("patchset is empty!\n"); + return 0; + } + + down(&rmi4_data->modifiers.list_sema); + list_for_each_entry(cm, &rmi4_data->modifiers.mod_head, link) { + const char *name = cm->name ? cm->name : "na"; + bool is_query = cm->id == SYNA_MOD_QUERY; + + /* skip printing modifiers, but query */ + if (!is_query && show_query) + continue; + blen += scnprintf(buf + blen, count - blen, "%c[%s]\n", + cm->effective ? '+' : '-', name); + if (!show_data) + continue; + + if (is_query && show_query) + handler = &synaptics_dsx_patch_query; + else + handler = &synaptics_dsx_patch_dump; + + if (cm->active) { + blen += scnprintf(buf + blen, + count - blen, " [active]"); + added = (*handler)(rmi4_data, cm->active, + buf + blen, count - blen); + blen += added; + count -= added; + } + + if (cm->clipa) { + blen += scnprintf(buf + blen, count - blen, + " [clip]<%u,%u,%u,%u>%s\n", + cm->clipa->xul_clip, cm->clipa->yul_clip, + cm->clipa->xbr_clip, cm->clipa->ybr_clip, + cm->clipa->inversion ? "in" : "out"); + } + + if (cm->suspended) { + blen += scnprintf(buf + blen, + count - blen, " [suspended]"); + added = (*handler)(rmi4_data, cm->suspended, + buf + blen, count - blen); + blen += added; + count -= added; + } + } + up(&rmi4_data->modifiers.list_sema); + + return blen; +} + +#define NAME_ID 0 +#define ACTION_ID 1 + +static ssize_t synaptics_rmi4_mod_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_dsx_patch **patch_ptr, *patch; + struct config_modifier *cm; + bool clear_only = false; + int id, bytes_left, ntokens = 0; + char *internal, *ptr, *token[2] = {0}; + + internal = kstrdup(buf, GFP_KERNEL); + for (ptr = internal; ntokens < 2; ntokens++) { + bytes_left = count - (ptr - internal); + ptr = strnchr(ptr, bytes_left, '['); + if (!ptr) + break; + token[ntokens] = ++ptr; /* advance to the name */ + bytes_left = count - (ptr - internal); + ptr = strnchr(ptr, bytes_left, ']'); + if (ptr) + *ptr++ = 0; + dev_dbg(dev, "token[%d]='%s'\n", ntokens, token[ntokens]); + } + + if (*buf != '[' && ntokens != 2) { + dev_err(dev, "invalid modifier syntax\n"); + count = -EINVAL; + goto leave_now; + } + + id = modifier_name2id(token[NAME_ID]); + switch (id) { + case SYNA_MOD_QUERY: + dev_err(dev, "use query attribute instead\n"); + count = -EINVAL; + break; + case -1: + dev_err(dev, "[%s] is invalid modifier\n", token[NAME_ID]); + count = -EINVAL; + break; + } + + if (count == -EINVAL) + goto leave_now; + + /* pointer has been advanced in the loop above to point to patch */ + if (*ptr == '\n') + clear_only = true; + + cm = modifier_by_id(rmi4_data, id); + if (!cm) { + if (clear_only) + goto leave_now; + + dev_info(dev, "allocating modifier [%s]\n", token[NAME_ID]); + cm = kzalloc(sizeof(*cm), GFP_KERNEL); + if (!cm) { + count = -ENOMEM; + goto leave_now; + } + + down(&rmi4_data->modifiers.list_sema); + list_add_tail(&cm->link, &rmi4_data->modifiers.mod_head); + up(&rmi4_data->modifiers.list_sema); + rmi4_data->modifiers.mods_num++; + cm->name = ascii_names[id]; + cm->id = id; + } + + if (!strncmp(token[ACTION_ID], "clip", 4)) { + bool clipping_is_active = false; + struct synaptics_clip_area clipa; + + if (cm->clipa) { + if (rmi4_data->clipping_on && + rmi4_data->clipa == cm->clipa) { + /* if currently effective clipping */ + /* is the one being edited */ + rmi4_data->clipa = NULL; + clipping_is_active = true; + } + kfree(cm->clipa); + } + + if (clear_only) { + if (clipping_is_active) { + rmi4_data->clipping_on = false; + pr_debug("removed [clip] from modifier [%s]\n", + token[NAME_ID]); + } + goto leave_now; + } + + ntokens = sscanf(ptr, "<%u %u %u %u %u>", &clipa.xul_clip, + &clipa.yul_clip, &clipa.xbr_clip, + &clipa.ybr_clip, &clipa.inversion); + if (ntokens != 5) { + dev_err(dev, "Invalid clip area modifier\n"); + goto leave_now; + } + + if (!cm->clipa) { + cm->clipa = kzalloc(sizeof(clipa), GFP_KERNEL); + if (!cm->clipa) { + count = -ENOMEM; + goto leave_now; + } + } + + memcpy(cm->clipa, &clipa, sizeof(clipa)); + + if (clipping_is_active) + rmi4_data->clipa = cm->clipa; + + pr_debug("added [clip] area to modifier [%s]\n", + token[NAME_ID]); + goto leave_now; + } + + if (!strncmp(token[ACTION_ID], "active", 6)) + patch_ptr = &cm->active; + else + patch_ptr = &cm->suspended; + + if (*patch_ptr) { + synaptics_dsx_free_patch(*patch_ptr); + *patch_ptr = NULL; + } + + if (clear_only) { + pr_debug("removed [%s] from modifier [%s]\n", + token[ACTION_ID], token[NAME_ID]); + goto apply_and_leave; + } + + patch = synaptics_dsx_init_patch(token[ACTION_ID]); + if (!patch) { + count = -ENOMEM; + goto leave_now; + } + + synaptics_dsx_parse_string(rmi4_data, ptr, patch, true); + *patch_ptr = patch; + pr_debug("added [%s] patch to modifier [%s]\n", + token[ACTION_ID], token[NAME_ID]); +apply_and_leave: + if (!rmi4_data->in_bootloader) + synaptics_dsx_enforce_modifiers(rmi4_data, cm); +leave_now: + kfree(internal); + return count; +} + +static ssize_t synaptics_rmi4_mod_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return modifiers_show(rmi4_data, buf, PAGE_SIZE, true, false); +} + +static ssize_t synaptics_rmi4_mod_sw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int id; + bool set_op; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct config_modifier *cm; + char mod_name[32]; + + if (*buf != '+' && *buf != '-') { + dev_err(dev, "invalid modifier syntax\n"); + return -EINVAL; + } + + set_op = *buf == '-' ? false : true; + if (sscanf(buf + 1, "[%s]", mod_name) != 1) { + dev_err(dev, "unable to read modifier name\n"); + return -EINVAL; + } + + id = modifier_name2id(mod_name); + if (id == -1) { + dev_err(dev, "invalid modifier name '%s'\n", mod_name); + return -EINVAL; + } + + cm = modifier_by_id(rmi4_data, id); + if (!cm) { + dev_err(dev, "modifier with id=%d does not exist\n", id); + return -EINVAL; + } + + if (cm->effective != set_op) { + cm->effective = set_op; + pr_debug("set [%s] %s\n", cm->name, + cm->effective ? "on" : "off"); + + if (cm->effective) { + if (cm->clipa) { + rmi4_data->clipping_on = true; + rmi4_data->clipa = cm->clipa; + pr_debug("enable clipping in [%s]\n", cm->name); + } + } else if (cm->clipa && rmi4_data->clipa == cm->clipa) { + rmi4_data->clipping_on = false; + rmi4_data->clipa = NULL; + pr_debug("disable clipping in [%s]\n", cm->name); + } + + if (!rmi4_data->in_bootloader) + synaptics_dsx_enforce_modifiers(rmi4_data, cm); + } + return count; +} + +static ssize_t synaptics_rmi4_recovery_resume_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + int err = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + switch (value) { + case 0: + break; + case 1: + /* queue resume process */ + pr_err("[wj]Call touch resume process.\n"); + queue_work(system_wq, &rmi4_data->resume_work); + break; + default: + pr_err("Invalid value\n"); + return -EINVAL; + } + return count; +} + +static ssize_t synaptics_rmi4_mod_sw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return modifiers_show(rmi4_data, buf, PAGE_SIZE, false, false); +} + +/* NOTE: query is stored in active */ +static ssize_t synaptics_rmi4_query_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_dsx_patch *patch; + struct config_modifier *cm = modifier_by_id(rmi4_data, SYNA_MOD_QUERY); + + if (!cm) { + dev_info(dev, "allocating modifier [%s]\n", + modifier_id2name(SYNA_MOD_QUERY)); + cm = kzalloc(sizeof(*cm), GFP_KERNEL); + if (!cm) { + dev_err(dev, "Cannot allocate query modifier\n"); + return -ENOMEM; + } + + down(&rmi4_data->modifiers.list_sema); + list_add_tail(&cm->link, &rmi4_data->modifiers.mod_head); + up(&rmi4_data->modifiers.list_sema); + rmi4_data->modifiers.mods_num++; + cm->name = ascii_names[SYNA_MOD_QUERY]; + cm->id = SYNA_MOD_QUERY; + } + + if (*buf == '\n') { + if (cm->active) { + synaptics_dsx_free_patch(cm->active); + cm->active = NULL; + pr_debug("discarded current query\n"); + } + goto leave_now; + } + + if (*buf != 'F') { + dev_err(dev, "invalid query syntax\n"); + return -EINVAL; + } + + patch = synaptics_dsx_init_patch(ascii_names[SYNA_MOD_QUERY]); + if (!patch) + return -ENOMEM; + + synaptics_dsx_parse_string(rmi4_data, buf, patch, false); + cm->active = patch; + pr_debug("new query added\n"); + +leave_now: + return count; +} + +static ssize_t synaptics_rmi4_query_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct config_modifier *cm = modifier_by_id(rmi4_data, SYNA_MOD_QUERY); + + return !cm ? 0 : modifiers_show(rmi4_data, buf, PAGE_SIZE, true, true); +} + + /** + * synaptics_rmi4_set_page() + * + * Called by synaptics_rmi4_i2c_read() and synaptics_rmi4_i2c_write(). + * + * This function writes to the page select register to switch to the + * assigned page. + */ +static int synaptics_rmi4_set_page(struct synaptics_rmi4_data *rmi4_data, + unsigned int address) +{ + int retval = 0; + unsigned char retry; + unsigned char buf[PAGE_SELECT_LEN]; + unsigned char page; + struct i2c_client *i2c = rmi4_data->i2c_client; + + page = ((address >> 8) & MASK_8BIT); + if (page != rmi4_data->current_page) { + buf[0] = MASK_8BIT; + buf[1] = page; + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + retval = i2c_master_send(i2c, buf, PAGE_SELECT_LEN); + if (retval != PAGE_SELECT_LEN) { + dev_err(&i2c->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + } else { + rmi4_data->current_page = page; + break; + } + } + } else + return PAGE_SELECT_LEN; + return (retval == PAGE_SELECT_LEN) ? retval : -EIO; +} + + /** + * synaptics_rmi4_i2c_read() + * + * Called by various functions in this driver, and also exported to + * other expansion Function modules such as rmi_dev. + * + * This function reads data of an arbitrary length from the sensor, + * starting from an assigned register address of the sensor, via I2C + * with a retry mechanism. + */ +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry; + unsigned char buf; + struct i2c_msg msg[] = { + { + .addr = rmi4_data->i2c_client->addr, + .flags = 0, + .len = 1, + .buf = &buf, + }, + { + .addr = rmi4_data->i2c_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + }, + }; + + buf = addr & MASK_8BIT; + + mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + retval = synaptics_rmi4_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) + goto exit; + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(rmi4_data->i2c_client->adapter, msg, 2) == 2) { + retval = length; + break; + } + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + } + + if (retry == SYN_I2C_RETRY_TIMES) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C read %db@x%x over retry limit\n", + __func__, length, addr); + retval = -EIO; + } + +exit: + mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + return retval; +} + + /** + * synaptics_rmi4_i2c_write() + * + * Called by various functions in this driver, and also exported to + * other expansion Function modules such as rmi_dev. + * + * This function writes data of an arbitrary length to the sensor, + * starting from an assigned register address of the sensor, via I2C with + * a retry mechanism. + */ +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry; + unsigned char buf[length + 1]; + struct i2c_msg msg[] = { + { + .addr = rmi4_data->i2c_client->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + + mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + retval = synaptics_rmi4_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) + goto exit; + + buf[0] = addr & MASK_8BIT; + memcpy(&buf[1], &data[0], length); + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(rmi4_data->i2c_client->adapter, msg, 1) == 1) { + retval = length; + break; + } + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + } + + if (retry == SYN_I2C_RETRY_TIMES) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C write %db@x%x over retry limit\n", + __func__, length, addr); + retval = -EIO; + } + +exit: + mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + return retval; +} + +static int synaptics_rmi4_f12_wakeup_gesture( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + struct synaptics_rmi4_packet_reg *reg_data_4 = + &rmi4_data->f12_data_registers_ptr->regs[F12_D4_IDX]; + + if (reg_data_4->offset == -1) { + dev_err(&rmi4_data->i2c_client->dev, + "unable to clear wakeup gesture IRQ\n"); + return -EINVAL; + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.data_base + reg_data_4->offset, + &f12_d4_0.gesture, sizeof(f12_d4_0)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "failure clearing wakeup gesture IRQ rc=%d\n", retval); + return retval; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "wakeup gesture status=0x%02x\n", f12_d4_0.gesture); + + letter = synaptics_convert_gesture_flag(f12_d4_0.gesture); + + if (f12_d4_0.gesture & DOUBLE_TAP_GESTURE) { + /* emulate power key press */ + input_report_key(rmi4_data->input_dev, KEY_SLIDE, 1); + input_report_key(rmi4_data->input_dev, KEY_SLIDE, 0); + input_sync(rmi4_data->input_dev); + + dev_dbg(&rmi4_data->i2c_client->dev, + "DBL_TAP wakeup gesture detected\n"); + } + + return 0; +} + + /** + * synaptics_rmi4_f12_abs_report() + * + * Called by synaptics_rmi4_report_touch() when valid Function $12 + * finger data has been detected. + * + * This function reads the Function $12 data registers, determines the + * status of each finger supported by the Function, processes any + * necessary coordinate manipulation, reports the finger data to + * the input subsystem, and returns the number of fingers detected. + */ +static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char finger; + unsigned char fingers_to_process; + unsigned char finger_status; + unsigned short data_addr; + unsigned short data_size; + int x; + int y; + int p; + int w; + int id; + static unsigned char active_touch_max_idx; + struct timespec hw_time = ktime_to_timespec(ktime_get()); + struct f12_d1_type *finger_data; + struct synaptics_rmi4_packet_reg *reg_data_1 = + &rmi4_data->f12_data_registers_ptr->regs[F12_D1_IDX]; + struct synaptics_rmi4_packet_reg *reg_data_15 = + &rmi4_data->f12_data_registers_ptr->regs[F12_D15_IDX]; +#ifdef CONFIG_TOUCHSCREEN_TOUCHX_BASE + unsigned char number_of_fingers_actually_touching = 0; +#endif + + if (atomic_read(&rmi4_data->panel_off_flag)) { + synaptics_dsx_resumeinfo_ignore(rmi4_data); + return 0; + } else + synaptics_dsx_resumeinfo_purgeoff(rmi4_data); + + fingers_to_process = fhandler->num_of_data_points; + data_addr = fhandler->full_addr.data_base; + + if (reg_data_15->offset != -1) { + int temp; + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr + reg_data_15->offset, + f12_d15_0.attn, + sizeof(f12_d15_0)); + if (retval < 0) + return 0; + + /* Start checking from the highest bit */ + temp = reg_data_15->size - 1; /* Highest byte */ + finger = (fingers_to_process - 1) % 8; /* Highest bit */ + do { + if (f12_d15_0.attn[temp] & (1 << finger)) + break; + + if (finger) { + finger--; + } else { + temp--; /* Move to the next lower byte */ + finger = 7; + } + + fingers_to_process--; + } while (fingers_to_process); + + pr_debug("fingers to process %d\n", fingers_to_process); + } + + fingers_to_process = max(fingers_to_process, active_touch_max_idx); + + if (fingers_to_process == 0) { + /* count release event */ + events_cnt++; + synaptics_dsx_release_all(rmi4_data); + return 0; + } + + data_size = fingers_to_process * fhandler->size_of_data_register_block; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr + reg_data_1->offset, + reg_data_1->data, + data_size); + if (retval < 0) + return 0; + /* count valid event */ + events_cnt++; + if (reporting_stopped) + return 0; + + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_SEC, hw_time.tv_sec); + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_NSEC, hw_time.tv_nsec); + +#ifdef CONFIG_TOUCHSCREEN_TOUCHX_BASE + if (touchxp.touchx) + mutex_lock(&touchxp.virtual_touch_mutex); + + finger_data = (struct f12_d1_type *)reg_data_1->data; + for (finger = 0; finger < fingers_to_process; finger++, finger_data++) { + finger_status = finger_data->type_and_stylus; + if (finger_status == 0) + continue; + number_of_fingers_actually_touching++; + } +#endif + + finger_data = (struct f12_d1_type *)reg_data_1->data; + for (finger = 0; finger < fingers_to_process; finger++, finger_data++) { + finger_status = finger_data->type_and_stylus; + if (finger_status) { + x = finger_data->x_lsb | (finger_data->x_msb << 8); + y = finger_data->y_lsb | (finger_data->y_msb << 8); + p = w = finger_data->z; + id = finger; + + if (rmi4_data->board->x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->board->y_flip) + y = rmi4_data->sensor_max_y - y; + + if (rmi4_data->clipping_on && rmi4_data->clipa) { + bool inside; + + inside = (x >= rmi4_data->clipa->xul_clip) && + (x <= rmi4_data->clipa->xbr_clip) && + (y >= rmi4_data->clipa->yul_clip) && + (y <= rmi4_data->clipa->ybr_clip); + + if (inside == rmi4_data->clipa->inversion) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%d,%d belong clipping area\n", + x, y); + continue; + } + } +#ifdef TYPE_B_PROTOCOL + input_mt_slot(rmi4_data->input_dev, finger); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, finger_status); +#endif + +#ifdef CONFIG_TOUCHSCREEN_TOUCHX_BASE + touchxp.touch_magic_dev = rmi4_data->input_dev; + if (touchxp.touchx) + touchxp.touchx(&x, &y, finger, + number_of_fingers_actually_touching); +#endif + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Finger %d:\n" + "x = %d\n" + "y = %d\n" + "p = %d\n" + "w = %d\n", + __func__, finger, + x, y, p, w); + + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); + input_report_abs(rmi4_data->input_dev, + ABS_MT_PRESSURE, p); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, w); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + touch_count++; + active_touch_max_idx = finger + 1; +#ifdef TYPE_B_PROTOCOL + } else { + input_mt_slot(rmi4_data->input_dev, finger); + /* Touch no longer active, close out slot */ + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, 0); +#endif + } + + synaptics_dsx_resumeinfo_touch(rmi4_data); + } + + if (!touch_count) { +#ifdef CONFIG_TOUCHSCREEN_TOUCHX_BASE + touchxp.finger_down = 0; +#endif + active_touch_max_idx = 0; +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + } + + input_mt_report_pointer_emulation(rmi4_data->input_dev, false); + input_sync(rmi4_data->input_dev); + +#ifdef CONFIG_TOUCHSCREEN_TOUCHX_BASE + if (touchxp.touchx) + mutex_unlock(&touchxp.virtual_touch_mutex); +#endif + + return touch_count; +} + + /** + * synaptics_rmi4_f11_abs_report() + * + * Called by synaptics_rmi4_report_touch() when valid Function $11 + * finger data has been detected. + * + * This function reads the Function $11 data registers, determines the + * status of each finger supported by the Function, processes any + * necessary coordinate manipulation, reports the finger data to + * the input subsystem, and returns the number of fingers detected. + */ +static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char reg_index; + unsigned char finger; + unsigned char fingers_supported; + unsigned char num_of_finger_status_regs; + unsigned char finger_shift; + unsigned char finger_status; + unsigned char data_reg_blk_size; + unsigned char finger_status_reg[3]; + unsigned char data[F11_STD_DATA_LEN]; + unsigned short data_addr; + unsigned short data_offset; + int x; + int y; + int wx; + int wy; + int z; + struct timespec hw_time = ktime_to_timespec(ktime_get()); +#ifdef CONFIG_TOUCHSCREEN_TOUCHX_BASE + unsigned char number_of_fingers_actually_touching = 0; +#endif + + /* + * The number of finger status registers is determined by the + * maximum number of fingers supported - 2 bits per finger. So + * the number of finger status registers to read is: + * register_count = ceil(max_num_of_fingers / 4) + */ + fingers_supported = fhandler->num_of_data_points; + num_of_finger_status_regs = (fingers_supported + 3) / 4; + data_addr = fhandler->full_addr.data_base; + data_reg_blk_size = fhandler->size_of_data_register_block; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + finger_status_reg, + num_of_finger_status_regs); + if (retval < 0) + return 0; + /* count valid event */ + events_cnt++; + if (reporting_stopped) + return 0; + + if (atomic_read(&rmi4_data->panel_off_flag)) { + synaptics_dsx_resumeinfo_ignore(rmi4_data); + return 0; + } else + synaptics_dsx_resumeinfo_purgeoff(rmi4_data); + + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_SEC, hw_time.tv_sec); + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_NSEC, hw_time.tv_nsec); + +#ifdef CONFIG_TOUCHSCREEN_TOUCHX_BASE + if (touchxp.touchx) + mutex_lock(&touchxp.virtual_touch_mutex); + + for (finger = 0; finger < fingers_supported; finger++) { + reg_index = finger / 4; + finger_shift = (finger % 4) * 2; + finger_status = (finger_status_reg[reg_index] >> finger_shift) + & MASK_2BIT; + if (finger_status) + number_of_fingers_actually_touching++; + } +#endif + for (finger = 0; finger < fingers_supported; finger++) { + reg_index = finger / 4; + finger_shift = (finger % 4) * 2; + finger_status = (finger_status_reg[reg_index] >> finger_shift) + & MASK_2BIT; + /* + * Each 2-bit finger status field represents the following: + * 00 = finger not present + * 01 = finger present and data accurate + * 10 = finger present but data may be inaccurate + * 11 = reserved + */ + if (finger_status) { + data_offset = data_addr + + num_of_finger_status_regs + + (finger * data_reg_blk_size); + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_offset, + data, + data_reg_blk_size); + if (retval < 0) + return 0; + + x = (data[0] << 4) | (data[2] & MASK_4BIT); + y = (data[1] << 4) | ((data[2] >> 4) & MASK_4BIT); + wx = (data[3] & MASK_4BIT); + wy = (data[3] >> 4) & MASK_4BIT; + z = data[4]; + + if (rmi4_data->board->x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->board->y_flip) + y = rmi4_data->sensor_max_y - y; + + if (rmi4_data->clipping_on && rmi4_data->clipa) { + bool inside; + + inside = (x >= rmi4_data->clipa->xul_clip) && + (x <= rmi4_data->clipa->xbr_clip) && + (y >= rmi4_data->clipa->yul_clip) && + (y <= rmi4_data->clipa->ybr_clip); + + if (!inside) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%d,%d ouside clipping area\n", + x, y); + continue; + } + } +#ifdef TYPE_B_PROTOCOL + input_mt_slot(rmi4_data->input_dev, finger); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, finger_status); +#endif + +#ifdef CONFIG_TOUCHSCREEN_TOUCHX_BASE + touchxp.touch_magic_dev = rmi4_data->input_dev; + if (touchxp.touchx) + touchxp.touchx(&x, &y, finger, + number_of_fingers_actually_touching); +#endif + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Finger %d:\n" + "x = %d\n" + "y = %d\n" + "wx = %d\n" + "wy = %d\n" + "z = %d\n", + __func__, finger, + x, y, wx, wy, z); + + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); + input_report_abs(rmi4_data->input_dev, + ABS_MT_PRESSURE, z); + +#ifdef REPORT_2D_W + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, max(wx, wy)); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, min(wx, wy)); +#endif +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + touch_count++; +#ifdef TYPE_B_PROTOCOL + } else { + input_mt_slot(rmi4_data->input_dev, finger); + /* Touch no longer active, close out slot */ + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, 0); +#endif + } + + synaptics_dsx_resumeinfo_touch(rmi4_data); + } + +#ifdef CONFIG_TOUCHSCREEN_TOUCHX_BASE + if (!touch_count) + touchxp.finger_down = 0; +#endif + +#ifndef TYPE_B_PROTOCOL + if (!touch_count) + input_mt_sync(rmi4_data->input_dev); +#endif + + input_mt_report_pointer_emulation(rmi4_data->input_dev, false); + input_sync(rmi4_data->input_dev); + +#ifdef CONFIG_TOUCHSCREEN_TOUCHX_BASE + if (touchxp.touchx) + mutex_unlock(&touchxp.virtual_touch_mutex); +#endif + + return touch_count; +} + +static void synaptics_rmi4_f1a_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char button; + unsigned char index; + unsigned char shift; + unsigned char status; + unsigned char *data; + unsigned short data_addr = fhandler->full_addr.data_base; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + static bool current_status[MAX_NUMBER_OF_BUTTONS]; +#ifdef NO_0D_WHILE_2D + static bool before_2d_status[MAX_NUMBER_OF_BUTTONS]; + static bool while_2d_status[MAX_NUMBER_OF_BUTTONS]; +#endif + + if (tsb_buff_clean_flag) { + memset(current_status, 0, sizeof(current_status)); +#ifdef NO_0D_WHILE_2D + memset(before_2d_status, 0, sizeof(before_2d_status)); + memset(while_2d_status, 0, sizeof(while_2d_status)); +#endif + tsb_buff_clean_flag = 0; + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + f1a->button_data_buffer, + f1a->button_bitmask_size); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read button data registers\n", + __func__); + return; + } + + if (atomic_read(&rmi4_data->panel_off_flag)) { + synaptics_dsx_resumeinfo_ignore(rmi4_data); + return; + } else + synaptics_dsx_resumeinfo_purgeoff(rmi4_data); + + data = f1a->button_data_buffer; + + for (button = 0; button < f1a->valid_button_count; button++) { + index = button / 8; + shift = button % 8; + status = ((data[index] >> shift) & MASK_1BIT); + + if (current_status[button] == status) + continue; + else + current_status[button] = status; + + ud_set_id(&button_ud_stats, button); + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Button %d (code %d) ->%d\n", + __func__, button, + f1a->button_map[button], + status); +#ifdef NO_0D_WHILE_2D + if (rmi4_data->fingers_on_2d == false) { + if (status == 1) { + before_2d_status[button] = 1; + } else { + if (while_2d_status[button] == 1) { + while_2d_status[button] = 0; + continue; + } else { + before_2d_status[button] = 0; + } + } + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + ud_log_status(&button_ud_stats, status == 1); + } else { + if (before_2d_status[button] == 1) { + before_2d_status[button] = 0; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + ud_log_status(&button_ud_stats, status == 1); + } else { + if (status == 1) + while_2d_status[button] = 1; + else + while_2d_status[button] = 0; + } + } +#else + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + ud_log_status(&button_ud_stats, status == 1); +#endif + synaptics_dsx_resumeinfo_touch(rmi4_data); + } + + input_sync(rmi4_data->input_dev); + + return; +} + +static void synaptics_rmi4_f01_handler(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned short data_addr = fhandler->full_addr.data_base; + struct synaptics_rmi4_f01_device_status status; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return; + + switch (status.status_code) { + case 0x00: + printk(KERN_INFO "%s: No error.\n", __func__); + break; + + case 0x01: + printk(KERN_INFO "%s: Touch IC reset complete.\n", __func__); + break; + + case 0x02: + printk(KERN_ERR "%s: Touch IC configuration error--%s.\n", + __func__, "check platform settings"); + break; + + case 0x03: + printk(KERN_ERR "%s: Touch IC device failure.\n", __func__); + break; + + case 0x04: + printk(KERN_ERR "%s: Configuration CRC failure.\n", __func__); + break; + + case 0x05: + printk(KERN_ERR "%s: Firmware CRC failure.\n", __func__); + break; + + case 0x06: + printk(KERN_ERR "%s: CRC in progress.\n", __func__); + break; + + default: + printk(KERN_ERR "%s: Unknown error 0x%02X received.\n", + __func__, status.status_code); + break; + } +} + +#define IM_BIT 0 +#define NMS_BIT 1 +#define CID_IM_BIT 2 +#define FS_IM_BIT 3 +#define FREQ_BIT 4 +#define GUARD_BIT 5 + +static void synaptics_rmi4_f51_handler(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + unsigned char presence_mask = 0; + ktime_t log = ktime_get(); + int ii, error; + + regs = find_function(SYNAPTICS_RMI4_F51 | DATA_TYPE); + if (!regs) + return; + + reg = ®s->regs[0]; + error = synaptics_rmi4_read_packet_reg(rmi4_data, regs, reg->r_number); + if (error < 0) + pr_err("F%x@D%d register read failed\n", + regs->f_number & 0xff, reg->r_number); + else { + presence_mask |= (1 << GUARD_BIT); + pr_debug("F%x@D%d: int status [0]=0x%x, [1]=0x%x\n", + regs->f_number & 0xff, reg->r_number, + f51_d0_0.data[0], f51_d0_0.data[1]); + tk_debug("F%x@D%d: int status [%s][%s][%s] md = %d\n", + regs->f_number & 0xff, reg->r_number, + f51_d0_0.noise_state ? "N" : "-", + f51_d0_0.gear_change ? "H" : "-", + f51_d0_0.guard_state ? "M" : "-", + f51_d0_0.md_present); + } + + regs = find_function(SYNAPTICS_RMI4_F54 | DATA_TYPE); + if (!regs) + return; + for (ii = 0; ii < regs->nr_regs; ii++) { + reg = ®s->regs[ii]; + if (!reg || reg->offset < 0) { + pr_err("F%x@D%d not present\n", + regs->f_number & 0xff, reg->r_number); + continue; + } + error = synaptics_rmi4_read_packet_reg(rmi4_data, regs, + reg->r_number); + if (error < 0) { + pr_err("F%x@D%d register read failed\n", + regs->f_number & 0xff, reg->r_number); + continue; + } + presence_mask |= (1 << ii); + } + + if (presence_mask & (1 << FREQ_BIT)) { + statistics_log_time(gStat.dur, (int)f54_d17_0.freq, log); + statistics_log_time(gStat.hop, (int)f54_d17_0.freq, log); + } + + if (presence_mask & (1 << NMS_BIT)) + statistics_log_time(gStat.nms, (int)f54_d10_0.noise_state, log); + + if (presence_mask & (1 << GUARD_BIT)) + statistics_log_time(gStat.mpd, (int)f51_d0_0.md_present, log); + + tk_debug("F%x: %d:%d %5d:%5d:%5d\n", + regs->f_number & 0xff, + presence_mask & (1 << FREQ_BIT) ? + (int)f54_d17_0.freq : -1, + presence_mask & (1 << NMS_BIT) ? + (int)f54_d10_0.noise_state : -1, + presence_mask & (1 << CID_IM_BIT) ? + (int)f54_d14_0.cid_im_lsb | + (f54_d14_0.cid_im_msb << 4) : -1, + presence_mask & (1 << IM_BIT) ? + (int)f54_d6_0.interference_metric_lsb | + (f54_d6_0.interference_metric_msb << 4) : -1, + presence_mask & (1 << FS_IM_BIT) ? + (int)f54_d16_0.freq_scan_im_lsb | + (f54_d16_0.freq_scan_im_msb << 4) : -1); +} + + /** + * synaptics_rmi4_report_touch() + * + * Called by synaptics_rmi4_sensor_report(). + * + * This function calls the appropriate finger data reporting function + * based on the function handler it receives and returns the number of + * fingers detected. + */ +static void synaptics_rmi4_report_touch(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + unsigned char *touch_count) +{ + unsigned char touch_count_2d; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Function %02x reporting\n", + __func__, fhandler->fn_number); + + switch (fhandler->fn_number) { + case SYNAPTICS_RMI4_F01: + synaptics_rmi4_f01_handler(rmi4_data, fhandler); + break; + + case SYNAPTICS_RMI4_F51: + synaptics_rmi4_f51_handler(rmi4_data, fhandler); + break; + + case SYNAPTICS_RMI4_F11: + synaptics_dsx_resumeinfo_isr(rmi4_data); + + touch_count_2d = synaptics_rmi4_f11_abs_report(rmi4_data, + fhandler); + + *touch_count += touch_count_2d; + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + + case SYNAPTICS_RMI4_F12: + if (rmi4_data->suspend_is_wakeable) { + synaptics_rmi4_f12_wakeup_gesture(rmi4_data, fhandler); + break; + } + + synaptics_dsx_resumeinfo_isr(rmi4_data); + + touch_count_2d = synaptics_rmi4_f12_abs_report(rmi4_data, + fhandler); + + + *touch_count += touch_count_2d; + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + + case SYNAPTICS_RMI4_F1A: + synaptics_dsx_resumeinfo_isr(rmi4_data); + + synaptics_rmi4_f1a_report(rmi4_data, fhandler); + break; + + default: + break; + } + + return; +} + + /** + * synaptics_rmi4_sensor_report() + * + * Called by synaptics_rmi4_irq(). + * + * This function determines the interrupt source(s) from the sensor + * and calls synaptics_rmi4_report_touch() with the appropriate + * function handler for each function with valid data inputs. + */ +static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char touch_count = 0; + unsigned char intr[MAX_INTR_REGISTERS]; + unsigned int intr_status_mask; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_exp_fn *exp_fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + /* + * Get interrupt status information from F01 Data1 register to + * determine the source(s) that are flagging the interrupt. + */ + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + intr, + rmi4_data->num_of_intr_regs); + if (retval < 0) + return retval; + + /* + * Traverse the function handler list and service the source(s) + * of the interrupt accordingly. + */ + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + if (fhandler->intr_mask & + intr[fhandler->intr_reg_num]) { + synaptics_rmi4_report_touch(rmi4_data, + fhandler, &touch_count); + } + } + } + + batohui(&intr_status_mask, intr, rmi4_data->num_of_intr_regs); + /* + * Go through external handlers list only when interrupt + * is not handled by currently active internal functions. + */ + if (intr_status_mask & (~rmi4_data->active_fn_intr_mask)) { + mutex_lock(&exp_fn_ctrl.list_mutex); + if (!list_empty(&exp_fn_ctrl.fn_list)) { + list_for_each_entry(exp_fhandler, + &exp_fn_ctrl.fn_list, link) { + if (exp_fhandler->inserted && + (exp_fhandler->func_attn != NULL)) + exp_fhandler->func_attn( + rmi4_data, intr[0]); + } + } + mutex_unlock(&exp_fn_ctrl.list_mutex); + } + + return touch_count; +} + + /** + * synaptics_rmi4_irq() + * + * Called by the kernel when an interrupt occurs (when the sensor + * asserts the attention irq). + * + * This function is the ISR thread and handles the acquisition + * and the reporting of finger data when the presence of fingers + * is detected. + */ +static irqreturn_t synaptics_rmi4_irq(int irq, void *data) +{ + struct synaptics_rmi4_data *rmi4_data = data; + struct synaptics_rmi4_irq_info *tmp_q; + + if (rmi4_data->number_irq > 0) { + rmi4_data->last_irq++; + if (rmi4_data->last_irq >= rmi4_data->number_irq) + rmi4_data->last_irq = 0; + tmp_q = + &(rmi4_data->irq_info[rmi4_data->last_irq]); + getnstimeofday(&(tmp_q->irq_time)); + } + + synaptics_rmi4_sensor_report(rmi4_data); + + return IRQ_HANDLED; +} + + /** + * synaptics_rmi4_irq_enable() + * + * Called by synaptics_rmi4_probe() and the power management functions + * in this driver and also exported to other expansion Function modules + * such as rmi_dev. + * + * This function handles the enabling and disabling of the attention + * irq including the setting up of the ISR thread. + */ +static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval = 0; + unsigned char intr_status; + const struct synaptics_dsx_platform_data *platform_data = + rmi4_data->board; + + if (enable) { + if (rmi4_data->irq_enabled) + return retval; + + /* Clear interrupts first */ + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + &intr_status, + rmi4_data->num_of_intr_regs); + if (retval < 0) + return retval; + + retval = request_threaded_irq(rmi4_data->irq, NULL, + synaptics_rmi4_irq, platform_data->irq_flags, + DRIVER_NAME, rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create irq thread\n", + __func__); + return retval; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Started irq thread\n", __func__); + + rmi4_data->irq_enabled = true; + } else { + if (rmi4_data->irq_enabled) { + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, rmi4_data); + rmi4_data->irq_enabled = false; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Stopped irq thread\n", __func__); + } + } + + return retval; +} + + /** + * synaptics_rmi4_f11_init() + * + * Called by synaptics_rmi4_query_device(). + * + * This funtion parses information from the Function 11 registers + * and determines the number of fingers supported, x and y data ranges, + * offset to the associated interrupt status register, interrupt bit + * mask, and gathers finger data acquisition capabilities from the query + * registers. + */ +static int synaptics_rmi4_f11_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned char intr_offset; + unsigned char abs_data_size; + unsigned char abs_data_blk_size; + unsigned char query[F11_STD_QUERY_LEN]; + unsigned char control[F11_STD_CTRL_LEN]; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + query, + sizeof(query)); + if (retval < 0) + return retval; + + /* Maximum number of fingers supported */ + if ((query[1] & MASK_3BIT) <= 4) + fhandler->num_of_data_points = (query[1] & MASK_3BIT) + 1; + else if ((query[1] & MASK_3BIT) == 5) + fhandler->num_of_data_points = 10; + + rmi4_data->num_of_fingers = fhandler->num_of_data_points; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.ctrl_base, + control, + sizeof(control)); + if (retval < 0) + return retval; + + /* Maximum x and y */ + rmi4_data->sensor_max_x = ((control[6] & MASK_8BIT) << 0) | + ((control[7] & MASK_4BIT) << 8); + rmi4_data->sensor_max_y = ((control[8] & MASK_8BIT) << 0) | + ((control[9] & MASK_4BIT) << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Function %02x max x = %d max y = %d\n", + __func__, fhandler->fn_number, + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + + /* Reporting mode */ + if (!(control[0] & MASK_3BIT)) { + pr_warn("Reporting mode: continuous\n"); + } else { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: thresholds: x=0x%x, y=0x%x\n", + __func__, control[2], control[3]); + } + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + abs_data_size = query[5] & MASK_2BIT; + abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0)); + fhandler->size_of_data_register_block = abs_data_blk_size; + +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit); +#endif + set_bit(EV_ABS, rmi4_data->input_dev->evbit); + + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_X, 0, + rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_Y, 0, + rmi4_data->sensor_max_y, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_PRESSURE, 0, + 255, 0, 0); +#ifdef REPORT_2D_W + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, 0, + 15, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, 0, + 15, 0, 0); +#endif +#ifdef TYPE_B_PROTOCOL + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers, 0); +#else + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TRACKING_ID, 0, + rmi4_data->num_of_fingers - 1, 0, 0); + input_set_events_per_packet(rmi4_data->input_dev, 64); +#endif + return retval; +} + +static int synaptics_rmi4_f1a_alloc_mem(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + struct synaptics_rmi4_f1a_handle *f1a; + + f1a = kzalloc(sizeof(*f1a), GFP_KERNEL); + if (!f1a) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for function handle\n", + __func__); + return -ENOMEM; + } + + fhandler->data = (void *)f1a; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + f1a->button_query.data, + sizeof(f1a->button_query.data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read query registers\n", + __func__); + return retval; + } + + f1a->button_count = f1a->button_query.max_button_count + 1; + f1a->button_bitmask_size = (f1a->button_count + 7) / 8; + + f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size, + sizeof(*(f1a->button_data_buffer)), GFP_KERNEL); + if (!f1a->button_data_buffer) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for data buffer\n", + __func__); + return -ENOMEM; + } + + f1a->button_map = kcalloc(f1a->button_count, + sizeof(*(f1a->button_map)), GFP_KERNEL); + if (!f1a->button_map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for button map\n", + __func__); + return -ENOMEM; + } + + return 0; +} + +static int synaptics_rmi4_f51_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned char intr_offset; + struct synaptics_rmi4_func_packet_regs *regs; + unsigned char *data; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_subpkt *subpkt; + struct { + unsigned char num_of_query_regs; + unsigned char num_of_data_regs; + unsigned char num_of_ctrl_regs; + } f51_query = {0}; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + (unsigned char *)&f51_query, + sizeof(f51_query)); + pr_debug("F%x: # of query=%d, data=%d, ctrl=%d\n", + fd->fn_number, + f51_query.num_of_query_regs, + f51_query.num_of_data_regs, + f51_query.num_of_ctrl_regs); + /* check for error and mandatory registers presense */ + if (retval < 0 || f51_query.num_of_query_regs < 3) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Error querying F51\n", + __func__); + return -EIO; + } + /* check for mandatory registers presense */ + if (f51_query.num_of_data_regs < 2 || + f51_query.num_of_ctrl_regs < 10) { + return -ENODEV; + } + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + /* FIXME: interrupt bit 5 (belongs F54) is used for some reason */ + fhandler->intr_mask = 0x40; + + regs = find_function(SYNAPTICS_RMI4_F51 | DATA_TYPE); + if (!regs) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: F%x data not found\n", + __func__, fd->fn_number); + return 0; + } + + regs->base_addr = fhandler->full_addr.data_base; + reg = ®s->regs[0]; + subpkt = ®->subpkt[0]; + pr_debug("F%x@D%d size %u\n", + fd->fn_number, reg->r_number, subpkt->size); + data = kzalloc(subpkt->size, GFP_KERNEL); + if (data) { + reg->offset = reg->r_number; + reg->size = subpkt->size; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + } + + regs = find_function(SYNAPTICS_RMI4_F51); + if (!regs) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: F%x ctrl not found\n", + __func__, fd->fn_number); + return 0; + } + + regs->base_addr = fhandler->full_addr.ctrl_base; + for (ii = 0; ii < regs->nr_regs; ii++) { + reg = ®s->regs[ii]; + subpkt = ®->subpkt[0]; + pr_debug("F%x@C%d size %u\n", + fd->fn_number, reg->r_number, subpkt->size); + data = kzalloc(subpkt->size, GFP_KERNEL); + if (data) { + reg->offset = reg->r_number; + reg->size = subpkt->size; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + } + } + + retval = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (retval < 0) + dev_err(&rmi4_data->i2c_client->dev, "Error reading F51\n"); + else { + dev_info(&rmi4_data->i2c_client->dev, + "F%x ctrl[0]=%02x\n", fd->fn_number, f51_c0_0.data[0]); + dev_info(&rmi4_data->i2c_client->dev, + "F%x ctrl[4]=%02x\n", fd->fn_number, f51_c4_0.data[0]); + } + return 0; +} + + /** + * synaptics_rmi4_f12_init() + * + * Called by synaptics_rmi4_query_device(). + * + * This funtion parses information from the Function 12 registers + * and determines the number of fingers supported, x and y data ranges, + * offset to the associated interrupt status register, interrupt bit + * mask, and gathers finger data acquisition capabilities from the query + * registers. + */ +static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned char intr_offset; + struct synaptics_rmi4_func_packet_regs *regs; + + regs = rmi4_data->f12_data_registers_ptr; + retval = synaptics_rmi4_scan_f12_reg_info(rmi4_data, + fhandler->full_addr.query_base + regs->query_offset, + fhandler->full_addr.data_base, regs); + if (retval < 0) + return retval; + + if (f12_d15[0].present) + pr_debug("F12 has data register 15\n"); + + regs = find_function(SYNAPTICS_RMI4_F12); + retval = synaptics_rmi4_scan_f12_reg_info(rmi4_data, + fhandler->full_addr.query_base + regs->query_offset, + fhandler->full_addr.ctrl_base, regs); + if (retval < 0) + return retval; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + + retval = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (retval < 0) + return retval; + + /* Maximum number of fingers supported */ + if (f12_c23[1].present) { + fhandler->num_of_data_points = + f12_c23_1.max_num_reported_objects; + rmi4_data->num_of_fingers = f12_c23_1.max_num_reported_objects; + } else + return -ENOENT; + + if (f12_c08[0].present) { + rmi4_data->sensor_max_x = + (f12_c08_0.max_x_msb << 8) | f12_c08_0.max_x_lsb; + rmi4_data->sensor_max_y = + (f12_c08_0.max_y_msb << 8) | f12_c08_0.max_y_lsb; + } else + return -ENOENT; + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + /* Data size per touch */ + if (f12_c28[0].present) { + for (ii = 0; ii < 8; ii++) + if (f12_c28_0.reported_bytes_per_object & (1 << ii)) + fhandler->size_of_data_register_block++; + } else + return -ENOENT; + +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit); +#endif + set_bit(EV_ABS, rmi4_data->input_dev->evbit); + + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_X, 0, + rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_Y, 0, + rmi4_data->sensor_max_y, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_PRESSURE, 0, + 255, 0, 0); +#ifdef REPORT_2D_W + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, 0, + 255, 0, 0); +#endif +#ifdef TYPE_B_PROTOCOL + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers, 0); +#else + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TRACKING_ID, 0, + rmi4_data->num_of_fingers - 1, 0, 0); + input_set_events_per_packet(rmi4_data->input_dev, 64); +#endif + return retval; +} + +static int synaptics_rmi4_cap_button_map( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + unsigned char ii; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + const struct synaptics_dsx_platform_data *pdata = rmi4_data->board; + + if (!pdata->cap_button_map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: cap_button_map is \ + NULL in board file\n", + __func__); + return -ENODEV; + } else if (!pdata->cap_button_map->map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Button map is missing in board file\n", + __func__); + return -ENODEV; + } else { + if (pdata->cap_button_map->nbuttons != + f1a->button_count) { + f1a->valid_button_count = min(f1a->button_count, + pdata->cap_button_map->nbuttons); + } else { + f1a->valid_button_count = f1a->button_count; + } + + set_bit(EV_KEY, rmi4_data->input_dev->evbit); + + for (ii = 0; ii < f1a->valid_button_count; ii++) { + f1a->button_map[ii] = + pdata->cap_button_map->map[ii]; + set_bit(f1a->button_map[ii], + rmi4_data->input_dev->keybit); + input_set_capability(rmi4_data->input_dev, + EV_KEY, f1a->button_map[ii]); + } + } + + return 0; +} + +static void synaptics_rmi4_f1a_kfree(struct synaptics_rmi4_fn *fhandler) +{ + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + + if (f1a) { + kfree(f1a->button_data_buffer); + kfree(f1a->button_map); + kfree(f1a); + fhandler->data = NULL; + } + + return; +} + +static int synaptics_rmi4_f1a_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned short intr_offset; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + retval = synaptics_rmi4_f1a_alloc_mem(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + retval = synaptics_rmi4_cap_button_map(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + rmi4_data->button_0d_enabled = 1; + + return 0; + +error_exit: + synaptics_rmi4_f1a_kfree(fhandler); + + return retval; +} + +static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler, + struct synaptics_rmi4_fn_desc *rmi_fd, int page_number) +{ + *fhandler = kzalloc(sizeof(**fhandler), GFP_KERNEL); + if (!(*fhandler)) + return -ENOMEM; + + (*fhandler)->full_addr.data_base = + (rmi_fd->data_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.ctrl_base = + (rmi_fd->ctrl_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.cmd_base = + (rmi_fd->cmd_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.query_base = + (rmi_fd->query_base_addr | + (page_number << 8)); + + return 0; +} + +static unsigned char *simple_init_packet_reg( + struct synaptics_rmi4_packet_reg *reg, short r_offset) +{ + unsigned char *data_ptr = NULL; + if (reg->subpkt[0].data && reg->subpkt[0].size) { + reg->subpkt[0].present = true; + reg->subpkt[0].offset = 0; + memset(reg->subpkt[0].data, 0, reg->subpkt[0].size); + + reg->offset = r_offset; + kfree(reg->data); + data_ptr = kzalloc(reg->size, GFP_KERNEL); + } + return data_ptr; +} + +static void simple_deinit_packet_reg(struct synaptics_rmi4_packet_reg *reg) +{ + reg->expected = false; + reg->offset = -1; + reg->size = 0; + reg->subpkt[0].expected = false; + reg->subpkt[0].offset = -1; +} + +#define F01_RMI_Ctrl0 0 +#define F01_RMI_Ctrl2 2 +#define F01_RMI_Ctrl3 3 +#define F01_RMI_Ctrl5 5 +#define F01_RMI_Ctrl9 9 + +static void synaptics_rmi4_scan_f01_reg_info( + struct synaptics_rmi4_data *rmi4_data) +{ + int r, s, retval; + unsigned short query42_offset = F01_QUERY21_OFFSET; + unsigned short query43_size = 0; + unsigned short current_ctrl_offset = rmi4_data->num_of_intr_regs; + bool has_recalibration = false; + bool can_access_ctrl9 = false; + u8 query43[4]; + struct synaptics_rmi4_f01_query42 query42; + struct synaptics_rmi4_f01_query1 query1; + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + + regs = find_function(SYNAPTICS_RMI4_F01); + if (unlikely(regs == NULL)) { + dev_info(&rmi4_data->i2c_client->dev, + "%s: F01 registers not defined\n", __func__); + return; + } + /* invalidate offsets only and keep statically defined register size */ + for (r = 0; r < regs->nr_regs; ++r) { + regs->regs[r].offset = -1; + kfree(regs->regs[r].data); + regs->regs[r].data = NULL; + for (s = 0; s < regs->regs[r].nr_subpkts; ++s) { + regs->regs[r].subpkt[s].present = 0; + if (regs->regs[r].subpkt[s].data && + regs->regs[r].subpkt[s].size) + memset(regs->regs[r].subpkt[s].data, 0, + regs->regs[r].subpkt[s].size); + } + } + + regs->base_addr = rmi4_data->f01_ctrl_base_addr; + pr_debug("F01_RMI_Ctrl base addr 0x%x\n", regs->base_addr); + + reg = find_packet_reg(regs, F01_RMI_Ctrl0); + if (unlikely(reg == NULL)) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: F01_RMI_Ctrl0 not defined\n", + __func__); + return; + } + + if (regs && reg && reg->nr_subpkts) { + reg->data = simple_init_packet_reg(reg, 0); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + F01_QUERY1_OFFSET, + query1.data, + sizeof(query1.data)); + if (retval < 0) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: error reading F01_RMI_Query1\n", __func__); + return; + } + + if (query1.has_sensor_id) { + query42_offset++; /* has F01_RMI_Query22 */ + pr_debug("has F01_RMI_Query22\n"); + } + + if (query1.has_adjustable_doze) { + pr_debug("has F01_RMI_Ctrl2 and F01_RMI_Ctrl3\n"); + current_ctrl_offset++; + reg = find_packet_reg(regs, F01_RMI_Ctrl2); + if (reg) { + reg->data = simple_init_packet_reg(reg, + current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + current_ctrl_offset++; + reg = find_packet_reg(regs, F01_RMI_Ctrl3); + if (reg) { + reg->data = simple_init_packet_reg(reg, + current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + } + + if (query1.reserved) { + current_ctrl_offset++; /* has F01_RMI_Ctrl4 */ + query42_offset++; /* has F01_RMI_Query21 */ + query42_offset += 18; /* has F01_RMI_Query23-41 */ + pr_debug("has F01_RMI_Query21,23-41 and F01_RMI_Ctrl4\n"); + } + + if (query1.has_doze_holdoff) { + pr_debug("has F01_RMI_Ctrl5\n"); + current_ctrl_offset++; + reg = find_packet_reg(regs, F01_RMI_Ctrl5); + if (reg) { + reg->data = simple_init_packet_reg(reg, + current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + } + + query42.data[0] = 0; + if (query1.has_query42) { + pr_debug("F01_RMI_Query42 offset %d\n", query42_offset); + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + + query42_offset, + query42.data, + sizeof(query42.data)); + if (retval < 0) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: error reading F01_RMI_Query42\n", + __func__); + } else + has_recalibration = query42.has_recalibration; + } + + if (query42.has_ds4_queries) { + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + + query42_offset + 1, + query43, + sizeof(query43)); + if (retval < 0) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: error reading F01_RMI_Query43\n", + __func__); + } else { + can_access_ctrl9 = true; + query43_size = query43[0] & 0xf; + pr_debug("F01_RMI_Query43 size: %d\n", query43_size); + } + } + + if (query43_size) { + if (query43[2] & 0x1) { + current_ctrl_offset++; /* has F01_RMI_Ctrl6 */ + pr_debug("has F01_RMI_Ctrl6\n"); + } + if (query43[2] & 0x2) { + current_ctrl_offset++; /* has F01_RMI_Ctrl7 */ + pr_debug("has F01_RMI_Ctrl7\n"); + } + if (query43[2] & 0x4) { + current_ctrl_offset++; /* has F01_RMI_Ctrl8 */ + pr_debug("has F01_RMI_Ctrl8\n"); + } + } + + reg = find_packet_reg(regs, F01_RMI_Ctrl9); + if (unlikely(reg == NULL)) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: F01_RMI_Ctrl9 not present\n", __func__); + return; + } + + if (regs && reg) { + if (has_recalibration == false || can_access_ctrl9 == false) { + simple_deinit_packet_reg(reg); + dev_warn(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + return; + } + + current_ctrl_offset++; + pr_debug("F01_RMI_Ctrl9 offset %d\n", current_ctrl_offset); + + reg->data = simple_init_packet_reg(reg, current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } +} + + /** + * synaptics_rmi4_query_device() + * + * Called by synaptics_rmi4_probe(). + * + * This funtion scans the page description table, records the offsets + * to the register types of Function $01, sets up the function handlers + * for Function $11 and Function $12, determines the number of interrupt + * sources from the sensor, adds valid Functions with data inputs to the + * Function linked list, parses information from the query registers of + * Function $01, and enables the interrupt sources from the valid Functions + * with data inputs. + */ +static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char page_number; + unsigned char intr_count = 0; + unsigned char f01_query[F01_STD_QUERY_LEN] = {0}; + unsigned short pdt_entry_addr; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct f34_properties f34_query; + struct f34_properties_v2 f34_query_v2; + struct synaptics_rmi4_f01_device_status status; + + rmi = &(rmi4_data->rmi4_mod_info); + + INIT_LIST_HEAD(&rmi->support_fn_list); + + /* Scan the page description tables of the pages to service */ + for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) { + for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END; + pdt_entry_addr -= PDT_ENTRY_SIZE) { + pdt_entry_addr |= (page_number << 8); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + pdt_entry_addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + fhandler = NULL; + + if (rmi_fd.fn_number == 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Reached end of PDT\n", + __func__); + break; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: F%02x v%d found (page %d)\n", + __func__, rmi_fd.fn_number, rmi_fd.fn_version, + page_number); + + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F51: + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f51_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { + /* not critical, just continue */ + kfree(fhandler); + fhandler = NULL; + dev_err(&rmi4_data->i2c_client->dev, + "%s: No handler for F%x\n", + __func__, rmi_fd.fn_number); + } + break; + + case SYNAPTICS_RMI4_F34: + if (rmi_fd.fn_version < 2) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.query_base_addr + + F34_PROPERTIES_OFFSET, + &f34_query.data[0], + sizeof(f34_query)); + if (retval < 0) + return retval; + + if (f34_query.has_config_id) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.ctrl_base_addr, + rmi->config_id, + sizeof(rmi->config_id)); + if (retval < 0) + return retval; + } + } else if (rmi_fd.fn_version == 2) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.query_base_addr + + F34_PROPERTIES_OFFSET_V2, + &f34_query_v2.data[0], + sizeof(f34_query_v2)); + if (retval < 0) + return retval; + + if (f34_query_v2.has_config_id) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.ctrl_base_addr, + rmi->config_id, + sizeof(rmi->config_id)); + if (retval < 0) + return retval; + } + } + break; + + case SYNAPTICS_RMI4_F01: + rmi4_data->f01_query_base_addr = + rmi_fd.query_base_addr; + rmi4_data->f01_ctrl_base_addr = + rmi_fd.ctrl_base_addr; + rmi4_data->f01_data_base_addr = + rmi_fd.data_base_addr; + rmi4_data->f01_cmd_base_addr = + rmi_fd.cmd_base_addr; + pr_debug("query_base_addr 0x%04x\n", + rmi4_data->f01_query_base_addr); + pr_debug("ctrl_base_addr 0x%04x\n", + rmi4_data->f01_ctrl_base_addr); + pr_debug("data_base_addr 0x%04x\n", + rmi4_data->f01_data_base_addr); + pr_debug("cmd_base_addr 0x%04x\n", + rmi4_data->f01_cmd_base_addr); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return retval; + + rmi4_data->in_bootloader = + status.flash_prog == 1; + break; + + case SYNAPTICS_RMI4_F11: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f11_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to init F11\n", + __func__); + return retval; + } + break; + + case SYNAPTICS_RMI4_F12: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f12_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to init F12\n", + __func__); + return retval; + } + break; + + case SYNAPTICS_RMI4_F1A: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f1a_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + switch (retval) { + case -ENOMEM: + return retval; + case -ENODEV: + /* removing buttons handler if there */ + /* were no key codes in platform data */ + kfree(fhandler); + fhandler = NULL; + rmi_fd.intr_src_count = 0; + break; + } + break; + } + + /* Accumulate the interrupt count */ + intr_count += (rmi_fd.intr_src_count & MASK_3BIT); + + if (fhandler && rmi_fd.intr_src_count) { + pr_debug("adding handler for F%x\n", + fhandler->fn_number); + list_add_tail(&fhandler->link, + &rmi->support_fn_list); + } + } + } + + rmi4_data->num_of_intr_regs = (intr_count + 7) / 8; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Number of interrupt registers = %d\n", + __func__, rmi4_data->num_of_intr_regs); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr, + f01_query, + sizeof(f01_query)); + if (retval < 0) + return retval; + + /* RMI Version 4.0 currently supported */ + rmi->version_major = 4; + rmi->version_minor = 0; + + rmi->manufacturer_id = f01_query[0]; + rmi->product_props = f01_query[1]; + memcpy(&rmi->product_info[0], &f01_query[2], + SYNAPTICS_RMI4_PRODUCT_INFO_SIZE); + memcpy(rmi->serial, &f01_query[4], SYNAPTICS_RMI4_SERIAL_SIZE); + memcpy(rmi->product_id_string, &f01_query[11], + SYNAPTICS_RMI4_PRODUCT_ID_SIZE); + rmi->product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE] = 0; + + synaptics_dsx_validate_product_string(rmi->product_id_string); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr+PACKAGE_ID_OFFSET, + rmi->package_id, + sizeof(rmi->package_id)); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr+FW_VERSION_OFFSET, + rmi->build_id, + sizeof(rmi->build_id)); + if (retval < 0) + return retval; + + if (rmi->manufacturer_id != 1) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Non-Synaptics device found," + "manufacturer ID = %d\n", + __func__, rmi->manufacturer_id); + } + + if (!rmi4_data->in_bootloader && !rmi4_data->input_registered) { + retval = input_register_device(rmi4_data->input_dev); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to register input device\n", + __func__); + return retval; + } + rmi4_data->input_registered = true; + } + + memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask)); + + /* + * Map out the interrupt bit masks for the interrupt sources + * from the registered function handlers. + */ + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + rmi4_data->intr_mask[fhandler->intr_reg_num] |= + fhandler->intr_mask; + } + } + + /* interrupt mask for currently active functions */ + batohui(&rmi4_data->active_fn_intr_mask, + rmi4_data->intr_mask, rmi4_data->num_of_intr_regs); + pr_debug("active func intr_mask 0x%x\n", + rmi4_data->active_fn_intr_mask); + + if (rmi4_data->in_bootloader) + pr_info("Product: %s is in bootloader mode\n", + rmi->product_id_string); + else { + unsigned int config_id, firmware_id; + + batohui(&firmware_id, rmi->build_id, sizeof(rmi->build_id)); + batohui(&config_id, rmi->config_id, sizeof(config_id)); + pr_info("Product: %s, firmware id: %x, config id: %08x\n", + rmi->product_id_string, firmware_id, config_id); + /* has to be called after determining */ + /* the number of interrupt registers */ + synaptics_rmi4_scan_f01_reg_info(rmi4_data); + } + + return 0; +} + +static void synaptics_rmi4_cleanup(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fn *fhandler, *next_list_entry; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + list_for_each_entry_safe(fhandler, next_list_entry, + &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + synaptics_rmi4_f1a_kfree(fhandler); + else + kfree(fhandler->data); + list_del(&fhandler->link); + kfree(fhandler); + } +} + +static void synaptics_dsx_release_all(struct synaptics_rmi4_data *rmi4_data) +{ + /* reset some TSB global vars like fingers_on_2d after resume + * of reset touch IC + */ + if (rmi4_data->button_0d_enabled) { + tsb_buff_clean_flag = 1; + rmi4_data->fingers_on_2d = false; + } + + if (rmi4_data->input_dev) { + /* + * Enforce touch release event report to work-around + * such event missing while touch IC is off. + */ +#ifdef TYPE_B_PROTOCOL + int i; + for (i = 0; i < rmi4_data->num_of_fingers; i++) { + input_mt_slot(rmi4_data->input_dev, i); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, false); + } +#else + input_mt_sync(rmi4_data->input_dev); +#endif + input_sync(rmi4_data->input_dev); + } +} + +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data) +{ + int current_state, retval; + bool need_to_query = false; + + current_state = synaptics_dsx_get_state_safe(rmi4_data); + if (current_state == STATE_UNKNOWN) { + synaptics_rmi4_cleanup(rmi4_data); + need_to_query = true; + } + + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_HW_RESET); + if (retval > 0) + pr_debug("successful reset took %dms\n", retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + if (need_to_query) { + if (!rmi4_data->input_dev) { + retval = synaptics_dsx_alloc_input(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to allocate input device\n", + __func__); + return retval; + } + } + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to query device\n", + __func__); + return retval; + } + + if (control_access_block_get()) { + control_access_block_update_static(rmi4_data); + control_access_block_update_dynamic(rmi4_data); + } + + /* kick off detection work after touch ic changes its mode */ + if (exp_fn_ctrl.det_workqueue) + queue_delayed_work(exp_fn_ctrl.det_workqueue, + &exp_fn_ctrl.det_work, 0); + } + + return 0; +} + +static int control_access_block_update_static( + struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char one_data = 1; + unsigned char zero_data = 0; + unsigned char touch_data_size = 0; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct touch_control_access_block *cab = control_access_block_get(); + + if (!cab) + return -ENODEV; + + rmi = &(rmi4_data->rmi4_mod_info); + + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F11) { + touch_data_size = fhandler->size_of_data_register_block; + } else if (fhandler->fn_number == SYNAPTICS_RMI4_F12) { + touch_data_size = fhandler->size_of_data_register_block; + } + } + + control_access_block_zap(SYN_DSX_WAKE); + control_access_block_update_wake(rmi4_data->f01_ctrl_base_addr, + 1, &zero_data); + + control_access_block_zap(SYN_DSX_SLEEP); + control_access_block_update_sleep(rmi4_data->f01_ctrl_base_addr, + 1, &one_data); + + control_access_block_zap(SYN_DSX_RESET); + control_access_block_update_reset(rmi4_data->f01_cmd_base_addr, + 1, &one_data); + + control_access_block_zap(SYN_DSX_STATUS); + control_access_block_update_status(rmi4_data->f01_data_base_addr + 1, + rmi4_data->num_of_intr_regs); + + control_access_block_zap(SYN_DSX_MODE); + control_access_block_update_mode(rmi4_data->f01_data_base_addr, 1); + + if (touch_data_size) { + control_access_block_zap(SYN_DSX_DATA); + control_access_block_update_data_mt( + rmi4_data->f01_data_base_addr + + rmi4_data->num_of_intr_regs + 1, + touch_data_size, + rmi4_data->aod_mt); + } + + cab->do_sync = true; + pr_debug("updated control access block\n"); + return 0; +} + +static int control_access_block_update_dynamic( + struct synaptics_rmi4_data *rmi4_data) +{ + int i; + struct touch_control_access_block *cab = control_access_block_get(); + struct synaptics_dsx_patch *patch = NULL; + + if (!rmi4_data->patching_enabled || !cab || !patch || !patch->cfg_num) { + pr_debug("nothing to add to control access block\n"); + return 0; + } + + control_access_block_zap(SYN_DSX_CONFIG); + + for (i = 0; i < ARRAY_SIZE(synaptics_cfg_regs); i++) { + int f_number; + struct synaptics_dsx_func_patch *fp; + struct synaptics_rmi4_func_packet_regs *regs; + + /* skip query registers */ + if (synaptics_cfg_regs[i].f_number & QUERY_TYPE) + continue; + f_number = synaptics_cfg_regs[i].f_number; + regs = find_function(f_number); + + list_for_each_entry(fp, &patch->cfg_head, link) { + struct synaptics_rmi4_subpkt *subpkt; + struct synaptics_rmi4_packet_reg *reg; + + if (fp->func != f_number) + continue; + reg = find_packet_reg(regs, fp->regstr); + if (!reg || reg->offset < 0) + continue; + if (fp->subpkt >= reg->nr_subpkts) + continue; + subpkt = reg->subpkt + fp->subpkt; + + /* no need to check for misalignment here, since */ + /* synaptics_copy_multiple_subpkts() takes care of it */ + if (!subpkt->present || !subpkt->data) + continue; + + /* exclude power control from patch set */ + /* TODO: with modifiers introduced, this */ + /* check might not be necessary anymore */ + if (fp->func == SYNAPTICS_RMI4_F01 && + fp->regstr == 0 && fp->subpkt == 0) + continue; + + control_access_block_update_wo(SYN_DSX_CONFIG, + synaptics_cfg_regs[i].base_addr + reg->offset + + subpkt->offset, + fp->size, fp->bitmask, fp->data); + } + } + + cab->do_sync = true; + pr_debug("filled in control access block\n"); + return 0; +} + +/** +* synaptics_rmi4_detection_work() +* +* Called by the kernel at the scheduled time. +* +* This function is armed by synaptics_new_function call. It checks for +* the insertion and removal of other expansion Function modules such as +* rmi_dev and calls their initialization and removal callback functions +* accordingly. +*/ +static void synaptics_rmi4_detection_work(struct work_struct *work) +{ + struct synaptics_rmi4_exp_fn *exp_fhandler, *next_list_entry; + struct synaptics_rmi4_data *rmi4_data; +#ifdef FPS_NOTIFIER + static unsigned char fps_registered_retry; +#endif + int state, error; + + mutex_lock(&exp_fn_ctrl_mutex); + rmi4_data = exp_fn_ctrl.rmi4_data_ptr; + mutex_unlock(&exp_fn_ctrl_mutex); + + if (rmi4_data == NULL) { + if (exp_fn_ctrl.det_workqueue) + queue_delayed_work(exp_fn_ctrl.det_workqueue, + &exp_fn_ctrl.det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + return; + } +#ifdef FPS_NOTIFIER + if (rmi4_data->fps_detection_enabled && + !rmi4_data->is_fps_registered) { + error = FPS_register_notifier( + &rmi4_data->fps_notif, 0xBEEF, false); + if (error) { + if (++fps_registered_retry == + SYN_FPS_REGISTERED_RETRY_TIMES) { + rmi4_data->fps_detection_enabled = false; + } else if (exp_fn_ctrl.det_workqueue) + queue_delayed_work( + exp_fn_ctrl.det_workqueue, + &exp_fn_ctrl.det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + pr_err("Failed to register fps_notifier retry %d\n", + fps_registered_retry); + } else { + rmi4_data->is_fps_registered = true; + pr_debug("registered FPS notifier\n"); + } + } +#endif + + mutex_lock(&exp_fn_ctrl.list_mutex); + if (list_empty(&exp_fn_ctrl.fn_list)) + goto release_mutex; + + list_for_each_entry_safe(exp_fhandler, + next_list_entry, + &exp_fn_ctrl.fn_list, + link) { + if (exp_fhandler->func_init == NULL) { + if (exp_fhandler->inserted == true) { + exp_fhandler->func_remove(rmi4_data); + list_del(&exp_fhandler->link); + kfree(exp_fhandler); + } + continue; + } + + if (exp_fhandler->inserted == true) + continue; + + if (rmi4_data->in_bootloader && + (exp_fhandler->mode == IC_MODE_UI)) + continue; + + exp_fhandler->func_init(rmi4_data); + state = synaptics_dsx_get_state_safe(rmi4_data); + exp_fhandler->inserted = true; + if (exp_fhandler->fn_type == RMI_F54) { + int scan_failures = 0; + struct synaptics_rmi4_func_packet_regs *regs; + + regs = find_function(SYNAPTICS_RMI4_F54); + if (!regs) + continue; + + error = synaptics_rmi4_scan_f54_ctrl_reg_info(regs); + if (error) { + regs->nr_regs = 0; + pr_err("F54_Ctrl scan failed\n"); + } + + regs = find_function(SYNAPTICS_RMI4_F54 | COMMAND_TYPE); + error = synaptics_rmi4_scan_f54_cmd_reg_info(regs); + if (error) { + regs->nr_regs = 0; + pr_err("F54_Cmd scan failed\n"); + } + + regs = find_function(SYNAPTICS_RMI4_F54 | DATA_TYPE); + error = synaptics_rmi4_scan_f54_data_reg_info(regs); + if (error) { + regs->nr_regs = 0; + pr_err("F54_Data scan failed\n"); + scan_failures++; + } + + regs = find_function(SYNAPTICS_RMI4_F54 | QUERY_TYPE); + error = synaptics_rmi4_scan_f54_query_reg_info(regs); + if (error) { + regs->nr_regs = 0; + pr_err("F54_Query scan failed\n"); + scan_failures++; + } + + if (!scan_failures) + statistics_init(rmi4_data); + } + + if (exp_fhandler->fn_type == RMI_CTRL_ACCESS_BLK) { + error = control_access_block_update_static(rmi4_data); + /* FIXME: what if feature is disabled? */ + /* invalidate insertion */ + if (error) { + exp_fhandler->inserted = false; + pr_err("postpone CAB init\n"); + } else + control_access_block_update_dynamic(rmi4_data); + } + } + +release_mutex: + mutex_unlock(&exp_fn_ctrl.list_mutex); + + return; +} + +/** +* synaptics_rmi4_new_function() +* +* Called by other expansion Function modules in their module init and +* module exit functions. +* +* This function is used by other expansion Function modules such as +* rmi_dev to register themselves with the driver by providing their +* initialization and removal callback function pointers so that they +* can be inserted or removed dynamically at module init and exit times, +* respectively. +*/ +void synaptics_rmi4_new_function(enum exp_fn fn_type, bool insert, + int (*func_init)(struct synaptics_rmi4_data *rmi4_data), + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data), + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask), enum ic_modes mode) +{ + struct synaptics_rmi4_exp_fn *exp_fhandler; + + mutex_lock(&exp_fn_ctrl_mutex); + if (!exp_fn_ctrl.inited) { + mutex_init(&exp_fn_ctrl.list_mutex); + INIT_LIST_HEAD(&exp_fn_ctrl.fn_list); + exp_fn_ctrl.det_workqueue = + create_singlethread_workqueue("rmi_det_workqueue"); + if (IS_ERR_OR_NULL(exp_fn_ctrl.det_workqueue)) + pr_err("unable to create a workqueue\n"); + INIT_DELAYED_WORK(&exp_fn_ctrl.det_work, + synaptics_rmi4_detection_work); + exp_fn_ctrl.inited = true; + } + mutex_unlock(&exp_fn_ctrl_mutex); + + mutex_lock(&exp_fn_ctrl.list_mutex); + if (insert) { + exp_fhandler = kzalloc(sizeof(*exp_fhandler), GFP_KERNEL); + if (!exp_fhandler) { + pr_err("failed to alloc mem for expansion function\n"); + goto exit; + } + exp_fhandler->fn_type = fn_type; + exp_fhandler->func_init = func_init; + exp_fhandler->func_attn = func_attn; + exp_fhandler->func_remove = func_remove; + exp_fhandler->inserted = false; + exp_fhandler->mode = mode; + list_add_tail(&exp_fhandler->link, &exp_fn_ctrl.fn_list); + } else { + list_for_each_entry(exp_fhandler, &exp_fn_ctrl.fn_list, link) { + if (exp_fhandler->func_init == func_init) { + exp_fhandler->inserted = false; + exp_fhandler->func_init = NULL; + exp_fhandler->func_attn = NULL; + goto exit; + } + } + } + +exit: + mutex_unlock(&exp_fn_ctrl.list_mutex); + if (exp_fn_ctrl.det_workqueue) + queue_delayed_work(exp_fn_ctrl.det_workqueue, + &exp_fn_ctrl.det_work, 0); + return; +} +EXPORT_SYMBOL(synaptics_rmi4_new_function); + +static void synaptics_dsx_free_patch(struct synaptics_dsx_patch *patch_set) +{ + struct synaptics_dsx_func_patch *fp, *next_list_entry; + down(&patch_set->list_sema); + list_for_each_entry_safe(fp, next_list_entry, + &patch_set->cfg_head, link) { + kfree(fp->data); + list_del(&fp->link); + } + up(&patch_set->list_sema); +} + +static struct synaptics_dsx_patch *synaptics_dsx_init_patch(const char *name) +{ + struct synaptics_dsx_patch *patch_set; + patch_set = kzalloc(sizeof(struct synaptics_dsx_patch), GFP_KERNEL); + if (patch_set) { + patch_set->name = name; + sema_init(&patch_set->list_sema, 1); + INIT_LIST_HEAD(&patch_set->cfg_head); + } + return patch_set; +} + +static void synaptics_dsx_queued_resume(struct work_struct *w) +{ + struct synaptics_rmi4_data *rmi4_data = + container_of(w, struct synaptics_rmi4_data, resume_work); + synaptics_rmi4_resume(&(rmi4_data->i2c_client->dev)); +} + +static inline int synaptics_dsx_display_off(struct device *dev) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + cancel_work_sync(&rmi4_data->resume_work); + return synaptics_rmi4_suspend(dev); +} + +static inline int synaptics_dsx_display_on(struct device *dev) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + pr_debug("queue resume\n"); + queue_work(system_wq, &rmi4_data->resume_work); + return 0; +} + +static int rmi_reboot(struct notifier_block *nb, + unsigned long event, + void *unused) +{ + struct synaptics_rmi4_data *rmi4_data = + container_of(nb, struct synaptics_rmi4_data, rmi_reboot); +#ifdef REBOOT_DISABLE_REGULATOR + const struct synaptics_dsx_platform_data *platform_data = + rmi4_data->board; +#endif + +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + mmi_panel_unregister_notifier(&rmi4_data->panel_nb); +#elif defined(CONFIG_FB) + fb_unregister_client(&rmi4_data->panel_nb); +#endif + +#ifdef REBOOT_DISABLE_REGULATOR + if (platform_data->regulator_en) { + pr_debug("touch reboot - disable regulators\n"); + regulator_force_disable(rmi4_data->regulator); + regulator_put(rmi4_data->regulator); + msleep(1000); + } +#endif + + return NOTIFY_DONE; +} + +#if defined(USB_CHARGER_DETECTION) +/***************************************************************/ +/* USB charging source info from power_supply driver directly */ +/***************************************************************/ +static enum power_supply_property ps_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, +}; + +static const char * const ps_usb_supply[] = { "usb", }; +static bool ps_usb_present; +static int ps_get_property(struct power_supply *psy, + enum power_supply_property psp, union power_supply_propval *val) +{ + val->intval = 0; + return 0; +} + +static void ps_external_power_changed(struct power_supply *psy) +{ + struct power_supply *usb_psy = power_supply_get_by_name("usb"); + union power_supply_propval pval = {0}; + struct synaptics_rmi4_data *rmi4_data = container_of(psy, + struct synaptics_rmi4_data, psy); + struct device *dev = &rmi4_data->i2c_client->dev; + struct config_modifier *cm; + int is_plugged, state; + unsigned char charger_set; + + if (!usb_psy || !usb_psy->get_property) + return; + + usb_psy->get_property(usb_psy, POWER_SUPPLY_PROP_PRESENT, &pval); + dev_dbg(dev, "external_power_changed: %d\n", pval.intval); + + if (ps_usb_present == (pval.intval == 1)) { + ps_usb_present = pval.intval == 1; + return; + } + + is_plugged = !!pval.intval; + down(&rmi4_data->modifiers.list_sema); + list_for_each_entry(cm, &rmi4_data->modifiers.mod_head, link) { + if (cm->id == SYNA_MOD_CHARGER) { + cm->effective = is_plugged; + break; + } + } + up(&rmi4_data->modifiers.list_sema); + /* in case there is no patch required */ + if (!cm) + return; + state = synaptics_dsx_get_state_safe(rmi4_data); + dev_info(dev, "power supply presence %d in state %d\n", + is_plugged, state); + /* reset touch ic on power supply presence change */ + if (state == STATE_ACTIVE) { + /* set unknown state to ensure IRQ gets */ + /* enabled on state transition to active */ + synaptics_dsx_sensor_state(rmi4_data, STATE_UNKNOWN); + /* disable IRQ to handle reset */ + synaptics_rmi4_irq_enable(rmi4_data, false); + if (!is_plugged) { + int retval; + if (rmi4_data->pullout_reset) { + /* perform SW reset to restore defaults */ + retval = synaptics_dsx_ic_reset( + rmi4_data, RMI4_SW_RESET); + if (retval < 0) + dev_err(dev, "power supply: sw reset"\ + " failed %d\n", retval); + } else { + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &charger_set, + sizeof(charger_set)); + if (retval < 0) + dev_err(dev, "read i2c register 0x%02x"\ + " failed %d\n", rmi4_data->f01_ctrl_base_addr, retval); + + charger_set &= (MASK_8BIT & ~(1 << 5)); + retval = synaptics_rmi4_i2c_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &charger_set, + sizeof(charger_set)); + if (retval < 0) + dev_err(dev, "write i2c register 0x%02x"\ + " failed %d\n", rmi4_data->f01_ctrl_base_addr, retval); + }; + } + synaptics_dsx_sensor_ready_state(rmi4_data, false); + /* TODO: just apply a single patch on insertion??? */ + } + ps_usb_present = pval.intval == 1; +} + +#define ps_notifier_unregister(r) power_supply_unregister(&r->psy) + +static int ps_notifier_register(struct synaptics_rmi4_data *rmi4_data) +{ + int error; + struct device *dev = &rmi4_data->i2c_client->dev; + + rmi4_data->psy.num_supplies = 1; + rmi4_data->psy.supplied_from = ((char **) ps_usb_supply); + rmi4_data->psy.name = "synaptics-psy"; + rmi4_data->psy.type = POWER_SUPPLY_TYPE_UNKNOWN; + rmi4_data->psy.properties = ps_props; + rmi4_data->psy.num_properties = ARRAY_SIZE(ps_props); + rmi4_data->psy.get_property = ps_get_property; + rmi4_data->psy.external_power_changed = ps_external_power_changed; + + error = power_supply_register(dev, &rmi4_data->psy); + if (error < 0) { + dev_err(dev, "power_supply_register failed rc=%d\n", error); + return error; + } + return 0; +} +#else +#define ps_notifier_register(r) +#define ps_notifier_unregister(r) +#endif + +#include <linux/major.h> +#include <linux/kdev_t.h> + +#define ATTR_WO_USER(attr) __ATTR(attr, 0644, NULL, attr##_store) + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!rmi4_data) { + pr_err("cannot get rmi4_data pointer\n"); + return (ssize_t)0; + } + path = kobject_get_path(&rmi4_data->i2c_client->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Temporary interface to be able to change clipping area dimensions in run time */ +/* Exmaple: echo "450 1850 610 1919" > clipa */ +static ssize_t clipa_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_clip_area clipa = {0}; + + if (sscanf(buf, "%u %u %u %u", + &clipa.xul_clip, + &clipa.yul_clip, + &clipa.xbr_clip, + &clipa.ybr_clip) != 4) { + pr_err("Insufficient parameters list\n"); + return -EINVAL; + } + + pr_info("new clipping area: xul=%u, yul=%u, xbr=%u, ybr=%u\n", + clipa.xul_clip, clipa.yul_clip, + clipa.xbr_clip, clipa.ybr_clip); + *rmi4_data->clipa = clipa; + + return (ssize_t)count; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "synaptics"); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + ATTR_WO_USER(clipa), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +static int synaptics_dsx_sysfs_touchscreen( + struct synaptics_rmi4_data *rmi4_data, bool create) +{ + struct synaptics_rmi4_device_info *rmi = &(rmi4_data->rmi4_mod_info); + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + static int minor; + + if (create) { + minor = input_get_new_minor(rmi4_data->i2c_client->addr, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + pr_info("assigned minor %d\n", minor); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + rmi4_data, rmi->product_id_string); + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + pr_err("error creating touchscreen class\n"); + + return -ENODEV; +} + + /** + * synaptics_rmi4_probe() + * + * Called by the kernel when an association with an I2C device of the + * same name is made (after doing i2c_add_driver). + * + * This funtion allocates and initializes the resources for the driver + * as an input driver, turns on the power to the sensor, queries the + * sensor for its supported Functions and characteristics, registers + * the driver to the input subsystem, sets up the interrupt, and creates + * a work queue for detection of other expansion Function modules. + */ +static int synaptics_rmi4_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int retval; + struct pinctrl *pinctrl; + unsigned char attr_count; + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_dsx_platform_data *platform_data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "%s: SMBus byte data not supported\n", + __func__); + return -EIO; + } + + rmi4_data = kzalloc(sizeof(*rmi4_data), GFP_KERNEL); + if (!rmi4_data) { + dev_err(&client->dev, + "%s: Failed to alloc mem for rmi4_data\n", + __func__); + return -ENOMEM; + } + + /* assign pointer to client structure right away for further use */ + rmi4_data->i2c_client = client; + rmi4_data->event_blank = FB_EARLY_EVENT_BLANK; + + if (client->dev.of_node) + platform_data = synaptics_dsx_of_init(client, rmi4_data); + else + platform_data = client->dev.platform_data; + + if (!platform_data) { + dev_err(&client->dev, + "%s: No platform data found\n", + __func__); + kfree(rmi4_data); + return -EINVAL; + } + + rmi = &(rmi4_data->rmi4_mod_info); + + rmi4_data->current_page = MASK_8BIT; + rmi4_data->board = platform_data; + rmi4_data->irq_enabled = false; + atomic_set(&rmi4_data->touch_stopped, 1); + rmi4_data->ic_on = true; + rmi4_data->flash_enabled = false; + rmi4_data->f12_data_registers_ptr = + find_function(SYNAPTICS_RMI4_F12 | DATA_TYPE); + rmi4_data->i2c_read = synaptics_rmi4_i2c_read; + rmi4_data->i2c_write = synaptics_rmi4_i2c_write; + rmi4_data->set_state = synaptics_dsx_sensor_state; + rmi4_data->ready_state = synaptics_dsx_sensor_ready_state; + rmi4_data->irq_enable = synaptics_rmi4_irq_enable; + rmi4_data->reset_device = synaptics_rmi4_reset_device; + + /* Initialize some resume debug information */ + rmi4_data->resume_info = kzalloc( + sizeof(struct synaptics_rmi4_resume_info) * + MAX_NUMBER_TRACKED_RESUMES, + GFP_KERNEL); + if (!rmi4_data->resume_info) { + dev_err(&client->dev, + "%s: Failed to allocate memory for resume information\n", + __func__); + rmi4_data->number_resumes = 0; + } else + rmi4_data->number_resumes = MAX_NUMBER_TRACKED_RESUMES; + rmi4_data->last_resume = -1; + + /* Initialize some interrupit timing debug information */ + rmi4_data->irq_info = kzalloc( + sizeof(struct synaptics_rmi4_irq_info) * + MAX_NUMBER_TRACKED_IRQS, + GFP_KERNEL); + if (!rmi4_data->irq_info) { + dev_err(&client->dev, + "%s: Failed to allocate memory for IRQ debug information\n", + __func__); + rmi4_data->number_irq = 0; + } else + rmi4_data->number_irq = MAX_NUMBER_TRACKED_IRQS; + rmi4_data->last_irq = -1; + + mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex)); + mutex_init(&(rmi4_data->state_mutex)); + atomic_set(&wakeup_gesture, 0); + atomic_set(&glove_ctrl, 0); + pinctrl = devm_pinctrl_get_select(&rmi4_data->i2c_client->dev, + "active"); + if (IS_ERR(pinctrl)) { + long int error = PTR_ERR(pinctrl); + dev_err(&rmi4_data->i2c_client->dev, + "%s: pinctrl failed err %ld\n", __func__, error); + goto err_input_device; + } + + if (platform_data->gpio_config) + retval = platform_data->gpio_config(platform_data, true); + else { + retval = gpio_request(platform_data->reset_gpio, + RESET_GPIO_NAME); + if (!retval) + retval = gpio_direction_output( + platform_data->reset_gpio, 1); + } + if (retval < 0) { + dev_err(&client->dev, + "%s: Failed to configure GPIO\n", + __func__); + goto err_input_device; + } + + /* get irq number initialized before calling reset */ + rmi4_data->irq = gpio_to_irq(platform_data->irq_gpio); + + i2c_set_clientdata(client, rmi4_data); + + retval = synaptics_dsx_alloc_input(rmi4_data); + if (retval < 0) { + dev_err(&client->dev, + "%s: Failed to allocate input device\n", + __func__); + goto err_free_gpio; + } + + rmi4_data->regulator = regulator_get(&client->dev, "touch_vdd"); + if (IS_ERR(rmi4_data->regulator)) { + if (PTR_ERR(rmi4_data->regulator) == -EPROBE_DEFER) { + retval = PTR_ERR(rmi4_data->regulator); + goto err_regulator_defer; + } + dev_warn(&client->dev, + "%s: Failed to get regulator\n", + __func__); + } else { + int error = regulator_enable(rmi4_data->regulator); + if (error) { + regulator_put(rmi4_data->regulator); + dev_err(&client->dev, + "%s: Error %d enabling touch-vdd regulator\n", + __func__, error); + goto err_regulator_enable; + } + platform_data->regulator_en = true; + pr_debug("touch-vdd regulator is %s\n", + regulator_is_enabled(rmi4_data->regulator) ? + "on" : "off"); + } + + rmi4_data->vdd_quirk = devm_regulator_get(&client->dev, "vdd_quirk"); + if (!IS_ERR(rmi4_data->vdd_quirk)) { + retval = regulator_enable(rmi4_data->vdd_quirk); + if (retval) { + dev_err(&client->dev, "Failed to enable vdd-quirk\n"); + goto vdd_quirk_error; + } + } else { + retval = PTR_ERR(rmi4_data->regulator); + if (retval == -EPROBE_DEFER) + goto vdd_quirk_error; + } + + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_HW_RESET); + if (retval > 0) + pr_debug("successful reset took %dms\n", retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(&client->dev, + "%s: Failed to query device\n", + __func__); + goto err_query_device; + } + + init_waitqueue_head(&rmi4_data->wait); + INIT_WORK(&rmi4_data->resume_work, synaptics_dsx_queued_resume); + +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + rmi4_data->panel_nb.pre_display_off = synaptics_rmi4_suspend; + rmi4_data->panel_nb.display_on = synaptics_rmi4_resume; + rmi4_data->panel_nb.dev = &client->dev; + if (!mmi_panel_register_notifier(&rmi4_data->panel_nb)) + pr_info("registered MMI panel notifier\n"); + else + dev_err(&client->dev, + "%s: Unable to register MMI notifier\n", + __func__); +#elif defined(CONFIG_FB) + rmi4_data->panel_nb.notifier_call = synaptics_dsx_panel_cb; + if (!fb_register_client(&rmi4_data->panel_nb)) + pr_debug("registered FB notifier\n"); + else + dev_err(&client->dev, + "%s: Unable to register FB notifier\n", + __func__); +#endif + mutex_lock(&exp_fn_ctrl_mutex); + if (!exp_fn_ctrl.inited) { + mutex_init(&exp_fn_ctrl.list_mutex); + INIT_LIST_HEAD(&exp_fn_ctrl.fn_list); + exp_fn_ctrl.det_workqueue = + create_singlethread_workqueue("rmi_det_workqueue"); + if (IS_ERR_OR_NULL(exp_fn_ctrl.det_workqueue)) + pr_err("unable to create a workqueue\n"); + INIT_DELAYED_WORK(&exp_fn_ctrl.det_work, + synaptics_rmi4_detection_work); + exp_fn_ctrl.inited = true; + } + mutex_unlock(&exp_fn_ctrl_mutex); + +#ifdef CONFIG_TOUCHSCREEN_PROPERTY + property_init(); +#endif + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(&client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto err_sysfs; + } + } + + synaptics_dsx_sensor_ready_state(rmi4_data, true); + + rmi4_data->pullout_reset = false; + rmi4_data->rmi_reboot.notifier_call = rmi_reboot; + rmi4_data->rmi_reboot.next = NULL; + rmi4_data->rmi_reboot.priority = 1; + retval = register_reboot_notifier(&rmi4_data->rmi_reboot); + if (retval) + dev_err(&client->dev, "register for reboot failed\n"); + + mutex_lock(&exp_fn_ctrl_mutex); + exp_fn_ctrl.rmi4_data_ptr = rmi4_data; + mutex_unlock(&exp_fn_ctrl_mutex); + + +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS + if (rmi4_data->folio_detection_enabled) { + /* register notifier at the end of probe to */ + /* avoid unnecessary reset in STANDBY state */ + rmi4_data->folio_notif.notifier_call = folio_notifier_callback; + dev_dbg(&client->dev, "registering folio notifier\n"); + retval = mmi_hall_register_notifier(&rmi4_data->folio_notif, + MMI_HALL_FOLIO, true); + if (retval) { + dev_err(&client->dev, + "Error registering folio_notifier: %d\n", + retval); + /* inability to register folio notifications handler */ + /* is not fatal, thus reset return value to success */ + retval = 0; + } + } +#endif + synaptics_dsx_sysfs_touchscreen(rmi4_data, true); + + if (rmi4_data->charger_detection_enabled) + ps_notifier_register(rmi4_data); + +#ifdef FPS_NOTIFIER + if (rmi4_data->fps_detection_enabled) { + rmi4_data->fps_notif.notifier_call = fps_notifier_callback; + dev_dbg(&client->dev, "registering FPS notifier\n"); + retval = FPS_register_notifier( + &rmi4_data->fps_notif, 0xBEEF, false); + if (retval) { + if (exp_fn_ctrl.det_workqueue) + queue_delayed_work(exp_fn_ctrl.det_workqueue, + &exp_fn_ctrl.det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + dev_err(&client->dev, + "Failed to register fps_notifier: %d\n", + retval); + retval = 0; + } else + rmi4_data->is_fps_registered = true; + } +#endif + + tpd_info_t = (struct tpd_version_info*)kzalloc( + sizeof(struct tpd_version_info), GFP_KERNEL); + if(!tpd_info_t) + { + pr_err("failed to alloc tpd_info_t\n"); + //return -ENOMEM; + } + return retval; + +err_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + } + +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + mmi_panel_unregister_notifier(&rmi4_data->panel_nb); +#elif defined(CONFIG_FB) + fb_unregister_client(&rmi4_data->panel_nb); +#endif + +err_query_device: + if (rmi4_data->input_registered) { + input_unregister_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + } + + if (!IS_ERR(rmi4_data->vdd_quirk)) + regulator_disable(rmi4_data->vdd_quirk); + +vdd_quirk_error: + if (platform_data->regulator_en) { + regulator_disable(rmi4_data->regulator); + regulator_put(rmi4_data->regulator); + } + +err_regulator_enable: + synaptics_rmi4_cleanup(rmi4_data); + +err_regulator_defer: + input_free_device(rmi4_data->input_dev); +err_free_gpio: + if (platform_data->gpio_config) + gpio_free(platform_data->irq_gpio); + gpio_set_value(platform_data->reset_gpio, 0); + gpio_free(platform_data->reset_gpio); +err_input_device: + kfree(rmi4_data); + + return retval; +} + + /** + * synaptics_rmi4_remove() + * + * Called by the kernel when the association with an I2C device of the + * same name is broken (when the driver is unloaded). + * + * This funtion terminates the work queue, stops sensor data acquisition, + * frees the interrupt, unregisters the driver from the input subsystem, + * turns off the power to the sensor, and frees other allocated resources. + */ +static int synaptics_rmi4_remove(struct i2c_client *client) +{ + unsigned char attr_count; + struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client); + struct synaptics_rmi4_device_info *rmi; + const struct synaptics_dsx_platform_data *platform_data = + rmi4_data->board; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (exp_fn_ctrl.inited) { + cancel_delayed_work_sync(&exp_fn_ctrl.det_work); + flush_workqueue(exp_fn_ctrl.det_workqueue); + destroy_workqueue(exp_fn_ctrl.det_workqueue); + } + + atomic_set(&rmi4_data->touch_stopped, 1); + wake_up(&rmi4_data->wait); + + synaptics_rmi4_irq_enable(rmi4_data, false); + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + } + + if (rmi4_data->input_registered) { + input_unregister_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + } + + if (!IS_ERR(rmi4_data->vdd_quirk)) + regulator_disable(rmi4_data->vdd_quirk); + + if (platform_data->regulator_en) { + regulator_disable(rmi4_data->regulator); + regulator_put(rmi4_data->regulator); + } + +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + mmi_panel_unregister_notifier(&rmi4_data->panel_nb); +#elif defined(CONFIG_FB) + fb_unregister_client(&rmi4_data->panel_nb); +#endif + + synaptics_dsx_sysfs_touchscreen(rmi4_data, false); + synaptics_rmi4_cleanup(rmi4_data); + +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS + if (rmi4_data->folio_detection_enabled) + mmi_hall_unregister_notifier(rmi4_data); +#endif + if (rmi4_data->charger_detection_enabled) + ps_notifier_unregister(rmi4_data); +#ifdef FPS_NOTIFIER + if (rmi4_data->is_fps_registered) + FPS_unregister_notifier(&rmi4_data->fps_notif, 0xBEEF); +#endif + kfree(rmi4_data); + kfree(tpd_info_t); + return 0; +} + +#ifdef FPS_NOTIFIER +static int fps_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + int state, fps_state = *(int *)data; + struct synaptics_rmi4_data *rmi4_data = + container_of(self, struct synaptics_rmi4_data, fps_notif); + + if (rmi4_data && event == 0xBEEF && + rmi4_data && rmi4_data->i2c_client) { + struct config_modifier *cm = + modifier_by_id(rmi4_data, SYNA_MOD_FPS); + if (!cm) { + dev_err(&rmi4_data->i2c_client->dev, + "No FPS modifier found\n"); + goto done; + } + + state = synaptics_dsx_get_state_safe(rmi4_data); + dev_dbg(&rmi4_data->i2c_client->dev, + "FPS: %s(%d), suspend flag: %d, BL flag: %d\n", + synaptics_dsx_state_name(state), state, + atomic_read(&rmi4_data->touch_stopped), + rmi4_data->in_bootloader); + if (fps_state) {/* on */ + rmi4_data->clipping_on = true; + rmi4_data->clipa = cm->clipa; + cm->effective = true; + } else {/* off */ + rmi4_data->clipping_on = false; + rmi4_data->clipa = NULL; + cm->effective = false; + } + pr_info("FPS: clipping is %s\n", + rmi4_data->clipping_on ? "ON" : "OFF"); + } +done: + return 0; +} +#endif + +#if defined(CONFIG_FB) && !defined(CONFIG_MMI_PANEL_NOTIFICATIONS) +static int synaptics_dsx_panel_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + struct synaptics_rmi4_data *rmi4_data = + container_of(nb, struct synaptics_rmi4_data, panel_nb); + + if ((event == rmi4_data->event_blank || event == FB_EVENT_BLANK) && + evdata && evdata->info && evdata->info->node == 0 && + evdata->data && rmi4_data) { + int *blank = evdata->data; + pr_debug("fb notification: event = %lu blank = %d\n", event, *blank); + /* entering suspend upon early blank or in-progress blank event*/ + /* to ensure shared power supply is still on */ + /* for in-cell design touch solutions */ + if (event == rmi4_data->event_blank) { + if (*blank != FB_BLANK_POWERDOWN) + return 0; + synaptics_dsx_display_off(&rmi4_data->i2c_client->dev); + } else if (*blank == FB_BLANK_UNBLANK || + (*blank == FB_BLANK_NORMAL && + atomic_read(&rmi4_data->touch_stopped))) { + synaptics_dsx_display_on(&rmi4_data->i2c_client->dev); + } + } + + return 0; +} +#endif + +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS +static int folio_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + int state, folio_state = *(int *)data; + struct synaptics_rmi4_data *rmi4_data = + container_of(self, struct synaptics_rmi4_data, folio_notif); + + if (rmi4_data && event == MMI_HALL_FOLIO && + rmi4_data && rmi4_data->i2c_client) { + struct config_modifier *cm = + modifier_by_id(rmi4_data, SYNA_MOD_FOLIO); + if (!cm) { + dev_err(&rmi4_data->i2c_client->dev, + "No FOLIO modifier found\n"); + goto done; + } + + state = synaptics_dsx_get_state_safe(rmi4_data); + dev_dbg(&rmi4_data->i2c_client->dev, + "state: %s(%d), suspend flag: %d, BL flag: %d\n", + synaptics_dsx_state_name(state), state, + atomic_read(&rmi4_data->touch_stopped), + rmi4_data->in_bootloader); + if (folio_state) {/* close */ + rmi4_data->clipa = cm->clipa; + rmi4_data->clipping_on = true; + cm->effective = true; + } else {/* open */ + rmi4_data->clipa = NULL; + rmi4_data->clipping_on = false; + cm->effective = false; + } + + if (control_access_block_get()) + control_access_block_update_dynamic(rmi4_data); + + dev_info(&rmi4_data->i2c_client->dev, "folio: %s\n", + folio_state ? "CLOSED" : "OPENED"); + + if (!(state & STATE_UI)) { + dev_dbg(&rmi4_data->i2c_client->dev, "Not in UI\n"); + goto done; + } + + if (!rmi4_data->in_bootloader) + synaptics_dsx_enforce_modifiers(rmi4_data, cm); + } +done: + return 0; +} +#endif + +static void synaptics_dsx_resumeinfo_start( + struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_resume_info *tmp; + + if (rmi4_data->number_resumes > 0) { + rmi4_data->last_resume++; + if (rmi4_data->last_resume >= rmi4_data->number_resumes) + rmi4_data->last_resume = 0; + + tmp = &(rmi4_data->resume_info[rmi4_data->last_resume]); + getnstimeofday(&(tmp->start)); + tmp->ignored_events = 0; + tmp->isr.tv_sec = 0; + tmp->isr.tv_nsec = 0; + tmp->send_touch.tv_sec = 0; + tmp->send_touch.tv_nsec = 0; + tmp->purge_off.tv_sec = 0; + tmp->purge_off.tv_nsec = 0; + } +} + +static void synaptics_dsx_resumeinfo_finish( + struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_resume_info *tmp; + + if (rmi4_data->number_resumes > 0 && rmi4_data->last_resume >= 0) { + tmp = &(rmi4_data->resume_info[rmi4_data->last_resume]); + getnstimeofday(&(tmp->finish)); + } +} + +static void synaptics_dsx_resumeinfo_isr( + struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_resume_info *tmp; + + if (rmi4_data->number_resumes > 0 && rmi4_data->last_resume >= 0) { + tmp = &(rmi4_data->resume_info[rmi4_data->last_resume]); + if (tmp->isr.tv_sec == 0) + getnstimeofday(&(tmp->isr)); + } +} + +static void synaptics_dsx_resumeinfo_purgeoff( + struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_resume_info *tmp; + + if (rmi4_data->number_resumes > 0 && rmi4_data->last_resume >= 0) { + tmp = &(rmi4_data->resume_info[rmi4_data->last_resume]); + + if (tmp->purge_off.tv_sec == 0) + getnstimeofday(&(tmp->purge_off)); + } +} + +static void synaptics_dsx_resumeinfo_ignore( + struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_resume_info *tmp; + + if (rmi4_data->number_resumes > 0 && rmi4_data->last_resume >= 0) { + tmp = &(rmi4_data->resume_info[rmi4_data->last_resume]); + + tmp->ignored_events++; + } +} + +static void synaptics_dsx_resumeinfo_touch( + struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_resume_info *tmp; + + if (rmi4_data->number_resumes > 0 && rmi4_data->last_resume >= 0) { + tmp = &(rmi4_data->resume_info[rmi4_data->last_resume]); + + if (tmp->send_touch.tv_sec == 0) + getnstimeofday(&(tmp->send_touch)); + } +} + /** + * synaptics_rmi4_suspend() + * + * Called by the kernel during the suspend phase when the system + * enters suspend. + * + * This function stops finger data acquisition and puts the sensor to + * sleep, disables the interrupt, and turns off the power to the sensor. + */ +static int synaptics_rmi4_suspend(struct device *dev) +{ + struct pinctrl *pinctrl; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + const struct synaptics_dsx_platform_data *platform_data = + rmi4_data->board; + static char ud_stats[PAGE_SIZE]; + + if (atomic_cmpxchg(&rmi4_data->touch_stopped, 0, 1) == 1) + return 0; + + rmi4_data->flash_enabled = false; + synaptics_dsx_sensor_state(rmi4_data, STATE_SUSPEND); + synaptics_dsx_release_all(rmi4_data); + + if (gStat.enabled) + statistics_stop_timekeeping(); + + synaptics_dsx_ud_stat(rmi4_data, ud_stats, sizeof(ud_stats)); + pr_info("%s\n", ud_stats); + + if (rmi4_data->purge_enabled) { + int value = 1; /* set flag */ + atomic_set(&rmi4_data->panel_off_flag, value); + pr_debug("touches purge is %s\n", value ? "ON" : "OFF"); + } + + if (rmi4_data->ic_on) { + /* use pinctrl to put touch RESET GPIO into SUSPEND state */ + gpio_free(platform_data->reset_gpio); + pinctrl = devm_pinctrl_get_select_default( + &rmi4_data->i2c_client->dev); + if (IS_ERR(pinctrl)) + dev_err(&rmi4_data->i2c_client->dev, + "pinctrl failed err %ld\n", PTR_ERR(pinctrl)); + + /* if touch REGULATOR is available - turn it OFF */ + if (platform_data->regulator_en) { + regulator_disable(rmi4_data->regulator); + pr_debug("touch-vdd regulator is %s\n", + regulator_is_enabled(rmi4_data->regulator) ? + "on" : "off"); + } + + rmi4_data->ic_on = false; + } + + return 0; +} + + /** + * synaptics_rmi4_resume() + * + * Called by the kernel during the resume phase when the system + * wakes up from suspend. + * + * This function turns on the power to the sensor, wakes the sensor + * from sleep, enables the interrupt, and starts finger data + * acquisition. + */ +static int synaptics_rmi4_resume(struct device *dev) +{ + int retval; + int reset = RMI4_HW_RESET; + struct pinctrl *pinctrl; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + const struct synaptics_dsx_platform_data *platform_data = + rmi4_data->board; + + if (atomic_cmpxchg(&rmi4_data->touch_stopped, 1, 0) == 0) + return 0; + + synaptics_dsx_resumeinfo_start(rmi4_data); + perftags_write("touch resume start"); + + if (!rmi4_data->ic_on) { + /* if touch REGULATOR is avaialble - turn it ON */ + if (platform_data->regulator_en) { + int error = regulator_enable(rmi4_data->regulator); + if (error) { + pr_err("Error %d enabling touch-vdd regulator\n", + error); + return error; + } + pr_debug("touch-vdd regulator is %s\n", + regulator_is_enabled(rmi4_data->regulator) ? + "on" : "off"); + } + + /* if RESET GPIO is in SUSPEND state - no HW reset */ + retval = gpio_get_value(platform_data->reset_gpio); + pr_debug("reset gpio state: %d\n", retval); + if (retval == 0) + reset = RMI4_WAIT_READY; + else /* if not in reset, IRQ might be enabled */ + synaptics_rmi4_irq_enable(rmi4_data, false); + + pinctrl = devm_pinctrl_get_select(&rmi4_data->i2c_client->dev, + "active"); + if (IS_ERR(pinctrl)) { + long int error = PTR_ERR(pinctrl); + dev_err(&rmi4_data->i2c_client->dev, + "pinctrl failed err %ld\n", error); + } + + if (gpio_request(platform_data->reset_gpio, + RESET_GPIO_NAME) < 0) + pr_err("failed to request reset gpio\n"); + + gpio_direction_output(platform_data->reset_gpio, 1); + rmi4_data->ic_on = true; + } + + /* perform HW reset if needed and wait for touch IC boot completion */ + retval = synaptics_dsx_ic_reset(rmi4_data, reset); + if (retval > 0) + pr_debug("waited for idle %dms\n", retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + /* read touch IC state */ + synaptics_dsx_sensor_ready_state(rmi4_data, false); + /* transition to active state is completed - allow flashing */ + rmi4_data->flash_enabled = true; + + if (rmi4_data->purge_enabled) { + int value = 0; /* clear flag */ + atomic_set(&rmi4_data->panel_off_flag, value); + pr_debug("touches purge is %s\n", value ? "ON" : "OFF"); + } + + if (gStat.enabled) + statistics_start_timekeeping(rmi4_data); + + /* set gloved status while resume */ + tpd_gloved_control(atomic_read(&glove_ctrl)); + + perftags_write("touch resume end"); + synaptics_dsx_resumeinfo_finish(rmi4_data); + return 0; +} + +static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = { + .suspend = synaptics_rmi4_suspend, + .resume = synaptics_rmi4_resume, +}; + +#ifdef CONFIG_OF +static const struct of_device_id synaptics_rmi4_match_tbl[] = { + { .compatible = "synaptics," DRIVER_NAME }, + { }, +}; +MODULE_DEVICE_TABLE(of, synaptics_rmi4_match_tbl); +#endif + +static const struct i2c_device_id synaptics_rmi4_id_table[] = { + {DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); + +static struct i2c_driver synaptics_rmi4_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#if !defined(CONFIG_FB) && defined(CONFIG_PM) + .pm = &synaptics_rmi4_dev_pm_ops, +#endif + }, + .probe = synaptics_rmi4_probe, + .remove = synaptics_rmi4_remove, + .id_table = synaptics_rmi4_id_table, +}; + + /** + * synaptics_rmi4_init() + * + * Called by the kernel during do_initcalls (if built-in) + * or when the driver is loaded (if a module). + * + * This function registers the driver to the I2C subsystem. + * + */ +static int __init synaptics_rmi4_init(void) +{ + return i2c_add_driver(&synaptics_rmi4_driver); +} + + /** + * synaptics_rmi4_exit() + * + * Called by the kernel when the driver is unloaded. + * + * This funtion unregisters the driver from the I2C subsystem. + * + */ +static void __exit synaptics_rmi4_exit(void) +{ + i2c_del_driver(&synaptics_rmi4_driver); +} + +module_init(synaptics_rmi4_init); +module_exit(synaptics_rmi4_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX I2C Touch Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SYNAPTICS_DSX_DRIVER_VERSION); |
