diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 99994ceb3a3d..229e52cb7bf3 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1444,4 +1444,6 @@ config TOUCHSCREEN_ZINITIX To compile this driver as a module, choose M here: the module will be called zinitix. +source "drivers/input/touchscreen/hxchipset/Kconfig" + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 6cd2a56f5bd0..17c7d4213c83 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -131,3 +131,4 @@ obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW) += raspberrypi-ts.o obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5xx.o obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o +obj-$(CONFIG_TOUCHSCREEN_HIMAX_CHIPSET) += hxchipset/ diff --git a/drivers/input/touchscreen/hxchipset/Kconfig b/drivers/input/touchscreen/hxchipset/Kconfig new file mode 100644 index 000000000000..cba7f53c5fa3 --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/Kconfig @@ -0,0 +1,66 @@ +#SPDX-License-Identifier: GPL-2.0 +# +# Himax Touchscreen driver configuration +# +config TOUCHSCREEN_HIMAX_CHIPSET + tristate "HIAMX touchscreens support" + +if TOUCHSCREEN_HIMAX_CHIPSET + +config TOUCHSCREEN_HIMAX_COMMON + tristate "HIMAX chipset i2c touchscreen" + depends on TOUCHSCREEN_HIMAX_CHIPSET + help + This enables support for HIMAX CHIPSET over I2C based touchscreens. + +choice + prompt "HIMAX touch IC types" + depends on TOUCHSCREEN_HIMAX_COMMON + default TOUCHSCREEN_HIMAX_INCELL + +config TOUCHSCREEN_HIMAX_INCELL + bool "HIMAX chipset in-cell function" + depends on TOUCHSCREEN_HIMAX_COMMON + help + This enables support for HIMAX CHIPSET of in-cell function. + +endchoice +# ***************** In-cell Start ***************** +config TOUCHSCREEN_HIMAX_IC_HX83193 + tristate "HIMAX chipset HX83193 function" + depends on TOUCHSCREEN_HIMAX_INCELL + help + This enables support for HIMAX CHIPSET of HX83193. + +config TOUCHSCREEN_HIMAX_IC_HX83192 + tristate "HIMAX chipset HX83192 function" + depends on TOUCHSCREEN_HIMAX_INCELL + help + This enables support for HIMAX CHIPSET of HX83192. + +config TOUCHSCREEN_HIMAX_IC_HX83191 + tristate "HIMAX chipset HX83191 function" + depends on TOUCHSCREEN_HIMAX_INCELL + help + This enables support for HIMAX CHIPSET of HX83191. + +# ***************** In-cell End ******************* + +config TOUCHSCREEN_HIMAX_DEBUG + bool "HIMAX debug function" + depends on TOUCHSCREEN_HIMAX_INCELL + help + This enables support for HIMAX debug function. +config TOUCHSCREEN_HIMAX_INSPECT + bool "HIMAX inspect function" + depends on TOUCHSCREEN_HIMAX_INCELL + help + This enables support for HIMAX debug function. + +config TOUCHSCREEN_HIMAX_EMBEDDED_FIRMWARE + bool "HIMAX embedded firmware function" + depends on TOUCHSCREEN_HIMAX_INCELL + help + This enables built-in FW inside kernel as binary array + +endif diff --git a/drivers/input/touchscreen/hxchipset/Makefile b/drivers/input/touchscreen/hxchipset/Makefile new file mode 100644 index 000000000000..12875debfa7d --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/Makefile @@ -0,0 +1,98 @@ +#SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Himax touchscreen drivers. +# +ifneq ($(filter y, $(CONFIG_KALLSYMS_ALL)),) + ccflags-y += -D__KERNEL_KALLSYMS_ALL_ENABLED__ +endif + +ifneq ($(filter y, $(CONFIG_FB)),) + ccflags-y += -DHX_CONFIG_FB +endif + +ifneq ($(filter y, $(CONFIG_DRM)),) + #ccflags-y += -DHX_CONFIG_DRM +endif + +ifneq ($(filter y m, $(CONFIG_TOUCHSCREEN_HIMAX_DEBUG)),) + himax_mmi-objs += himax_debug.o +endif + +ifneq ($(filter y m, $(CONFIG_TOUCHSCREEN_HIMAX_INSPECT)),) + himax_mmi-objs += himax_inspection.o +endif + +ifneq ($(filter y m, $(CONFIG_TOUCHSCREEN_HIMAX_INCELL)),) + himax_mmi-objs += himax_ic_incell_core.o +endif + +ifneq ($(filter y m, $(CONFIG_TOUCHSCREEN_HIMAX_IC_HX83193)),) + ccflags-y += -D__HIMAX_HX83193_MOD__ + ifneq ($(filter m, $(CONFIG_TOUCHSCREEN_HIMAX_COMMON)),) + obj-m += himax_ic_HX83193.o + endif + ifneq ($(filter y, $(CONFIG_TOUCHSCREEN_HIMAX_COMMON)),) + obj-y += himax_ic_HX83193.o + endif +endif + +ifneq ($(filter y m, $(CONFIG_TOUCHSCREEN_HIMAX_IC_HX83192)),) + ccflags-y += -D__HIMAX_HX83192_MOD__ + ifneq ($(filter m, $(CONFIG_TOUCHSCREEN_HIMAX_COMMON)),) + himax_mmi-objs += himax_ic_HX83192.o + endif + ifneq ($(filter y, $(CONFIG_TOUCHSCREEN_HIMAX_COMMON)),) + obj-y += himax_ic_HX83192.o + endif +endif + +ifneq ($(filter y m, $(CONFIG_TOUCHSCREEN_HIMAX_IC_HX83191)),) + ccflags-y += -D__HIMAX_HX83191_MOD__ + ifneq ($(filter m, $(CONFIG_TOUCHSCREEN_HIMAX_COMMON)),) + obj-m += himax_ic_HX83191.o + endif + ifneq ($(filter y, $(CONFIG_TOUCHSCREEN_HIMAX_COMMON)),) + obj-y += himax_ic_HX83191.o + endif +endif + +ifneq ($(filter m, $(CONFIG_TOUCHSCREEN_HIMAX_COMMON)),) + ccflags-y += -D__HIMAX_MOD__ + himax_mmi-objs += himax_common.o + himax_mmi-objs += himax_platform.o +ifneq ($(filter y, $(CONFIG_TOUCHSCREEN_HIMAX_EMBEDDED_FIRMWARE)),) + himax_mmi-objs += Himax_firmware.o +endif + obj-m += himax_mmi.o +endif +ifneq ($(filter y, $(CONFIG_TOUCHSCREEN_HIMAX_COMMON)),) + himax_mmi-objs += himax_common.o + himax_mmi-objs += himax_platform.o +ifneq ($(filter y, $(CONFIG_TOUCHSCREEN_HIMAX_EMBEDDED_FIRMWARE)),) + himax_mmi-objs += Himax_firmware.o +endif + obj-y += himax_mmi.o +endif + +ifneq ($(filter y, $(CONFIG_TOUCHSCREEN_HIMAX_EMBEDDED_FIRMWARE)),) +ccflags-y += -D__EMBEDDED_FW__ + +ld_array_start_str = _binary_$(srctree)/$(src)_Himax_firmware_bin_start +ld_array_start_sym = $(subst -,_,$(subst .,_,$(subst /,_,$(ld_array_start_str)))) +obj_array_start_sym = _binary___Himax_firmware_bin_start + +ld_array_size_str = _binary_$(srctree)/$(src)_Himax_firmware_bin_size +ld_array_size_sym = $(subst -,_,$(subst .,_,$(subst /,_,$(ld_array_size_str)))) +obj_array_size_sym = _binary___Himax_firmware_bin_size + +ld_array_end_str = _binary_$(srctree)/$(src)_Himax_firmware_bin_end +ld_array_end_sym = $(subst -,_,$(subst .,_,$(subst /,_,$(ld_array_end_str)))) +obj_array_end_sym = _binary___Himax_firmware_bin_end + +$(src)/Himax_firmware.o: $(src)/Himax_firmware.bin FORCE + $(LD) $(LDFLAGS) -r -b binary $(srctree)/$(src)/Himax_firmware.bin -o $(objtree)/$(obj)/Himax_firmware.o + $(OBJCOPY) --redefine-sym $(ld_array_start_sym)=$(obj_array_start_sym) $(objtree)/$(obj)/Himax_firmware.o + $(OBJCOPY) --redefine-sym $(ld_array_size_sym)=$(obj_array_size_sym) $(objtree)/$(obj)/Himax_firmware.o + $(OBJCOPY) --redefine-sym $(ld_array_end_sym)=$(obj_array_end_sym) $(objtree)/$(obj)/Himax_firmware.o + +endif diff --git a/drivers/input/touchscreen/hxchipset/himax.h b/drivers/input/touchscreen/hxchipset/himax.h new file mode 100644 index 000000000000..00ce435610e8 --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/himax.h @@ -0,0 +1,431 @@ +/* Himax Android Driver Sample Code for common functions + * + * Copyright (C) 2021 Himax Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef HIMAX_H +#define HIMAX_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_OF) +#include +#endif + +#define TRC_EOL "\n" +#define TRC_EOL_LENGTH sizeof(TRC_EOL) +#define TRC_LINE_LENGTH 68 +#define TRC_TEXT_LENGTH (TRC_LINE_LENGTH - TRC_EOL_LENGTH) /* reserve space */ +#define TRC_NUM_LINES 5000 + +struct himax_ts_data; + +struct himax_ic_data { + int vendor_arch_ver; + int vendor_config_ver; + int vendor_touch_cfg_ver; + int vendor_display_cfg_ver; + int vendor_cid_maj_ver; + int vendor_cid_min_ver; + int vendor_panel_ver; + int vendor_sensor_id; + int ic_adc_num; + uint8_t vendor_config_date[12]; + uint8_t vendor_cus_info[12]; + uint8_t vendor_proj_info[12]; + uint8_t vendor_ic_id[13]; + int HX_RX_NUM; + int HX_TX_NUM; + int HX_BT_NUM; + int HX_X_RES; + int HX_Y_RES; + int HX_MAX_PT; + bool HX_XY_REVERSE; + bool HX_INT_IS_EDGE; + bool HX_IS_ID_EN; + int HX_RX_NUM_2; + int HX_TX_NUM_2; +}; + +struct himax_report_data { + int touch_all_size; + int raw_cnt_max; + int raw_cnt_rmd; + int touch_info_size; + uint8_t finger_num; + uint8_t finger_on; + uint8_t *hx_coord_buf; + uint8_t hx_state_info[2]; + int rawdata_size; + uint8_t diag_cmd; + uint8_t *hx_rawdata_buf; + uint8_t rawdata_frame_size; +}; + +struct himax_core_fp { + void (*fp_burst_enable)(struct himax_ts_data *ts, uint8_t auto_add_4_byte); + void (*fp_interface_on)(struct himax_ts_data *ts); + void (*fp_sense_on)(struct himax_ts_data *ts, uint8_t FlashMode); + bool (*fp_sense_off)(struct himax_ts_data *ts, bool check_en); + bool (*fp_wait_wip)(struct himax_ts_data *ts, int Timing); + void (*fp_init_psl)(struct himax_ts_data *ts); + void (*fp_resume_ic_action)(struct himax_ts_data *ts); + void (*fp_suspend_ic_action)(struct himax_ts_data *ts); + void (*fp_power_on_init)(struct himax_ts_data *ts); + bool (*fp_dd_clk_set)(struct himax_ts_data *ts, bool enable); + void (*fp_dd_reg_en)(struct himax_ts_data *ts, bool enable); + bool (*fp_dd_reg_write)(struct himax_ts_data *ts, uint8_t addr, uint8_t pa_num, int len, + uint8_t *data, uint8_t bank); + bool (*fp_dd_reg_read)(struct himax_ts_data *ts, uint8_t addr, uint8_t pa_num, int len, + uint8_t *data, uint8_t bank); + bool (*fp_ic_id_read)(struct himax_ts_data *ts); + /* CORE_FW */ + void (*fp_parse_raw_data)(struct himax_ts_data *ts, struct himax_report_data *hx_touch_data, + int mul_num, int self_num, uint8_t diag_cmd, int16_t *mutual_data, + int16_t *self_data); + void (*fp_system_reset)(struct himax_ts_data *ts); + int (*fp_Calculate_CRC_with_AP)(struct himax_ts_data *ts, unsigned char *FW_content, + int CRC_from_FW, int len); + uint32_t (*fp_check_CRC)(struct himax_ts_data *ts, uint8_t *start_addr, int reload_length); + void (*fp_diag_register_set)(struct himax_ts_data *ts, uint8_t diag_command, + uint8_t storage_type, bool is_dirly); + void (*fp_control_reK)(struct himax_ts_data *ts, bool enable); + int (*fp_chip_self_test)(struct himax_ts_data *ts, struct seq_file *s, void *v); + void (*fp_reload_disable)(struct himax_ts_data *ts, int disable); + int (*fp_read_ic_trigger_type)(struct himax_ts_data *ts); + void (*fp_read_FW_ver)(struct himax_ts_data *ts); + bool (*fp_read_event_stack)(struct himax_ts_data *ts, uint8_t *buf, uint8_t length); + void (*fp_return_event_stack)(struct himax_ts_data *ts); + bool (*fp_calculateChecksum)(struct himax_ts_data *ts, bool change_iref, uint32_t size); + void (*fp_read_FW_status)(struct himax_ts_data *ts); + void (*fp_irq_switch)(struct himax_ts_data *ts, int switch_on); + int (*fp_assign_sorting_mode)(struct himax_ts_data *ts, uint8_t *tmp_data); + int (*fp_check_sorting_mode)(struct himax_ts_data *ts, uint8_t *tmp_data); + int (*fp_get_max_dc)(struct himax_ts_data *ts); + /* CORE_FW */ + + /* CORE_FLASH */ + void (*fp_chip_erase)(struct himax_ts_data *ts); + bool (*fp_block_erase)(struct himax_ts_data *ts, int start_addr, int length); + bool (*fp_sector_erase)(struct himax_ts_data *ts, int start_addr, int length); + void (*fp_flash_programming)(struct himax_ts_data *ts, uint8_t *FW_content, int start_addr, + int FW_Size); + void (*fp_flash_page_write)(struct himax_ts_data *ts, uint8_t *write_addr, int length, + uint8_t *write_data); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_64k)(struct himax_ts_data *ts, unsigned char *fw, + int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_128k)(struct himax_ts_data *ts, unsigned char *fw, + int len, bool change_iref); + void (*fp_flash_dump_func)(struct himax_ts_data *ts, uint8_t local_flash_command, + int Flash_Size, uint8_t *flash_buffer); + bool (*fp_flash_lastdata_check)(struct himax_ts_data *ts, uint32_t size); + bool (*fp_bin_desc_get)(struct himax_ts_data *ts, unsigned char *fw, uint32_t max_sz); + void (*fp_flash_read)(struct himax_ts_data *ts, uint8_t *r_data, int start_addr, + int length); + /* CORE_FLASH */ + + /* CORE_SRAM */ + bool (*fp_get_DSRAM_data)(struct himax_ts_data *ts, uint8_t *info_data, bool DSRAM_Flag); + /* CORE_SRAM */ + + /* CORE_DRIVER */ + void (*fp_chip_init)(struct himax_ts_data *ts); + int (*fp_fw_ver_bin)(struct himax_ts_data *ts); + void (*fp_pin_reset)(struct himax_ts_data *ts); + void (*fp_touch_information)(struct himax_ts_data *ts); + void (*fp_reload_config)(struct himax_ts_data *ts); + int (*fp_get_touch_data_size)(struct himax_ts_data *ts); + int (*fp_cal_data_len)(struct himax_ts_data *ts, int raw_cnt_rmd, int HX_MAX_PT, + int raw_cnt_max); + bool (*fp_diag_check_sum)(struct himax_ts_data *ts, + struct himax_report_data *hx_touch_data); + void (*fp_diag_parse_raw_data)(struct himax_ts_data *ts, + struct himax_report_data *hx_touch_data, int mul_num, + int self_num, uint8_t diag_cmd, int32_t *mutual_data, + int32_t *self_data); + void (*fp_ic_reset)(struct himax_ts_data *ts, uint8_t loadconfig, uint8_t int_off); +}; + +struct himax_virtual_key { + int index; + int keycode; + int x_range_min; + int x_range_max; + int y_range_min; + int y_range_max; +}; + +struct trc_line { + u8 read_offset; + char text[TRC_LINE_LENGTH]; +}; + +struct trc_lines { + bool enabled; + struct mutex mutex; + int fill_status; + int read_idx; + int write_idx; + struct trc_line lines[TRC_NUM_LINES]; +}; + +struct himax_debug_ops { + void (*fp_ts_dbg_func)(struct himax_ts_data *ts, int start); + int (*fp_set_diag_cmd)(struct himax_ts_data *ts, struct himax_ic_data *ic_data, + struct himax_report_data *hx_touch_data); +}; + +struct himax_debug_procfs { + struct proc_dir_entry *proc_dir; + struct proc_dir_entry *self_test; + struct proc_dir_entry *wpbplock_node; + struct proc_dir_entry *fail_det; + struct proc_dir_entry *inspect_mode; + + struct proc_dir_entry *diag_dir; + struct proc_dir_entry *stack; + struct proc_dir_entry *delta; + struct proc_dir_entry *dc; + struct proc_dir_entry *baseline; + + struct proc_dir_entry *vendor; + struct proc_dir_entry *debug; + struct proc_dir_entry *flash_dump; +}; + +#define BUF_SIZE 1024 + +struct himax_debug { + struct himax_debug_ops ops; + bool flash_dump_going; + uint8_t diag_arr_num; + int max_mutual; + int min_mutual; + int max_self; + int min_self; + uint8_t byte_length; + uint8_t reg_cmd[4]; + struct himax_debug_procfs procfs; + uint8_t cfg_flag; + bool dsram_flag; + bool is_2t2r; + int hx_rx_num_2; + int hx_tx_num_2; + uint32_t *diag_mutual_2; + int32_t *diag_mutual; + int32_t *diag_mutual_new; + int32_t *diag_mutual_old; + uint8_t diag_max_cnt; + uint8_t hx_state_info[2]; + uint8_t diag_coor[128]; + int32_t *diag_self; + int32_t *diag_self_new; + int32_t *diag_self_old; + uint8_t *gma_buf; + bool fw_update_complete; + bool fw_update_going; + int handshaking_result; + unsigned char debug_level_cmd; + int flash_size; + uint8_t *flash_buffer; + uint8_t flash_cmd; + uint8_t flash_progress; + bool flash_dump_rst; /*Fail = 0, Pass = 1*/ + char buf_tmp[BUF_SIZE]; + uint8_t *reg_read_data; + struct timespec64 time_start; + struct timespec64 time_end; + struct timespec64 time_delta; + uint8_t process_type; + uint8_t mode_flag; + uint8_t h_overflow; +}; + +struct himax_ic_incell { + struct fw_operation *pfw_op; + struct flash_operation *pflash_op; +}; + +struct himax_target_report_data { + int *x; + int *y; + int *w; + int *finger_id; + int finger_on; + int finger_num; + int ig_count; +}; + +struct himax_ts_data { + bool initialized; + bool suspended; + atomic_t suspend_mode; + uint8_t x_channel; + uint8_t y_channel; + uint8_t useScreenRes; + uint8_t diag_cmd; + char chip_name[30]; + uint8_t chip_cell_type; + uint8_t protocol_type; + uint8_t first_pressed; + uint8_t coord_data_size; + uint8_t area_data_size; + uint8_t coordInfoSize; + uint8_t raw_data_frame_size; + uint8_t nFinger_support; + uint8_t irq_enabled; + uint8_t diag_self[50]; + uint16_t finger_pressed; + uint16_t last_slot; + uint16_t pre_finger_mask; + uint16_t old_finger; + int hx_point_num; + uint32_t debug_log_level; + uint32_t widthFactor; + uint32_t heightFactor; + int lcm_gpio; + int rst_gpio; + int use_irq; + int (*power)(int on); + int pre_finger_data[10][2]; + struct device *dev; + struct extcon_dev *edev; + struct workqueue_struct *himax_wq; + struct work_struct work; + struct input_dev *input_dev; + struct hrtimer timer; + struct i2c_client *client; + struct himax_i2c_platform_data *pdata; + struct himax_virtual_key *button; + struct mutex rw_lock; + atomic_t irq_state; + spinlock_t irq_lock; + /******* SPI-start *******/ + struct spi_device *spi; + int hx_irq; + uint8_t *xfer_buff; + /******* SPI-end *******/ + int in_self_test; + int suspend_resume_done; + int bus_speed; + int touch_num; + + struct notifier_block fb_notif; + struct workqueue_struct *himax_att_wq; + struct delayed_work work_att; + + struct workqueue_struct *flash_wq; + struct work_struct flash_work; + + struct workqueue_struct *himax_boot_upgrade_wq; + struct delayed_work work_boot_upgrade; + + struct workqueue_struct *ts_int_workqueue; + struct delayed_work ts_int_work; + + struct workqueue_struct *himax_diag_wq; + struct delayed_work himax_diag_delay_wrok; + struct trc_lines trc_lines; + struct notifier_block cable_hotplug_nb; + bool probe_flag; + struct work_struct probe_work; + struct work_struct connected_work; + struct work_struct disconnected_work; + + struct himax_debug debug; + struct himax_ic_incell ic_incell; + struct himax_core_fp core_fp; + struct himax_core_command_operation *core_cmd_op; + struct sram_operation *psram_op; + uint8_t *internal_buffer; + + /* inspection */ + int gap_vertical_partial; + int *gap_vertical_part; + int gap_horizontal_partial; + int *gap_horizontal_part; + int dc_max; + int one_kind_raw_size; + uint32_t rslt_data_len; + int **inspection_criteria; + int *inspt_crtra_flag; + int *test_item_flag; + int do_lpwg_test; + int hx_criteria_item; + int hx_criteria_size; + char *rslt_data; + bool file_w_flag; + char file_path[256]; + char rslt_log[256]; + char start_log[512]; + + struct himax_ic_data *ic_data; + unsigned char ic_checksum; + unsigned long fw_ver_maj_flash_addr; + unsigned long fw_ver_min_flash_addr; + unsigned long cfg_ver_maj_flash_addr; + unsigned long cfg_ver_min_flash_addr; + unsigned long cid_ver_maj_flash_addr; + unsigned long cid_ver_min_flash_addr; + uint32_t cfg_table_flash_addr; + int fw_ver; + int cfg_ver; + int cid_maj; /*GUEST ID*/ + int cid_min; /*VER for GUEST*/ + const struct firmware *hxfw; + bool boot_upgrade_flag; + uint32_t hx_chip_inited; + struct himax_report_data *hx_touch_data; + int hx_ic_amount; + + /*ts_work about start*/ + struct himax_target_report_data *target_report_data; + /*ts_work about end*/ + int hx_touch_info_point_cnt; + bool chip_test_r_flag; + u8 hx_hw_reset_activate; + uint8_t aa_press; + uint8_t en_noisefilter; + uint8_t last_en_noisefilter; + int p_point_num; + int ts_dbg; + uint8_t hx_proc_send_flag; + uint8_t inspect_mode_flag; + + u8 *rw_buf; + int hx_fail_det; + + const char *location; + struct list_head chips; +}; + +#endif diff --git a/drivers/input/touchscreen/hxchipset/himax_common.c b/drivers/input/touchscreen/hxchipset/himax_common.c new file mode 100644 index 000000000000..d041c22335dc --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/himax_common.c @@ -0,0 +1,2283 @@ +/* Himax Android Driver Sample Code for common functions + * + * Copyright (C) 2021 Himax Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/*#include "himax_common.h"*/ +/*#include "himax_ic_core.h"*/ +#include "himax.h" +#include "himax_inspection.h" + +#define SUPPORT_FINGER_DATA_CHECKSUM 0x0F +#define TS_WAKE_LOCK_TIMEOUT (5000) +#define FRAME_COUNT 5 + +#if defined(__EMBEDDED_FW__) +struct firmware g_embedded_fw = { + .data = _binary___Himax_firmware_bin_start, +}; +#endif + +#define HIMAX_PROC_TOUCH_FOLDER "android_touch" +#define HIMAX_PROC_SELF_TEST_FILE "self_test" +#define HIMAX_PROC_WP_BP_LOCK_FILE "WPBPlock_node" +#define HIMAX_PROC_FAIL_DET_FILE "Faildet" + +#if defined(HX_TP_INSPECT_MODE) +#define HIMAX_PROC_INSPECT_MODE_FILE "Inspect_mode" +#endif + +static int himax_chip_num; + +static ssize_t himax_self_test(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + size_t ret = 0; + + I("%s: enter, %d\n", __func__, __LINE__); + + if (ts->suspended == 1) { + E("%s: please do self test in normal active mode\n", __func__); + return HX_INIT_FAIL; + } + himax_int_enable(ts, 0); /* disable irq */ + + ts->in_self_test = 1; + ts->core_fp.fp_chip_self_test(ts, s, v); + ts->in_self_test = 0; + + himax_int_enable(ts, 1); + + return ret; +} + +static void *himax_self_test_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + return (void *)((unsigned long)*pos + 1); +} + +static void *himax_self_test_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_self_test_seq_stop(struct seq_file *s, void *v) +{ +} + +static int himax_self_test_seq_read(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + size_t ret = 0; + + if (ts->chip_test_r_flag) { +#if defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT) + if (ts->rslt_data) + seq_printf(s, "%s", ts->rslt_data); + else +#endif + seq_puts(s, "No chip test data.\n"); + } else { + himax_self_test(s, v); + } + + return ret; +} + +static const struct seq_operations himax_self_test_seq_ops = { + .start = himax_self_test_seq_start, + .next = himax_self_test_seq_next, + .stop = himax_self_test_seq_stop, + .show = himax_self_test_seq_read, +}; + +static int himax_self_test_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_self_test_seq_ops); +}; + +static ssize_t himax_self_test_write(struct file *filp, const char __user *buff, size_t len, + loff_t *data) +{ + struct himax_ts_data *ts = pde_data(file_inode(filp)); + char buf[80] = { 0 }; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if (buf[0] == 'r') { + ts->chip_test_r_flag = true; + I("%s: Start to read chip test data.\n", __func__); + } else { + ts->chip_test_r_flag = false; + I("%s: Back to do self test.\n", __func__); + } + + return len; +} + +static void *himax_WPBPlock_node_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + return (void *)((unsigned long)*pos + 1); +} + +static void *himax_WPBPlock_node_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_WPBPlock_node_seq_stop(struct seq_file *s, void *v) +{ +} + +static int himax_WPBPlock_node_seq_read(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + size_t ret = 0; + uint8_t status; + + status = himax_mcu_WP_BP_status(ts); + + if (status == 0x9C) { + seq_printf(s, "WP BP lock status is lock with value %x.\n", status); + } else { + seq_printf(s, "WP BP lock status is unlock with value %x.\n", status); + } + + return ret; +} + +static const struct seq_operations himax_WPBPlock_node_seq_ops = { + .start = himax_WPBPlock_node_seq_start, + .next = himax_WPBPlock_node_seq_next, + .stop = himax_WPBPlock_node_seq_stop, + .show = himax_WPBPlock_node_seq_read, +}; + +static int himax_WPBPlock_node_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_WPBPlock_node_seq_ops); +}; + +static ssize_t himax_WPBPlock_node_write(struct file *filp, const char __user *buff, size_t len, + loff_t *data) +{ + struct himax_ts_data *ts = pde_data(file_inode(filp)); + char buf[80] = { 0 }; + int result = 0; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if ((buf[0] == 'd') || (buf[0] == 'D')) { + I("%s: Start to disable BP lock.\n", __func__); + result = himax_mcu_WP_BP_disable(ts); + I("%s: himax_mcu_WP_BP_disable return :%d.\n", __func__, result); + + } else if ((buf[0] == 'e') || (buf[0] == 'E')) { + I("%s: Start to enable BP lock.\n", __func__); + result = himax_mcu_WP_BP_enable(ts); + I("%s: himax_mcu_WP_BP_enable return :%d.\n", __func__, result); + } else { + I("%s: Input cmd is incorrect!\n", __func__); + } + + return len; +} + +static void *himax_fail_det_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + return (void *)((unsigned long)*pos + 1); +} + +static void *himax_fail_det_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_fail_det_seq_stop(struct seq_file *s, void *v) +{ +} + +static int himax_fail_det_seq_read(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + size_t ret = 0; + uint8_t data[24] = { 0 }; + + ts->core_fp.fp_dd_clk_set(ts, true); + ts->core_fp.fp_dd_reg_en(ts, true); + + ts->core_fp.fp_dd_reg_read(ts, 0xE5, 0, 8, data, 0); + I("%s E5_Bank0: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n", + __func__, data[0], data[1], data[2], data[3]); + I("%s E5_Bank0: para[4]=0x%2.2X,para[5]=0x%2.2X,para[6]=0x%2.2X, para[7]=0x%2.2X\n", + __func__, data[4], data[5], data[6], data[7]); + seq_printf(s, + "E5_Bank0: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n", + data[0], data[1], data[2], data[3]); + seq_printf(s, + "E5_Bank0: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n", + data[4], data[5], data[6], data[7]); + + ts->core_fp.fp_dd_reg_read(ts, 0xE5, 0, 8, data, 1); + I("%s E5_Bank1: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n", + __func__, data[0], data[1], data[2], data[3]); + I("%s E5_Bank1: para[4]=0x%2.2X,para[5]=0x%2.2X,para[6]=0x%2.2X, para[7]=0x%2.2X\n", + __func__, data[4], data[5], data[6], data[7]); + + seq_printf(s, + "E5_Bank1: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n", + data[0], data[1], data[2], data[3]); + seq_printf(s, + "E5_Bank1: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n", + data[4], data[5], data[6], data[7]); + + ts->core_fp.fp_dd_clk_set(ts, false); + + return ret; +} + +static const struct seq_operations himax_fail_det_seq_ops = { + .start = himax_fail_det_seq_start, + .next = himax_fail_det_seq_next, + .stop = himax_fail_det_seq_stop, + .show = himax_fail_det_seq_read, +}; + +static int himax_fail_det_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_fail_det_seq_ops); +}; + +static ssize_t himax_fail_det_write(struct file *filp, const char __user *buff, size_t len, + loff_t *data) +{ + /*not implement write function*/ + return len; +} + +static const struct proc_ops himax_proc_self_test_ops = { + .proc_open = himax_self_test_proc_open, + .proc_read = seq_read, + .proc_write = himax_self_test_write, + .proc_release = seq_release, +}; + +static const struct proc_ops himax_proc_WPBPlock_node_ops = { + .proc_open = himax_WPBPlock_node_proc_open, + .proc_read = seq_read, + .proc_write = himax_WPBPlock_node_write, + .proc_release = seq_release, +}; + +static const struct proc_ops himax_proc_fail_det_ops = { + .proc_open = himax_fail_det_proc_open, + .proc_read = seq_read, + .proc_write = himax_fail_det_write, + .proc_release = seq_release, +}; + +#if defined(HX_TP_INSPECT_MODE) +static void himax_bank_search_set(struct himax_ts_data *ts, uint16_t Nframe, uint8_t checktype) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + /*skip frame 0x100070F4*/ + himax_parse_assign_cmd(addr_skip_frame, tmp_addr, sizeof(tmp_addr)); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + + switch (checktype) { + case HX_ACT_IDLE_RAWDATA: + case HX_ACT_IDLE_BPN_RAWDATA: + case HX_ACT_IDLE_NOISE: + tmp_data[0] = BS_ACT_IDLE; + break; + case HX_LP_RAWDATA: + case HX_LP_BPN_RAWDATA: + case HX_LP_ABS_NOISE: + case HX_LP_WT_NOISE: + tmp_data[0] = BS_LPWUG; + break; + case HX_LP_IDLE_RAWDATA: + case HX_LP_IDLE_BPN_RAWDATA: + case HX_LP_IDLE_NOISE: + tmp_data[0] = BS_LP_dile; + break; + case HX_RAWDATA: + case HX_BPN_RAWDATA: + case HX_SC: + tmp_data[0] = BS_RAWDATA; + break; + case HX_WT_NOISE: + case HX_ABS_NOISE: + tmp_data[0] = BS_NOISE; + break; + default: + tmp_data[0] = BS_OPENSHORT; + break; + } + tmp_data[0] = 1; + himax_mcu_register_write(ts, tmp_addr, 4, tmp_data, 0); +} +static int himax_chip_inspect_mode(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + char buf[64] = { 0 }; + uint32_t cnt = 0; + bool is_done = false; + int16_t Low_bound; + int16_t low_threshold; + + /* + * A low threshold value 0xff00 is assign defaultly, for int16_t + * type, 0xff00 equal to -256(2^16- 0xff00). So set the low threshold + * as -256 directly. + */ + low_threshold = -256; + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x74; + tmp_addr[0] = 0x54; + + switch (ts->inspect_mode_flag) { + case 0x01: /*Start to do Short_test*/ + tmp_data[3] = 0x5A; + tmp_data[2] = 0x00; + tmp_data[1] = 0x01; + tmp_data[0] = 0xA5; + seq_printf(s, "Item : Short_test.\n"); + break; + case 0x02: /*Start to do Open_test*/ + tmp_data[3] = 0x5A; + tmp_data[2] = 0x00; + tmp_data[1] = 0x02; + tmp_data[0] = 0xA4; + seq_printf(s, "Item : Open_test.\n"); + break; + case 0x08: /*Start to do Noise_test*/ + tmp_data[3] = 0x5A; + tmp_data[2] = 0x00; + tmp_data[1] = 0x08; + tmp_data[0] = 0x9E; + seq_printf(s, "Item : Noise_test. \n"); + break; + case 0x0B: /*Start to do All_test*/ + tmp_data[3] = 0x5A; + tmp_data[2] = 0x00; + tmp_data[1] = 0x0B; + tmp_data[0] = 0x9B; + seq_printf(s, "Item : All_test.\n"); + break; + default: + seq_printf(s, "Input cmd is incorrect.\n"); + return false; + } + + ts->core_fp.fp_sense_off(ts, true); + if (ts->core_fp.fp_reload_disable != NULL) + ts->core_fp.fp_reload_disable(ts, 1); + himax_bank_search_set(ts, 1, 1); + ts->core_fp.fp_sense_on(ts, 0x01); + msleep(20); + + himax_mcu_register_write(ts, tmp_addr, DATA_LEN_4, tmp_data, 0); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, 0); + I("Now register =0x%02X, 0x%02X, 0x%02X, 0x%02X\n", tmp_data[3], tmp_data[2], tmp_data[1], + tmp_data[0]); + + while (cnt++ < 8000) { + ts->core_fp.fp_read_event_stack(ts, buf, 64); + I("%s : %s = 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", __func__, "Waiting for test", + buf[0], buf[1], buf[2], buf[3]); + if ((buf[0] == 0x20) && (buf[1] == 0x19)) { + is_done = true; + break; + } + msleep(1); + } + + if (is_done) { + if (buf[3] == 0x01) { + switch (ts->inspect_mode_flag) { + case 0x01: /*Start to do Short_test*/ + seq_printf(s, "Short Test : %s \n", + ((buf[7] & 0x01) == 0x01) ? "Fail" : "Pass"); + Low_bound = ((buf[14] << 8) + buf[15]); + Low_bound = (Low_bound > low_threshold) ? + (low_threshold - Low_bound) : Low_bound; + seq_printf(s, "%s = %5d / %5d\n", "Short Max. / Min.", + ((buf[12] << 8) + buf[13]), Low_bound); + break; + case 0x02: /*Start to do Open_test*/ + seq_printf(s, "Open Test : %s \n", + ((buf[7] & 0x02) == 0x02) ? "Fail" : "Pass"); + Low_bound = ((buf[18] << 8) + buf[19]); + Low_bound = (Low_bound > low_threshold) ? + (low_threshold - Low_bound) : Low_bound; + seq_printf(s, "%s = %5d / %5d\n", "Open Max. / Min.", + ((buf[16] << 8) + buf[17]), Low_bound); + break; + case 0x08: /*Start to do Noise_test*/ + seq_printf(s, "Noise Test : %s \n", + ((buf[7] & 0x08) == 0x08) ? "Fail" : "Pass"); + Low_bound = ((buf[26] << 8) + buf[27]); + Low_bound = (Low_bound > low_threshold) ? + (low_threshold - Low_bound) : Low_bound; + seq_printf(s, "%s = %5d / %5d\n", "Noise Max. / Min.", + ((buf[24] << 8) + buf[25]), Low_bound); + // seq_printf(s, "%s = %5d / %5d\n", "Noise Max. / Min.", ((buf[24] << 8) + buf[25]), ((buf[26] << 8) + buf[27])); + break; + case 0x0B: /*Start to do All_test*/ + seq_printf(s, "Short Test : %s \n", + ((buf[7] & 0x01) == 0x01) ? "Fail" : "Pass"); + seq_printf(s, "Open Test : %s \n", + ((buf[7] & 0x02) == 0x02) ? "Fail" : "Pass"); + seq_printf(s, "Noise Test : %s \n", + ((buf[7] & 0x08) == 0x08) ? "Fail" : "Pass"); + Low_bound = ((buf[14] << 8) + buf[15]); + Low_bound = (Low_bound > low_threshold) ? + (low_threshold - Low_bound) : Low_bound; + seq_printf(s, "%s = %5d / %5d\n", "Short Max. / Min.", + ((buf[12] << 8) + buf[13]), Low_bound); + Low_bound = ((buf[18] << 8) + buf[19]); + Low_bound = (Low_bound > low_threshold) ? + (low_threshold - Low_bound) : Low_bound; + seq_printf(s, "%s = %5d / %5d\n", "Open Max. / Min.", + ((buf[16] << 8) + buf[17]), Low_bound); + Low_bound = ((buf[26] << 8) + buf[27]); + Low_bound = (Low_bound > low_threshold) ? + (low_threshold - Low_bound) : Low_bound; + seq_printf(s, "%s = %5d / %5d\n", "Noise Max. / Min.", + ((buf[24] << 8) + buf[25]), Low_bound); + break; + default: + seq_printf(s, "Input cmd is incorrect.\n"); + } + } else if (buf[3] == 0x10) + seq_printf(s, "%s \n", "Self Test command error."); + else if (buf[3] == 0x20) + seq_printf(s, "%s \n", "Self Test command CRC error."); + + I("%s = 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", "Test Result", buf[4], buf[5], buf[6], + buf[7]); + I("%s = 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", "Result Information", buf[8], buf[9], + buf[10], buf[11]); + + } else + seq_printf( + s, + "[ERROR] Inspect Mode has not Completed! Please check if FW support it or not! \n"); + + return true; +} + +static ssize_t himax_inspect_mode(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + + I("%s: enter, %d\n", __func__, __LINE__); + + if (ts->suspended == 1) { + E("%s: please do self test in normal active mode\n", __func__); + return HX_INIT_FAIL; + } + if (ts->inspect_mode_flag == 0x0F) { /*User Call HELP!*/ + seq_printf(s, "\n"); + seq_printf(s, "@ Short Test : %s \n", + "#echo Short > Inspect_mode ; #cat Inspect_mode"); + seq_printf(s, "@ Open Test : %s \n", + "#echo Open > Inspect_mode ; #cat Inspect_mode"); + seq_printf(s, "@ Noise Test : %s \n", + "#echo Noise > Inspect_mode ; #cat Inspect_mode"); + seq_printf(s, "@ All Test : %s \n", + "#echo All > Inspect_mode ; #cat Inspect_mode"); + seq_printf(s, "\n"); + seq_printf(s, "@ Set Inspect_mode Threshold by writing register : \n"); + seq_printf(s, + " Address | Threshold Item | [31:16] | [15:0] \n"); + seq_printf(s, + " 0x100074A0 | Short Test | High Boundary | Low Boundary \n"); + seq_printf(s, + " 0x100074A4 | Open Test | High Boundary | Low Boundary \n"); + seq_printf(s, + " 0x100074AC | Noise Test | High Boundary | Low Boundary \n"); + seq_printf(s, + " 0x100074B0 | Raw data | High Boundary | Low Boundary \n"); + seq_printf(s, "\n"); + seq_printf(s, "Example : To set High Boundary = 0xFF ; Low Boundary = 0x11\n"); + seq_printf(s, "#echo register,w:x100074A4:x00FF0011 > debug \n"); + seq_printf(s, "\n"); + return true; + } + + himax_int_enable(ts, 0); /* disable irq */ + // ts->in_self_test = 1; + himax_chip_inspect_mode(s, v); + // ts->in_self_test = 0; + himax_int_enable(ts, 1); + + return 0; +} + +static void *himax_inspect_mode_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + return (void *)((unsigned long)*pos + 1); +} + +static void *himax_inspect_mode_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_inspect_mode_seq_stop(struct seq_file *s, void *v) +{ +} + +static int himax_inspect_mode_seq_read(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + size_t ret = 0; + + if (ts->chip_test_r_flag) { +#if defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT) + if (ts->rslt_data) + seq_printf(s, "%s", ts->rslt_data); + else +#endif + seq_puts(s, "No chip test data.\n"); + } else { + himax_inspect_mode(s, v); + } + + return ret; +} + +static const struct seq_operations himax_inspect_mode_seq_ops = { + .start = himax_inspect_mode_seq_start, + .next = himax_inspect_mode_seq_next, + .stop = himax_inspect_mode_seq_stop, + .show = himax_inspect_mode_seq_read, +}; + +static int himax_inspect_mode_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_inspect_mode_seq_ops); +}; + +static ssize_t himax_inspect_mode_write(struct file *filp, const char __user *buff, size_t len, + loff_t *data) +{ + struct himax_ts_data *ts = pde_data(file_inode(filp)); + char buf[80] = { 0 }; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if ((buf[0] == 's') || (buf[0] == 'S')) { + ts->inspect_mode_flag = 0x01; + I("%s: Start to do Short_test.\n", __func__); + } else if ((buf[0] == 'o') || (buf[0] == 'O')) { + ts->inspect_mode_flag = 0x02; + I("%s: Start to do Open_test.\n", __func__); + } else if ((buf[0] == 'n') || (buf[0] == 'N')) { + ts->inspect_mode_flag = 0x08; + I("%s: Start to do Noise_test.\n", __func__); + } else if ((buf[0] == 'a') || (buf[0] == 'A')) { + ts->inspect_mode_flag = 0x0B; + I("%s: Start to do All_test.\n", __func__); + } else if (buf[0] == 'h') { + ts->inspect_mode_flag = 0x0F; + I("%s: User Call HELP! Lest's assist user to operate.\n", __func__); + } else { + I("%s: Input cmd is incorrect!\n", __func__); + } + + return len; +} + +static const struct proc_ops himax_proc_inspect_mode_ops = { + //.owner = THIS_MODULE, + .proc_open = himax_inspect_mode_proc_open, + .proc_read = seq_read, + .proc_write = himax_inspect_mode_write, + .proc_release = seq_release, +}; +#endif + +static int himax_common_proc_init(struct himax_ts_data *ts) +{ + const char *dir_name = ts->location ? ts->location : HIMAX_PROC_TOUCH_FOLDER; + + ts->debug.procfs.proc_dir = proc_mkdir(dir_name, NULL); + + if (ts->debug.procfs.proc_dir == NULL) { + E(" %s: himax_touch_proc_dir file create failed!\n", __func__); + goto fail_1; + } +#if defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT) + himax_inspection_init(ts); +#endif + ts->debug.procfs.self_test = + proc_create_data(HIMAX_PROC_SELF_TEST_FILE, 0444, ts->debug.procfs.proc_dir, + &himax_proc_self_test_ops, ts); + if (ts->debug.procfs.self_test == NULL) { + E(" %s: proc self_test file create failed!\n", __func__); + goto fail_2; + } + + ts->debug.procfs.wpbplock_node = + proc_create_data(HIMAX_PROC_WP_BP_LOCK_FILE, 0444, ts->debug.procfs.proc_dir, + &himax_proc_WPBPlock_node_ops, ts); + if (ts->debug.procfs.wpbplock_node == NULL) { + E(" %s: proc BPlock file create failed!\n", __func__); + goto fail_3; + } + + ts->debug.procfs.fail_det = + proc_create_data(HIMAX_PROC_FAIL_DET_FILE, 0444, ts->debug.procfs.proc_dir, + &himax_proc_fail_det_ops, ts); + if (ts->debug.procfs.fail_det == NULL) { + E(" %s: proc fail det file create failed!\n", __func__); + goto fail_4; + } + +#if defined(HX_TP_INSPECT_MODE) + ts->debug.procfs.inspect_mode = + proc_create_data(HIMAX_PROC_INSPECT_MODE_FILE, 0644, ts->debug.procfs.proc_dir, + &himax_proc_inspect_mode_ops, ts); + + if (ts->debug.procfs.inspect_mode == NULL) { + E(" %s: proc INSPECT_MODE file create failed!\n", __func__); + goto fail_5; + } +#endif + return 0; +#if defined(HX_TP_INSPECT_MODE) +fail_5: + remove_proc_entry(HIMAX_PROC_FAIL_DET_FILE, ts->debug.procfs.proc_dir); +#endif +fail_4: + remove_proc_entry(HIMAX_PROC_WP_BP_LOCK_FILE, ts->debug.procfs.proc_dir); +fail_3: + remove_proc_entry(HIMAX_PROC_SELF_TEST_FILE, ts->debug.procfs.proc_dir); +fail_2: + if (ts->location) + remove_proc_entry(ts->location, NULL); + else + remove_proc_entry(HIMAX_PROC_TOUCH_FOLDER, NULL); +fail_1: + return -ENOMEM; +} + +static void himax_common_proc_deinit(struct himax_ts_data *ts) +{ + remove_proc_entry(HIMAX_PROC_SELF_TEST_FILE, ts->debug.procfs.proc_dir); + remove_proc_entry(HIMAX_PROC_WP_BP_LOCK_FILE, ts->debug.procfs.proc_dir); + remove_proc_entry(HIMAX_PROC_FAIL_DET_FILE, ts->debug.procfs.proc_dir); +#if defined(HX_TP_INSPECT_MODE) + remove_proc_entry(HIMAX_PROC_INSPECT_MODE_FILE, ts->debug.procfs.proc_dir); +#endif + if (ts->location) + remove_proc_entry(ts->location, NULL); + else + remove_proc_entry(HIMAX_PROC_TOUCH_FOLDER, NULL); +} + +void himax_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len) +{ + /*I("%s: Entering!\n", __func__);*/ + switch (len) { + case 1: + cmd[0] = addr; + /*I("%s: cmd[0] = 0x%02X\n", __func__, cmd[0]);*/ + break; + case 2: + cmd[0] = addr % 0x100; + cmd[1] = (addr >> 8) % 0x100; + /*I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X\n",*/ + /* __func__, cmd[0], cmd[1]);*/ + break; + case 4: + cmd[0] = addr % 0x100; + cmd[1] = (addr >> 8) % 0x100; + cmd[2] = (addr >> 16) % 0x100; + cmd[3] = addr / 0x1000000; + /* I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X,*/ + /*cmd[2] = 0x%02X,cmd[3] = 0x%02X\n", */ + /* __func__, cmd[0], cmd[1], cmd[2], cmd[3]);*/ + break; + default: + E("%s: input length fault,len = %d!\n", __func__, len); + } +} +EXPORT_SYMBOL(himax_parse_assign_cmd); + +int himax_input_register(struct himax_ts_data *ts) +{ + int ret = 0; + + ret = himax_dev_set(ts); + + if (ret < 0) { + I("%s, input device register fail!\n", __func__); + ret = INPUT_REGISTER_FAIL; + goto input_device_fail; + } + + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_ABS, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); + set_bit(KEY_BACK, ts->input_dev->keybit); + set_bit(KEY_HOME, ts->input_dev->keybit); + set_bit(KEY_MENU, ts->input_dev->keybit); + set_bit(KEY_SEARCH, ts->input_dev->keybit); + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT) + set_bit(KEY_POWER, ts->input_dev->keybit); +#endif + set_bit(BTN_TOUCH, ts->input_dev->keybit); + set_bit(KEY_APPSELECT, ts->input_dev->keybit); + set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); +#if defined(HX_PROTOCOL_A) + /*ts->input_dev->mtsize = ts->nFinger_support;*/ + input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 3, 0, 0); +#else + set_bit(MT_TOOL_FINGER, ts->input_dev->keybit); +#if defined(HX_PROTOCOL_B_3PA) + input_mt_init_slots(ts->input_dev, ts->nFinger_support, INPUT_MT_DIRECT); +#else + input_mt_init_slots(ts->input_dev, ts->nFinger_support); +#endif +#endif + I("input_set_abs_params: mix_x %d, max_x %d, min_y %d, max_y %d\n", ts->pdata->abs_x_min, + ts->pdata->abs_x_max, ts->pdata->abs_y_min, ts->pdata->abs_y_max); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, ts->pdata->abs_x_min, + ts->pdata->abs_x_max, ts->pdata->abs_x_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, ts->pdata->abs_y_min, + ts->pdata->abs_y_max, ts->pdata->abs_y_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, ts->pdata->abs_pressure_min, + ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0); +#if !defined(HX_PROTOCOL_A) + input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, ts->pdata->abs_pressure_min, + ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, ts->pdata->abs_width_min, + ts->pdata->abs_width_max, ts->pdata->abs_pressure_fuzz, 0); +#endif + /* input_set_abs_params(ts->input_dev, ABS_MT_AMPLITUDE, 0,*/ + /* ((ts->pdata->abs_pressure_max << 16)*/ + /* | ts->pdata->abs_width_max),*/ + /* 0, 0);*/ + /* input_set_abs_params(ts->input_dev, ABS_MT_POSITION,*/ + /* 0, (BIT(31)*/ + /* | (ts->pdata->abs_x_max << 16)*/ + /* | ts->pdata->abs_y_max),*/ + /* 0, 0);*/ + + if (himax_input_register_device(ts->input_dev) == 0) { + ret = NO_ERR; + } else { + E("%s: input register fail\n", __func__); + ret = INPUT_REGISTER_FAIL; + goto input_device_fail; + } + + I("%s, input device registered.\n", __func__); + +input_device_fail: + return ret; +} +EXPORT_SYMBOL(himax_input_register); + +static void calcDataSize(struct himax_ts_data *ts_data) +{ + ts_data->x_channel = ts_data->ic_data->HX_RX_NUM; + ts_data->y_channel = ts_data->ic_data->HX_TX_NUM; + ts_data->nFinger_support = ts_data->ic_data->HX_MAX_PT; + + ts_data->coord_data_size = 4 * ts_data->nFinger_support; + ts_data->area_data_size = + ((ts_data->nFinger_support / 4) + (ts_data->nFinger_support % 4 ? 1 : 0)) * 4; + ts_data->coordInfoSize = ts_data->coord_data_size + ts_data->area_data_size + 4; + ts_data->raw_data_frame_size = + 128 - ts_data->coord_data_size - ts_data->area_data_size - 4 - 4 - 1; + + if (ts_data->raw_data_frame_size == 0) { + E("%s: could NOT calculate!\n", __func__); + return; + } + + I("%s: coord_dsz:%d,area_dsz:%d,raw_data_fsz:%d", __func__, ts_data->coord_data_size, + ts_data->area_data_size, ts_data->raw_data_frame_size); +} + +static void calculate_point_number(struct himax_ts_data *ts) +{ + ts->hx_touch_info_point_cnt = ts->ic_data->HX_MAX_PT * 4; + + if ((ts->ic_data->HX_MAX_PT % 4) == 0) + ts->hx_touch_info_point_cnt += (ts->ic_data->HX_MAX_PT / 4) * 4; + else + ts->hx_touch_info_point_cnt += ((ts->ic_data->HX_MAX_PT / 4) + 1) * 4; +} + +#if defined(HX_BOOT_UPGRADE) +/*------------------------------------------------------------------------- +* +* Create: Unknown +* +* Using: Read FW_VER and CFG_VER value from FW file and compare with +* FW/CFG version from MCU. +* +* param: None +* +* Dependency function: himax_mcu_fw_ver_bin +* +*/ +static int himax_auto_update_check(struct himax_ts_data *ts) +{ + int32_t ret; + bool is_testFW = false; + + if ((0x80 & ((uint8_t)(ts->ic_data->vendor_config_ver >> 8))) == 0x80) + is_testFW = true; + + I("%s: Entering!\n", __func__); + if (ts->core_fp.fp_fw_ver_bin(ts) == 0) { + I("%s:ic_data->vendor_arch_ver:%d,g_i_FW_VER:%d,ic_data->vendor_config_ver:%d,g_i_CFG_VER:%d\n", + __func__, ts->ic_data->vendor_arch_ver, ts->fw_ver, + ts->ic_data->vendor_config_ver, ts->cfg_ver); + if (((ts->ic_data->vendor_arch_ver < ts->fw_ver) || + (ts->ic_data->vendor_config_ver < ts->cfg_ver)) && + !is_testFW) { + I("%s: Need update\n", __func__); + ret = 0; + } else { + I("%s: Need not update!\n", __func__); + ret = 1; + } + } else { + E("%s: FW bin fail!\n", __func__); + ret = 1; + } + + return ret; +} + +static int i_get_FW(struct himax_ts_data *ts) +{ + int ret = -1; + int result = NO_ERR; + + I("%s: file name = %s\n", __func__, ts->pdata->fw_name); + ret = request_firmware(&ts->hxfw, ts->pdata->fw_name, ts->dev); + if (ret < 0) { +#if defined(__EMBEDDED_FW__) + ts->hxfw = &g_embedded_fw; + I("%s: Not find FW in userspace, use embedded FW(size:%zu)", __func__, + g_embedded_fw.size); +#else + E("%s,%d: error code = %d\n", __func__, __LINE__, ret); + return OPEN_FILE_FAIL; +#endif + } + + return result; +} + +static int i_update_FW(struct himax_ts_data *ts) +{ + int upgrade_times = 0; + int8_t ret = 0; + int8_t result = 0; + himax_int_enable(ts, 0); + +update_retry: + + if (ts->hxfw->size == FW_SIZE_64k) + ret = ts->core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k( + ts, (unsigned char *)ts->hxfw->data, ts->hxfw->size, false); + else if (ts->hxfw->size == FW_SIZE_128k) + ret = ts->core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k( + ts, (unsigned char *)ts->hxfw->data, ts->hxfw->size, false); + + if (ret == 0) { + upgrade_times++; + E("%s: TP upgrade error, upgrade_times = %d\n", __func__, upgrade_times); + + if (upgrade_times < 3) + goto update_retry; + } else if (ret == -1) { + E("%s: WP BP disable error \n", __func__); + } else { + ts->core_fp.fp_reload_disable(ts, 0); + ts->core_fp.fp_power_on_init(ts); + ts->core_fp.fp_read_FW_ver(ts); + ts->core_fp.fp_touch_information(ts); + /*upgrade success*/ + I("%s: TP upgrade OK\n", __func__); + } + + /* need to be review with FW update flow */ + result = himax_mcu_WP_BP_enable(ts); + + if (result < 0) { + I("%s: WP BP enable fail\n", __func__); + } + + himax_int_enable(ts, 1); + return result; +} +#endif +/* + *static int himax_loadSensorConfig(struct himax_i2c_platform_data *pdata) + *{ + * I("%s: initialization complete\n", __func__); + * return NO_ERR; + *} + */ + +int himax_report_data_init(struct himax_ts_data *ts) +{ + if (ts->hx_touch_data->hx_coord_buf != NULL) { + kfree(ts->hx_touch_data->hx_coord_buf); + ts->hx_touch_data->hx_coord_buf = NULL; + } + if (ts->hx_touch_data->hx_rawdata_buf != NULL) { + kfree(ts->hx_touch_data->hx_rawdata_buf); + ts->hx_touch_data->hx_rawdata_buf = NULL; + } + ts->hx_touch_data->touch_all_size = ts->core_fp.fp_get_touch_data_size(ts); + ts->hx_touch_data->raw_cnt_max = ts->ic_data->HX_MAX_PT / 4; + ts->hx_touch_data->raw_cnt_rmd = ts->ic_data->HX_MAX_PT % 4; + /* more than 4 fingers */ + if (ts->hx_touch_data->raw_cnt_rmd != 0x00) { + ts->hx_touch_data->rawdata_size = + ts->core_fp.fp_cal_data_len(ts, ts->hx_touch_data->raw_cnt_rmd, + ts->ic_data->HX_MAX_PT, + ts->hx_touch_data->raw_cnt_max); + + ts->hx_touch_data->touch_info_size = + (ts->ic_data->HX_MAX_PT + ts->hx_touch_data->raw_cnt_max + 2) * 4; + } else { /* less than 4 fingers */ + ts->hx_touch_data->rawdata_size = + ts->core_fp.fp_cal_data_len(ts, ts->hx_touch_data->raw_cnt_rmd, + ts->ic_data->HX_MAX_PT, + ts->hx_touch_data->raw_cnt_max); + + ts->hx_touch_data->touch_info_size = + (ts->ic_data->HX_MAX_PT + ts->hx_touch_data->raw_cnt_max + 1) * 4; + } + if ((ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM + ts->ic_data->HX_TX_NUM + + ts->ic_data->HX_RX_NUM) % + ts->hx_touch_data->rawdata_size == + 0) + ts->hx_touch_data->rawdata_frame_size = + (ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM + ts->ic_data->HX_TX_NUM + + ts->ic_data->HX_RX_NUM) / + ts->hx_touch_data->rawdata_size; + else + ts->hx_touch_data->rawdata_frame_size = + (ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM + ts->ic_data->HX_TX_NUM + + ts->ic_data->HX_RX_NUM) / + ts->hx_touch_data->rawdata_size + + 1; + + I("%s:rawdata_fsz = %d,HX_MAX_PT:%d,hx_raw_cnt_max:%d\n", __func__, + ts->hx_touch_data->rawdata_frame_size, ts->ic_data->HX_MAX_PT, + ts->hx_touch_data->raw_cnt_max); + I("%s:hx_raw_cnt_rmd:%d,g_hx_rawdata_size:%d,touch_info_size:%d\n", __func__, + ts->hx_touch_data->raw_cnt_rmd, ts->hx_touch_data->rawdata_size, + ts->hx_touch_data->touch_info_size); + + ts->hx_touch_data->hx_coord_buf = + kzalloc(sizeof(uint8_t) * (ts->hx_touch_data->touch_info_size), GFP_KERNEL); + + if (ts->hx_touch_data->hx_coord_buf == NULL) + goto mem_alloc_fail_coord_buf; + + ts->hx_touch_data->hx_rawdata_buf = + kzalloc(sizeof(uint8_t) * (ts->hx_touch_data->touch_all_size - + ts->hx_touch_data->touch_info_size), + GFP_KERNEL); + if (ts->hx_touch_data->hx_rawdata_buf == NULL) + goto mem_alloc_fail_rawdata_buf; + + if (ts->target_report_data == NULL) { + ts->target_report_data = + kzalloc(sizeof(struct himax_target_report_data), GFP_KERNEL); + if (ts->target_report_data == NULL) + goto mem_alloc_fail_report_data; + + ts->target_report_data->x = + kzalloc(sizeof(int) * (ts->ic_data->HX_MAX_PT), GFP_KERNEL); + if (ts->target_report_data->x == NULL) + goto mem_alloc_fail_report_data_x; + + ts->target_report_data->y = + kzalloc(sizeof(int) * (ts->ic_data->HX_MAX_PT), GFP_KERNEL); + if (ts->target_report_data->y == NULL) + goto mem_alloc_fail_report_data_y; + + ts->target_report_data->w = + kzalloc(sizeof(int) * (ts->ic_data->HX_MAX_PT), GFP_KERNEL); + if (ts->target_report_data->w == NULL) + goto mem_alloc_fail_report_data_w; + + ts->target_report_data->finger_id = + kzalloc(sizeof(int) * (ts->ic_data->HX_MAX_PT), GFP_KERNEL); + if (ts->target_report_data->finger_id == NULL) + goto mem_alloc_fail_report_data_fid; + } + + return NO_ERR; + +mem_alloc_fail_report_data_fid: + kfree(ts->target_report_data->w); + ts->target_report_data->w = NULL; +mem_alloc_fail_report_data_w: + kfree(ts->target_report_data->y); + ts->target_report_data->y = NULL; +mem_alloc_fail_report_data_y: + kfree(ts->target_report_data->x); + ts->target_report_data->x = NULL; +mem_alloc_fail_report_data_x: + kfree(ts->target_report_data); + ts->target_report_data = NULL; +mem_alloc_fail_report_data: + kfree(ts->hx_touch_data->hx_rawdata_buf); + ts->hx_touch_data->hx_rawdata_buf = NULL; +mem_alloc_fail_rawdata_buf: + kfree(ts->hx_touch_data->hx_coord_buf); + ts->hx_touch_data->hx_coord_buf = NULL; +mem_alloc_fail_coord_buf: + + E("%s: Failed to allocate memory\n", __func__); + return MEM_ALLOC_FAIL; +} +EXPORT_SYMBOL(himax_report_data_init); + +static void himax_report_data_deinit(struct himax_ts_data *ts) +{ + kfree(ts->target_report_data->finger_id); + ts->target_report_data->finger_id = NULL; + kfree(ts->target_report_data->w); + ts->target_report_data->w = NULL; + kfree(ts->target_report_data->y); + ts->target_report_data->y = NULL; + kfree(ts->target_report_data->x); + ts->target_report_data->x = NULL; + kfree(ts->target_report_data); + ts->target_report_data = NULL; + kfree(ts->hx_touch_data->hx_rawdata_buf); + ts->hx_touch_data->hx_rawdata_buf = NULL; + kfree(ts->hx_touch_data->hx_coord_buf); + ts->hx_touch_data->hx_coord_buf = NULL; +} + +static int himax_ts_work_status(struct himax_ts_data *ts) +{ + /* 1: normal */ + int result = HX_REPORT_COORD; + + ts->hx_touch_data->diag_cmd = ts->diag_cmd; + if (ts->hx_touch_data->diag_cmd) + result = HX_REPORT_COORD_RAWDATA; + + /* I("Now Status is %d\n", result); */ + return result; +} + +static int himax_touch_get(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + if (ts->ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + switch (ts_path) { + /*normal*/ + case HX_REPORT_COORD: + if (ts->hx_hw_reset_activate) { + if (!ts->core_fp.fp_read_event_stack(ts, buf, 128)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + } else { + if (!ts->core_fp.fp_read_event_stack(ts, buf, + ts->hx_touch_data->touch_info_size)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + } + break; + case HX_REPORT_COORD_RAWDATA: + if (!ts->core_fp.fp_read_event_stack(ts, buf, 128)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + break; + default: + break; + } + + return ts_status; +} + +/* start error_control*/ +static int himax_checksum_cal(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + uint16_t check_sum_cal = 0; + int32_t i = 0; + int length = 0; + int zero_cnt = 0; + int raw_data_sel = 0; + int ret_val = ts_status; + + if (ts->ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + /* Normal */ + switch (ts_path) { + case HX_REPORT_COORD: + length = ts->hx_touch_data->touch_info_size; + break; + case HX_REPORT_COORD_RAWDATA: + length = ts->hx_touch_data->touch_info_size; + break; + default: + I("%s, Normal error!\n", __func__); + ret_val = HX_PATH_FAIL; + goto END_FUNCTION; + } + + for (i = 0; i < length; i++) { + check_sum_cal += buf[i]; + if (buf[i] == 0x00) + zero_cnt++; + } + + if (check_sum_cal % 0x100 != 0) { + I("point data_checksum not match check_sum_cal: 0x%02X", check_sum_cal); + ret_val = HX_CHKSUM_FAIL; + } else if (zero_cnt == length) { + if (ts->use_irq) + I("[HIMAX TP MSG] All Zero event\n"); + + ret_val = HX_CHKSUM_FAIL; + } else { + raw_data_sel = buf[ts->hx_touch_info_point_cnt] >> 4 & 0x0F; + /*I("%s:raw_out_sel=%x , hx_touch_data->diag_cmd=%x.\n",*/ + /* __func__, raw_data_sel,*/ + /* ts->hx_touch_data->diag_cmd);*/ + /*raw data out not match skip it*/ + if ((raw_data_sel != 0x0F) && (raw_data_sel != ts->hx_touch_data->diag_cmd)) { + /*I("%s:raw data out not match.\n", __func__);*/ + if (!ts->hx_touch_data->diag_cmd) { + /*Need to clear event stack here*/ + ts->core_fp.fp_read_event_stack( + ts, buf, (128 - ts->hx_touch_data->touch_info_size)); + /*I("%s: size =%d, buf[0]=%x ,buf[1]=%x,*/ + /* buf[2]=%x, buf[3]=%x.\n",*/ + /* __func__,*/ + /* (128-ts->hx_touch_data->touch_info_size),*/ + /* buf[0], buf[1], buf[2], buf[3]);*/ + /*I("%s:also clear event stack.\n", __func__);*/ + } + ret_val = HX_READY_SERVE; + } + } + +END_FUNCTION: + if (ts->ts_dbg != 0) + I("%s: END, ret_val=%d!\n", __func__, ret_val); + return ret_val; +} + +static int himax_err_ctrl(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ +#if defined(HX_RST_PIN_FUNC) + if (ts->hx_hw_reset_activate) { + /* drop 1st interrupts after chip reset */ + ts->hx_hw_reset_activate = 0; + I("[HX_HW_RESET_ACTIVATE]%s:Back from reset,ready to serve.\n", __func__); + ts_status = HX_RST_OK; + goto END_FUNCTION; + } +#endif + + ts_status = himax_checksum_cal(ts, buf, ts_path, ts_status); + +END_FUNCTION: + if (ts->ts_dbg != 0) + I("%s: END, ts_status=%d!\n", __func__, ts_status); + return ts_status; +} +/* end error_control*/ + +/* start distribute_data*/ +static int himax_distribute_touch_data(struct himax_ts_data *ts, uint8_t *buf, int ts_path, + int ts_status) +{ + uint8_t hx_state_info_pos = ts->hx_touch_data->touch_info_size - 3; + + if (ts->ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + if (ts_path == HX_REPORT_COORD) { + memcpy(ts->hx_touch_data->hx_coord_buf, &buf[0], + ts->hx_touch_data->touch_info_size); + + if (buf[hx_state_info_pos] != 0xFF && buf[hx_state_info_pos + 1] != 0xFF) + memcpy(ts->hx_touch_data->hx_state_info, &buf[hx_state_info_pos], 2); + else + memset(ts->hx_touch_data->hx_state_info, 0x00, + sizeof(ts->hx_touch_data->hx_state_info)); + + if ((ts->hx_hw_reset_activate) + + ) { + memcpy(ts->hx_touch_data->hx_rawdata_buf, + &buf[ts->hx_touch_data->touch_info_size], + ts->hx_touch_data->touch_all_size - + ts->hx_touch_data->touch_info_size); + } + } else if (ts_path == HX_REPORT_COORD_RAWDATA) { + memcpy(ts->hx_touch_data->hx_coord_buf, &buf[0], + ts->hx_touch_data->touch_info_size); + + if (buf[hx_state_info_pos] != 0xFF && buf[hx_state_info_pos + 1] != 0xFF) + memcpy(ts->hx_touch_data->hx_state_info, &buf[hx_state_info_pos], 2); + else + memset(ts->hx_touch_data->hx_state_info, 0x00, + sizeof(ts->hx_touch_data->hx_state_info)); + + memcpy(ts->hx_touch_data->hx_rawdata_buf, &buf[ts->hx_touch_data->touch_info_size], + ts->hx_touch_data->touch_all_size - ts->hx_touch_data->touch_info_size); + + } else { + E("%s, Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + } + + if (ts->ts_dbg != 0) + I("%s: End, ts_status=%d!\n", __func__, ts_status); + return ts_status; +} +/* end assign_data*/ + +/* start parse_report_data*/ +static int himax_parse_report_points(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + int x = 0, y = 0, w = 0; + int base = 0; + int event_id = 0; + int32_t loop_i = 0; + + if (ts->ts_dbg != 0) + I("%s: start!\n", __func__); + + ts->old_finger = ts->pre_finger_mask; + if (ts->hx_point_num == 0) { + if (ts->ts_dbg != 0) + I("%s: hx_point_num = 0!\n", __func__); + return ts_status; + } + ts->pre_finger_mask = 0; + ts->hx_touch_data->finger_num = + ts->hx_touch_data->hx_coord_buf[ts->coordInfoSize - 4] & 0x0F; + ts->hx_touch_data->finger_on = 1; + ts->aa_press = 1; + + ts->target_report_data->finger_num = ts->hx_touch_data->finger_num; + ts->target_report_data->finger_on = ts->hx_touch_data->finger_on; + ts->target_report_data->ig_count = ts->hx_touch_data->hx_coord_buf[ts->coordInfoSize - 5]; + + if (ts->ts_dbg != 0) + I("%s:finger_num = 0x%2X, finger_on = %d\n", __func__, + ts->target_report_data->finger_num, ts->target_report_data->finger_on); + + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + base = loop_i * 4; + x = ts->hx_touch_data->hx_coord_buf[base] << 8 | + ts->hx_touch_data->hx_coord_buf[base + 1]; + y = (ts->hx_touch_data->hx_coord_buf[base + 2] << 8 | + ts->hx_touch_data->hx_coord_buf[base + 3]); + + w = ts->hx_touch_data->hx_coord_buf[(ts->nFinger_support * 4) + loop_i]; + + if (ts->ts_dbg != 0) + D("%s: now parsing[%d]:x=%d, y=%d, w=%d\n", __func__, loop_i, x, y, w); + + if (ts->ic_data->HX_IS_ID_EN) { + x = (ts->hx_touch_data->hx_coord_buf[base] & 0x3F) << 8 | + ts->hx_touch_data->hx_coord_buf[base + 1]; + event_id = ts->hx_touch_data->hx_coord_buf[base] >> 0x06; + /*if ((event_id == 0) || (event_id == 3)) {*/ /*No touch event or Leave event*/ + if (event_id == 3) { /*No touch event or Leave event*/ + x = 0xFFFF; + y = 0xFFFF; + } + I("%s: now parsing[%d]:x=%d, y=%d, event_id=%d\n", __func__, loop_i, x, y, + event_id); + } + + if (x >= 0 && x <= ts->pdata->abs_x_max && y >= 0 && y <= ts->pdata->abs_y_max) { + ts->hx_touch_data->finger_num--; + + ts->target_report_data->x[loop_i] = x; + ts->target_report_data->y[loop_i] = y; + ts->target_report_data->w[loop_i] = w; + ts->target_report_data->finger_id[loop_i] = 1; + + /*I("%s: g_target_report_data->x[loop_i]=%d,*/ + /*g_target_report_data->y[loop_i]=%d,*/ + /*g_target_report_data->w[loop_i]=%d",*/ + /*__func__, ts->target_report_data->x[loop_i],*/ + /*ts->target_report_data->y[loop_i],*/ + /*ts->target_report_data->w[loop_i]); */ + + if (!ts->first_pressed) { + ts->first_pressed = 1; + I("S1@%d, %d\n", x, y); + } + + ts->pre_finger_data[loop_i][0] = x; + ts->pre_finger_data[loop_i][1] = y; + + ts->pre_finger_mask = ts->pre_finger_mask + (1 << loop_i); + } else { /* report coordinates */ + ts->target_report_data->x[loop_i] = x; + ts->target_report_data->y[loop_i] = y; + ts->target_report_data->w[loop_i] = w; + ts->target_report_data->finger_id[loop_i] = 0; + + if (loop_i == 0 && ts->first_pressed == 1) { + ts->first_pressed = 2; + I("E1@%d, %d\n", ts->pre_finger_data[0][0], + ts->pre_finger_data[0][1]); + } + } + } + + if (ts->ts_dbg != 0) { + for (loop_i = 0; loop_i < 10; loop_i++) + D("DBG X=%d Y=%d ID=%d\n", ts->target_report_data->x[loop_i], + ts->target_report_data->y[loop_i], + ts->target_report_data->finger_id[loop_i]); + + D("DBG finger number %d\n", ts->target_report_data->finger_num); + } + + if (ts->ts_dbg != 0) + I("%s: end!\n", __func__); + return ts_status; +} + +static int himax_parse_report_data(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + if (ts->ts_dbg != 0) + I("%s: start now_status=%d!\n", __func__, ts_status); + + ts->en_noisefilter = + (ts->hx_touch_data->hx_coord_buf[ts->hx_touch_info_point_cnt + 2] >> 3); + /* I("EN_NoiseFilter=%d\n", ts->en_noisefilter); */ + ts->en_noisefilter = ts->en_noisefilter & 0x01; + /* I("EN_NoiseFilter2=%d\n", ts->en_noisefilter); */ + ts->p_point_num = ts->hx_point_num; + + if (ts->hx_touch_data->hx_coord_buf[ts->hx_touch_info_point_cnt] == 0xff) + ts->hx_point_num = 0; + else + ts->hx_point_num = + ts->hx_touch_data->hx_coord_buf[ts->hx_touch_info_point_cnt] & 0x0f; + + switch (ts_path) { + case HX_REPORT_COORD: + ts_status = himax_parse_report_points(ts, ts_path, ts_status); + break; + case HX_REPORT_COORD_RAWDATA: + /* touch monitor rawdata */ + if (ts->debug.ops.fp_set_diag_cmd) { + ts->debug.ops.fp_set_diag_cmd(ts, ts->ic_data, ts->hx_touch_data); + I("%s:raw data_checksum not match\n", __func__); + } else { + E("%s,There is no init set_diag_cmd\n", __func__); + } + + ts_status = himax_parse_report_points(ts, ts_path, ts_status); + break; + + default: + E("%s:Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + break; + } + if (ts->ts_dbg != 0) + I("%s: end now_status=%d!\n", __func__, ts_status); + return ts_status; +} + +/* end parse_report_data*/ + +static void himax_report_all_leave_event(struct himax_ts_data *ts) +{ + int loop_i = 0; + + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { +#if !defined(HX_PROTOCOL_A) + input_mt_slot(ts->input_dev, loop_i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); +#endif + } + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_sync(ts->input_dev); +} + +/* start report_point*/ +static void himax_finger_report(struct himax_ts_data *ts) +{ + int i = 0; + bool valid = false; + + if (ts->ts_dbg != 0) { + I("%s:start hx_touch_data->finger_num=%d\n", __func__, + ts->hx_touch_data->finger_num); + } + for (i = 0; i < ts->nFinger_support; i++) { + if (ts->target_report_data->x[i] >= 0 && + ts->target_report_data->x[i] <= ts->pdata->abs_x_max && + ts->target_report_data->y[i] >= 0 && + ts->target_report_data->y[i] <= ts->pdata->abs_y_max) + valid = true; + else + valid = false; + if (ts->ts_dbg != 0) + I("valid=%d\n", valid); + if (valid) { + if (ts->ts_dbg != 0) { + I("report_data->x[i]=%d,y[i]=%d,w[i]=%d", + ts->target_report_data->x[i], ts->target_report_data->y[i], + ts->target_report_data->w[i]); + } +#if !defined(HX_PROTOCOL_A) + input_mt_slot(ts->input_dev, i); +#else + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, + ts->target_report_data->w[i]); +#if !defined(HX_PROTOCOL_A) + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, + ts->target_report_data->w[i]); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, + ts->target_report_data->w[i]); +#else + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, i); +#endif + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, + ts->target_report_data->x[i]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, + ts->target_report_data->y[i]); +#if !defined(HX_PROTOCOL_A) + ts->last_slot = i; + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + pr_debug("[HXTP]Finger down,report_data->x[i]=%d,y[i]=%d,w[i]=%d", + ts->target_report_data->x[i], ts->target_report_data->y[i], + ts->target_report_data->w[i]); + if (ts->touch_num >= 50) { + I("Finger down,report_data->x[i]=%d,y[i]=%d,w[i]=%d", + ts->target_report_data->x[i], ts->target_report_data->y[i], + ts->target_report_data->w[i]); + ts->touch_num = 0; + } +#else + input_mt_sync(ts->input_dev); +#endif + } else { +#if !defined(HX_PROTOCOL_A) + input_mt_slot(ts->input_dev, i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); +#endif + } + } +#if !defined(HX_PROTOCOL_A) + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + input_sync(ts->input_dev); + + if (ts->ts_dbg != 0) + I("%s:end\n", __func__); +} + +static void himax_finger_leave(struct himax_ts_data *ts) +{ +#if !defined(HX_PROTOCOL_A) + int32_t loop_i = 0; +#endif + + if (ts->ts_dbg != 0) + I("%s: start!\n", __func__); + + ts->hx_touch_data->finger_on = 0; + ts->target_report_data->finger_on = 0; + ts->target_report_data->finger_num = 0; + ts->aa_press = 0; + +#if defined(HX_PROTOCOL_A) + input_mt_sync(ts->input_dev); +#endif +#if !defined(HX_PROTOCOL_A) + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + input_mt_slot(ts->input_dev, loop_i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); + } +#endif + if (ts->pre_finger_mask > 0) + ts->pre_finger_mask = 0; + + if (ts->first_pressed == 1) { + ts->first_pressed = 2; + I("E1@%d, %d\n", ts->pre_finger_data[0][0], ts->pre_finger_data[0][1]); + } + + /*if (ts->debug_log_level & BIT(1))*/ + /* himax_log_touch_event(x, y, w, loop_i, ts->en_noisefilter,*/ + /* HX_FINGER_LEAVE); */ + + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_sync(ts->input_dev); + ts->touch_num++; + + if (ts->ts_dbg != 0) + I("%s: end!\n", __func__); +} + +static void himax_report_points(struct himax_ts_data *ts) +{ + if (ts->ts_dbg != 0) + I("%s: start!\n", __func__); + + if (ts->hx_point_num != 0) + himax_finger_report(ts); + else + himax_finger_leave(ts); + + ts->last_en_noisefilter = ts->en_noisefilter; + + if (ts->ts_dbg != 0) + I("%s: end!\n", __func__); +} +/* end report_points*/ + +int himax_report_data(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + if (ts->ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + if (ts_path == HX_REPORT_COORD || ts_path == HX_REPORT_COORD_RAWDATA) { + /* Touch Point information */ + himax_report_points(ts); + + } else { + E("%s:Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + } + + if (ts->ts_dbg != 0) + I("%s: END, ts_status=%d!\n", __func__, ts_status); + return ts_status; +} +/* end report_data */ + +static int himax_ts_operation(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + uint8_t hw_reset_check[2]; +#if defined(FCA_PROTOCOL_EN) + uint8_t loop_i; + uint8_t Finger_i = 0; + uint16_t x_coord[5]; + uint16_t y_coord[5]; + memset(x_coord, 0xFFFF, 5 * sizeof(uint16_t)); + memset(y_coord, 0xFFFF, 5 * sizeof(uint16_t)); +#endif + memset(ts->xfer_buff, 0x00, 128 * sizeof(uint8_t)); + memset(hw_reset_check, 0x00, sizeof(hw_reset_check)); + + ts_status = himax_touch_get(ts, ts->xfer_buff, ts_path, ts_status); + if (ts_status == HX_TS_GET_DATA_FAIL) + goto END_FUNCTION; +#if !defined(FCA_PROTOCOL_EN) + ts_status = himax_distribute_touch_data(ts, ts->xfer_buff, ts_path, ts_status); + ts_status = himax_err_ctrl(ts, ts->xfer_buff, ts_path, ts_status); + + if (ts_status == HX_REPORT_DATA || ts_status == HX_TS_NORMAL_END) + ts_status = himax_parse_report_data(ts, ts_path, ts_status); + else + goto END_FUNCTION; + + ts_status = himax_report_data(ts, ts_path, ts_status); + +#else + for (loop_i = 0; loop_i < 25; loop_i++) { + if (loop_i == 0) + I("%s: Msg_cnt = 0x%2X\n", __func__, ts->xfer_buff[0]); + else if ((loop_i % 4) == 0 && loop_i < 24) { + if (ts->xfer_buff[loop_i] >> 7) { + x_coord[Finger_i] = + (((ts->xfer_buff[loop_i] & 0x07) << 8) | + ts->xfer_buff[loop_i - 1]); /* (HI & 0x07) >> 0x08 | LO*/ + y_coord[Finger_i++] = (((ts->xfer_buff[loop_i + 2] & 0x07) << 8) | + ts->xfer_buff[loop_i + 1]); + } + } + } + if (!Finger_i) { + himax_finger_leave(ts); + } else { + for (loop_i = 0; loop_i < Finger_i; loop_i++) { + if (x_coord[loop_i] != 0xFFFF) { + I("x_coord[%d] = %2d\n", loop_i, x_coord[loop_i]); + I("y_coord[%d] = %2d\n", loop_i, y_coord[loop_i]); + } + input_mt_slot(ts->input_dev, + loop_i); //input_mt_slot(input, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 1); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_coord[loop_i]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_coord[loop_i]); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_key(ts->input_dev, BTN_TOUCH, 1); + input_sync(ts->input_dev); + } + } + + goto END_FUNCTION; +#endif + +END_FUNCTION: + return ts_status; +} + +void himax_fail_det_work(struct himax_ts_data *ts) +{ + uint8_t data[8] = { 0 }; + /* uint8_t addr[4] = {0xD4, 0x74, 0x00, 0x10}; + Clear Simulation Register + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + I("%s Clear: addr[0]=0x%2.2X,addr[1]=0x%2.2X,addr[2]=0x%2.2X, addr[3]=0x%2.2X\n", + __func__,addr[0],addr[1],addr[2],addr[3]); +*/ + ts->core_fp.fp_dd_clk_set(ts, true); + ts->core_fp.fp_dd_reg_en(ts, true); + + ts->core_fp.fp_dd_reg_read(ts, 0xE5, 0, 8, data, 0); + I("%s E5_Bank0: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n", + __func__, data[0], data[1], data[2], data[3]); + I("%s E5_Bank0: para[4]=0x%2.2X,para[5]=0x%2.2X,para[6]=0x%2.2X, para[7]=0x%2.2X\n", + __func__, data[4], data[5], data[6], data[7]); + + ts->core_fp.fp_dd_reg_read(ts, 0xE5, 0, 8, data, 1); + I("%s E5_Bank1: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n", + __func__, data[0], data[1], data[2], data[3]); + I("%s E5_Bank1: para[4]=0x%2.2X,para[5]=0x%2.2X,para[6]=0x%2.2X, para[7]=0x%2.2X\n", + __func__, data[4], data[5], data[6], data[7]); + + ts->core_fp.fp_dd_clk_set(ts, false); + + /* It depends on customer: */ + + goto AP_recovery; + +AP_recovery: + + I("%s: Now FAIL_DET tie high means IC need external recovery\n", __func__); + + himax_mcu_tp_lcm_pin_reset(ts); +} + +void himax_ts_work(struct himax_ts_data *ts) +{ + int ts_status = HX_TS_NORMAL_END; + int ts_path = 0; + + if (ts->debug.ops.fp_ts_dbg_func) + ts->debug.ops.fp_ts_dbg_func(ts, HX_FINGER_ON); + + ts_path = himax_ts_work_status(ts); + switch (ts_path) { + case HX_REPORT_COORD: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + case HX_REPORT_COORD_RAWDATA: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + default: + E("%s:Path Fault! value=%d\n", __func__, ts_path); + goto END_FUNCTION; + } + + if (ts_status == HX_TS_GET_DATA_FAIL) + goto GET_TOUCH_FAIL; + else + goto END_FUNCTION; + +GET_TOUCH_FAIL: + I("%s: Now reset the Touch chip.\n", __func__); +#if defined(HX_RST_PIN_FUNC) + if (HX_SYSTEM_RESET == 0) + ts->core_fp.fp_ic_reset(ts, false, true); + else + ts->core_fp.fp_system_reset(ts); +#endif +END_FUNCTION: + if (ts->debug.ops.fp_ts_dbg_func) + ts->debug.ops.fp_ts_dbg_func(ts, HX_FINGER_LEAVE); +} +/*end ts_work*/ +enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer) +{ + struct himax_ts_data *ts; + + ts = container_of(timer, struct himax_ts_data, timer); + queue_work(ts->himax_wq, &ts->work); + hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +#if defined(HX_BOOT_UPGRADE) +static void himax_boot_upgrade(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work_boot_upgrade.work); + if (i_get_FW(ts) != 0) + return; + + ts->core_fp.fp_bin_desc_get(ts, (unsigned char *)ts->hxfw->data, HX1K); + + if (ts->boot_upgrade_flag == true) { + I("%s: Forced upgrade\n", __func__); + goto UPDATE_FW; + } + + if (himax_auto_update_check(ts) != 0) + goto SKIP_UPDATE_FW; + +UPDATE_FW: + if (i_update_FW(ts) <= 0) + E("%s: Update FW fail\n", __func__); + else + I("%s: Update FW success\n", __func__); + +SKIP_UPDATE_FW: + release_firmware(ts->hxfw); + ts->hxfw = NULL; +} +#endif + +static int hx_chk_flash_sts(struct himax_ts_data *ts) +{ + int rslt = 0; + + I("%s: Entering\n", __func__); + + rslt = (!ts->core_fp.fp_calculateChecksum(ts, false, FW_SIZE_128k)); + /*avoid the FW is full of zero*/ + rslt |= ts->core_fp.fp_flash_lastdata_check(ts, FW_SIZE_128k); + + return rslt; +} + +#if defined(HX_CONFIG_FB) || defined(HX_CONFIG_DRM) +static void himax_fb_register(struct work_struct *work) +{ + int ret = 0; + + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work_att.work); + + I("%s in\n", __func__); +#if defined(HX_CONFIG_FB) + ts->fb_notif.notifier_call = himax_fb_notifier_callback; + ret = fb_register_client(&ts->fb_notif); +#elif defined(HX_CONFIG_DRM) + ts->fb_notif.notifier_call = himax_drm_notifier_callback; + ret = msm_drm_register_client(&ts->fb_notif); +#endif + if (ret) + E("Unable to register fb_notifier: %d\n", ret); +} +#endif + +#if defined(HX_CONTAINER_SPEED_UP) +static void himax_resume_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, ts_int_work.work); + + himax_chip_common_resume(ts); +} +#endif + +int himax_chip_common_init(struct himax_ts_data *ts) +{ + int ret = 0; + int err = PROBE_FAIL; + struct himax_i2c_platform_data *pdata; + struct himax_chip_entry *entry; + + ts->p_point_num = 0xFFFF; + +#if defined(__EMBEDDED_FW__) + g_embedded_fw.size = (size_t)_binary___Himax_firmware_bin_end - + (size_t)_binary___Himax_firmware_bin_start; +#endif + ts->xfer_buff = devm_kzalloc(ts->dev, 128 * sizeof(uint8_t), GFP_KERNEL); + if (ts->xfer_buff == NULL) { + err = -ENOMEM; + goto err_xfer_buff_fail; + } + + I("PDATA START\n"); + pdata = kzalloc(sizeof(struct himax_i2c_platform_data), GFP_KERNEL); + if (pdata == NULL) { /*Allocate Platform data space*/ + err = -ENOMEM; + goto err_dt_platform_data_fail; + } + + I("ic_data START\n"); + ts->ic_data = kzalloc(sizeof(struct himax_ic_data), GFP_KERNEL); + if (ts->ic_data == NULL) { /*Allocate IC data space*/ + err = -ENOMEM; + goto err_dt_ic_data_fail; + } + + /* allocate report data */ + ts->hx_touch_data = kzalloc(sizeof(struct himax_report_data), GFP_KERNEL); + if (ts->hx_touch_data == NULL) { + err = -ENOMEM; + goto err_alloc_touch_data_failed; + } + + ts->pdata = pdata; + + if (himax_parse_dt(ts, pdata) < 0) { + I(" pdata is NULL for DT\n"); + goto err_alloc_dt_pdata_failed; + } + + ts->lcm_gpio = pdata->lcm_rst; + +#if defined(HX_RST_PIN_FUNC) + ts->rst_gpio = pdata->gpio_reset; +#endif + himax_gpio_power_config(ts, pdata); + +#if !defined(CONFIG_OF) + if (pdata->power) { + ret = pdata->power(1); + if (ret < 0) { + E("%s: power on failed\n", __func__); + goto err_power_failed; + } + } +#endif + + ts->hx_chip_inited = 0; + if (list_empty(&ts->chips)) { + I("%s: No available chip exist!\n", __func__); + goto error_ic_detect_failed; + } + + list_for_each_entry (entry, &ts->chips, list) { + ++himax_chip_num; + if (!entry->ops.detect) + continue; + + if (entry->ops.detect(ts)) { + I("%s: chip found! list_num=%d\n", __func__, himax_chip_num); + goto found_hx_chip; + } + + I("%s:num=%d,chip NOT found! go Next\n", __func__, himax_chip_num); + } + + if (entry && list_entry_is_head(entry, &ts->chips, list)) { + I("%s: No available chip exist!\n", __func__); + goto error_ic_detect_failed; + } + +found_hx_chip: + if (ts->core_fp.fp_chip_init != NULL) { + I("%s:lsy---fp_chip_init\n", __func__); + ts->core_fp.fp_chip_init(ts); + } else { + E("%s: function point of chip_init is NULL!\n", __func__); + goto error_ic_detect_failed; + } + + if (pdata->virtual_key) + ts->button = pdata->virtual_key; + + if (hx_chk_flash_sts(ts) == 1) { + E("%s: check flash fail, please upgrade FW\n", __func__); +#if defined(HX_BOOT_UPGRADE) + ts->boot_upgrade_flag = 1; +#endif + } else { + ts->core_fp.fp_reload_disable(ts, 0); + ts->core_fp.fp_power_on_init(ts); + ts->core_fp.fp_read_FW_ver(ts); + } + +#if defined(HX_BOOT_UPGRADE) + ts->himax_boot_upgrade_wq = create_singlethread_workqueue("HX_boot_upgrade"); + if (!ts->himax_boot_upgrade_wq) { + E("allocate himax_boot_upgrade_wq failed\n"); + err = -ENOMEM; + goto err_boot_upgrade_wq_failed; + } + INIT_DELAYED_WORK(&ts->work_boot_upgrade, himax_boot_upgrade); + queue_delayed_work(ts->himax_boot_upgrade_wq, &ts->work_boot_upgrade, + msecs_to_jiffies(2000)); +#endif + +#if defined(HX_CONTAINER_SPEED_UP) + ts->ts_int_workqueue = create_singlethread_workqueue("himax_ts_resume_wq"); + if (!ts->ts_int_workqueue) { + E("%s: create ts_resume workqueue failed\n", __func__); + goto err_create_ts_resume_wq_failed; + } + INIT_DELAYED_WORK(&ts->ts_int_work, himax_resume_work_func); +#endif + + /*Himax Power On and Load Config*/ + /* if (himax_loadSensorConfig(pdata)) { + * E("%s: Load Sesnsor configuration failed, unload driver.\n", + * __func__); + * goto err_detect_failed; + * } + */ + ts->core_fp.fp_power_on_init(ts); + calculate_point_number(ts); + +#if defined(CONFIG_OF) + ts->power = pdata->power; +#endif + + /*calculate the i2c data size*/ + calcDataSize(ts); + I("%s: calcDataSize complete\n", __func__); + +#if defined(CONFIG_OF) + ts->pdata->abs_pressure_min = 0; + ts->pdata->abs_pressure_max = 200; + ts->pdata->abs_width_min = 0; + ts->pdata->abs_width_max = 200; + pdata->cable_config[0] = 0xF0; + pdata->cable_config[1] = 0x00; +#endif + + ts->suspended = false; + ts->touch_num = 50; + +#if defined(HX_PROTOCOL_A) + ts->protocol_type = PROTOCOL_TYPE_A; +#else + ts->protocol_type = PROTOCOL_TYPE_B; +#endif + I("%s: Use Protocol Type %c\n", __func__, ts->protocol_type == PROTOCOL_TYPE_A ? 'A' : 'B'); + + ret = himax_input_register(ts); + if (ret) { + E("%s: Unable to register %s input device\n", __func__, ts->input_dev->name); + goto err_input_register_device_failed; + } + + spin_lock_init(&ts->irq_lock); + ts->initialized = true; + +#if defined(HX_CONFIG_FB) || defined(HX_CONFIG_DRM) + ts->himax_att_wq = create_singlethread_workqueue("HMX_ATT_request"); + + if (!ts->himax_att_wq) { + E(" allocate himax_att_wq failed\n"); + err = -ENOMEM; + goto err_get_intr_bit_failed; + } + + INIT_DELAYED_WORK(&ts->work_att, himax_fb_register); + queue_delayed_work(ts->himax_att_wq, &ts->work_att, msecs_to_jiffies(15000)); +#endif + + /*touch data init*/ + err = himax_report_data_init(ts); + if (err) + goto err_report_data_init_failed; +#if defined(HIMAX_I2C_PLATFORM) + err = himax_sysfs_init(ts); + if (err) + goto err_creat_sysfs_file_failed; +#endif + + if (himax_common_proc_init(ts)) { + E(" %s: himax_common proc_init failed!\n", __func__); + err = -ENOMEM; + goto err_creat_proc_file_failed; + } + + himax_ts_register_interrupt(ts); + + himax_fail_det_register_interrupt(ts); + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) + if (himax_debug_init(ts)) + E(" %s: debug initial failed!\n", __func__); +#endif + +#if defined(HX_BOOT_UPGRADE) + if (ts->boot_upgrade_flag) + himax_int_enable(ts, 0); +#endif + ts->hx_chip_inited = true; + return 0; + +err_creat_proc_file_failed: +#if defined(HIMAX_I2C_PLATFORM) + himax_sysfs_deinit(ts); +err_creat_sysfs_file_failed: +#endif + himax_report_data_deinit(ts); +err_report_data_init_failed: + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; +#if defined(HX_CONFIG_FB) || defined(HX_CONFIG_DRM) + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +err_get_intr_bit_failed: +#endif +err_input_register_device_failed: + input_free_device(ts->input_dev); + /*err_detect_failed:*/ + +#if defined(HX_CONTAINER_SPEED_UP) + cancel_delayed_work_sync(&ts->ts_int_work); + destroy_workqueue(ts->ts_int_workqueue); +err_create_ts_resume_wq_failed: +#endif + +#if defined(HX_BOOT_UPGRADE) + cancel_delayed_work_sync(&ts->work_boot_upgrade); + destroy_workqueue(ts->himax_boot_upgrade_wq); +err_boot_upgrade_wq_failed: +#endif + +error_ic_detect_failed: + himax_gpio_power_deconfig(pdata); +#if !defined(CONFIG_OF) +err_power_failed: +#endif +err_alloc_dt_pdata_failed: + kfree(ts->hx_touch_data); + ts->hx_touch_data = NULL; +err_alloc_touch_data_failed: + kfree(ts->ic_data); + ts->ic_data = NULL; +err_dt_ic_data_fail: + kfree(pdata); + pdata = NULL; +err_dt_platform_data_fail: + devm_kfree(ts->dev, ts->xfer_buff); + ts->xfer_buff = NULL; +err_xfer_buff_fail: + return err; +} + +void himax_chip_common_deinit(struct himax_ts_data *ts) +{ + himax_ts_unregister_interrupt(ts); + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT) + himax_inspect_data_clear(ts); +#endif + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) + himax_debug_remove(ts); +#endif + + himax_common_proc_deinit(ts); + himax_report_data_deinit(ts); + +#if defined(HX_CONFIG_FB) + if (fb_unregister_client(&ts->fb_notif)) + E("Error occurred while unregistering fb_notifier.\n"); + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +#elif defined(HX_CONFIG_DRM) + + if (msm_drm_unregister_client(&ts->fb_notif)) + E("Error occurred while unregistering drm_notifier.\n"); + + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +#endif +#if defined(HX_CONTAINER_SPEED_UP) + cancel_delayed_work_sync(&ts->ts_int_work); + destroy_workqueue(ts->ts_int_workqueue); +#endif +#if defined(HX_BOOT_UPGRADE) + cancel_delayed_work_sync(&ts->work_boot_upgrade); + destroy_workqueue(ts->himax_boot_upgrade_wq); +#endif + himax_gpio_power_deconfig(ts->pdata); + himax_mcu_in_cmd_struct_free(ts); + if (ts->input_dev) { + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; + } + +#if defined(HIMAX_I2C_PLATFORM) + himax_sysfs_deinit(ts); +#endif + kfree(ts->rw_buf); + ts->rw_buf = NULL; + kfree(ts->hx_touch_data); + ts->hx_touch_data = NULL; + kfree(ts->ic_data); + ts->ic_data = NULL; + kfree(ts->pdata->virtual_key); + ts->pdata->virtual_key = NULL; + devm_kfree(ts->dev, ts->xfer_buff); + ts->xfer_buff = NULL; + kfree(ts->pdata); + ts->pdata = NULL; + kfree(ts); + ts = NULL; + + I("%s: Common section deinited!\n", __func__); +} + +int himax_chip_common_suspend(struct himax_ts_data *ts) +{ + if (ts->suspended) { + I("%s: Already suspended. Skipped.\n", __func__); + goto END; + } else { + ts->suspended = true; + I("%s: enter\n", __func__); + } + + if (ts->debug.flash_dump_going == true) { + I("[himax] %s: Flash dump is going, reject suspend\n", __func__); + goto END; + } + + himax_int_enable(ts, 0); + if (ts->core_fp.fp_suspend_ic_action != NULL) + ts->core_fp.fp_suspend_ic_action(ts); + + if (!ts->use_irq) { + int32_t cancel_state; + + cancel_state = cancel_work_sync(&ts->work); + if (cancel_state) + himax_int_enable(ts, 1); + } + + /*ts->first_pressed = 0;*/ + atomic_set(&ts->suspend_mode, 1); + ts->pre_finger_mask = 0; + + if (ts->pdata) + if (ts->pdata->powerOff3V3 && ts->pdata->power) + ts->pdata->power(0); + +END: + if (ts->in_self_test == 1) + ts->suspend_resume_done = 1; + + I("%s: END\n", __func__); + + return 0; +} + +int himax_chip_common_resume(struct himax_ts_data *ts) +{ + I("%s: enter\n", __func__); + + if (ts->suspended == false) { + I("%s: It had entered resume, skip this step\n", __func__); + goto END; + } else { + ts->suspended = false; + } + + atomic_set(&ts->suspend_mode, 0); + ts->diag_cmd = 0; + + if (ts->pdata) + if (ts->pdata->powerOff3V3 && ts->pdata->power) + ts->pdata->power(1); + +#if defined(HX_RST_PIN_FUNC) + if (ts->core_fp.fp_ic_reset != NULL) + ts->core_fp.fp_ic_reset(ts, false, false); +#endif + + himax_report_all_leave_event(ts); + + if (ts->core_fp.fp_sense_on != NULL) + ts->core_fp.fp_resume_ic_action(ts); + + himax_int_enable(ts, 1); + +END: + if (ts->in_self_test == 1) + ts->suspend_resume_done = 1; + + I("%s: END\n", __func__); + return 0; +} diff --git a/drivers/input/touchscreen/hxchipset/himax_common.h b/drivers/input/touchscreen/hxchipset/himax_common.h new file mode 100644 index 000000000000..be628bb86974 --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/himax_common.h @@ -0,0 +1,208 @@ +/* Himax Android Driver Sample Code for common functions + * + * Copyright (C) 2021 Himax Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef HIMAX_COMMON_H +#define HIMAX_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_OF) +#include +#endif + +#include "himax_platform.h" + +#define HIMAX_DRIVER_VER "Sample_code_V61_A08.5.2" +#define FLASH_DUMP_FILE "/sdcard/HX_Flash_Dump.bin" + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) +#define HX_TP_PROC_2T2R +/*if enable, selftest works in driver*/ +/*#define HX_TP_SELF_TEST_DRIVER*/ +#endif + +/*===========Himax Option function=============*/ +#define HX_BOOT_UPGRADE +/*#define HX_PROTOCOL_A*/ +#define HX_PROTOCOL_B_3PA +#define HX_SYSTEM_RESET 1 + +/*for Himax auto-motive chipset */ +#define HX_RST_PIN_FUNC +#define HX_TP_INSPECT_MODE +#define HX_FIX_TOUCH_INFO +/*#define HX_ID_EN*/ +/*#define FCA_PROTOCOL_EN */ +/*=============================================*/ + +/* Enable it if driver go into suspend/resume twice */ +/*#undef HX_CONFIG_FB*/ +/* Enable it if driver go into suspend/resume twice */ +/*#undef HX_CONFIG_DRM*/ + +#if defined(HX_CONFIG_FB) +#include +#include +#elif defined(HX_CONFIG_DRM) +#include +#endif + +#if defined(__HIMAX_MOD__) +#define HX_USE_KSYM +#if !defined(HX_USE_KSYM) || !defined(__KERNEL_KALLSYMS_ALL_ENABLED__) +#error Modulized driver must enable HX_USE_KSYM and CONFIG_KALLSYM_ALL +#endif +#endif + +/* WP GPIO setting, decided by which pin direct to OS side, WP need pin */ +/* high either GPIO0 or GPIO4 */ +/* #define WP_GPIO0 */ +#define WP_GPIO4 + +#if defined(HX_BOOT_UPGRADE) +/* FW Auto upgrade case, you need to setup the fix_touch_info of module */ +#define BOOT_UPGRADE_FWNAME "Himax_firmware.bin" +#endif + +#if defined(HX_CONTAINER_SPEED_UP) +/* Resume queue delay work time after LCM RST (unit:ms) */ +#define DELAY_TIME 40 +#endif + +#define HX_MAX_WRITE_SZ (64 * 1024 + 4) +#define HX_KEY_MAX_COUNT 4 +#define DEFAULT_RETRY_CNT 3 + +#define HX_83191A_SERIES_PWON "HX83191A" +#define HX_83192A_SERIES_PWON "HX83192A" +#define HX_83193A_SERIES_PWON "HX83193A" + +#define HX_TP_BIN_CHECKSUM_SW 1 +#define HX_TP_BIN_CHECKSUM_HW 2 +#define HX_TP_BIN_CHECKSUM_CRC 3 + +#define SHIFTBITS 5 +#define FW_SIZE_64k 65536 +#define FW_SIZE_128k 131072 + +#define NO_ERR 0 +#define READY_TO_SERVE 1 +#define WORK_OUT 2 + +#define I2C_FAIL -1 +#define HX_INIT_FAIL -1 +#define MEM_ALLOC_FAIL -2 +#define CHECKSUM_FAIL -3 +#define GESTURE_DETECT_FAIL -4 +#define INPUT_REGISTER_FAIL -5 +#define FW_NOT_READY -6 +#define LENGTH_FAIL -7 +#define OPEN_FILE_FAIL -8 +#define PROBE_FAIL -9 +#define ERR_WORK_OUT -10 +#define ERR_STS_WRONG -11 +#define ERR_TEST_FAIL -12 + +#define HW_CRC_FAIL 1 +#define HX_FINGER_ON 1 +#define HX_FINGER_LEAVE 2 + +#if defined(__EMBEDDED_FW__) +extern const uint8_t _binary___Himax_firmware_bin_start[]; +extern const uint8_t _binary___Himax_firmware_bin_end[]; +extern struct firmware g_embedded_fw; +#endif + +enum HX_TS_PATH { + HX_REPORT_COORD = 1, + HX_REPORT_COORD_RAWDATA, +}; + +enum HX_TS_STATUS { + HX_TS_GET_DATA_FAIL = -4, + HX_CHKSUM_FAIL, + HX_PATH_FAIL, + HX_TS_NORMAL_END = 0, + HX_READY_SERVE, + HX_REPORT_DATA, + HX_EXCP_WARNING, + HX_RST_OK, +}; + +enum cell_type { CHIP_IS_ON_CELL, CHIP_IS_IN_CELL }; + +#if defined(HX_FIX_TOUCH_INFO) +enum fix_touch_info { + FIX_HX_RX_NUM = 60, + FIX_HX_TX_NUM = 32, + FIX_HX_BT_NUM = 0, + FIX_HX_MAX_PT = 10, + + FIX_HX_XY_REVERSE = false, + FIX_HX_INT_IS_EDGE = true, + +#if defined(HX_TP_PROC_2T2R) + FIX_HX_RX_NUM_2 = 0, + FIX_HX_TX_NUM_2 = 0, +#endif +}; +#endif + +enum input_protocol_type { + PROTOCOL_TYPE_A = 0x00, + PROTOCOL_TYPE_B = 0x01, +}; + +/*void himax_HW_reset(uint8_t loadconfig,uint8_t int_off);*/ +int himax_chip_common_suspend(struct himax_ts_data *ts); +int himax_chip_common_resume(struct himax_ts_data *ts); + +struct himax_core_fp; +extern struct device *g_device; + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) +int himax_debug_init(struct himax_ts_data *ts); +int himax_debug_remove(struct himax_ts_data *ts); +#endif + +int himax_parse_dt(struct himax_ts_data *ts, struct himax_i2c_platform_data *pdata); +int himax_report_data(struct himax_ts_data *ts, int ts_path, int ts_status); +int himax_report_data_init(struct himax_ts_data *ts); +int himax_dev_set(struct himax_ts_data *ts); +int himax_input_register_device(struct input_dev *input_dev); +int himax_input_register(struct himax_ts_data *ts); + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_INCELL) +extern void himax_mcu_in_cmd_struct_free(struct himax_ts_data *ts); +#endif +#endif diff --git a/drivers/input/touchscreen/hxchipset/himax_debug.c b/drivers/input/touchscreen/hxchipset/himax_debug.c new file mode 100644 index 000000000000..5c4397507514 --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/himax_debug.c @@ -0,0 +1,2477 @@ +/* Himax Android Driver Sample Code for debug nodes + * + * Copyright (C) 2021 Himax Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "himax.h" +#include "himax_debug.h" +#include "himax_ic_core.h" + +static ssize_t himax_crc_test_read(struct himax_ts_data *ts, char *buf, size_t len) +{ + ssize_t ret = 0; + uint8_t result = 0; + uint32_t size = 0; + + ts->core_fp.fp_sense_off(ts, true); + msleep(20); + + size = FW_SIZE_128k; + + result = ts->core_fp.fp_calculateChecksum(ts, false, size); + ts->core_fp.fp_sense_on(ts, 0x01); + + if (result) + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "CRC test is Pass!\n"); + else + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "CRC test is Fail!\n"); + + return ret; +} + +static ssize_t himax_vendor_read(struct file *file, char *buf, size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = pde_data(file_inode(file)); + ssize_t ret = 0; + + ts->core_fp.fp_reload_disable(ts, 0); + ts->core_fp.fp_power_on_init(ts); + ts->core_fp.fp_read_FW_ver(ts); + ts->core_fp.fp_touch_information(ts); + + if (!ts->hx_proc_send_flag) { + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "IC = %s\n", ts->chip_name); + + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "Architecture Version = 0x%2.2X\n", ts->ic_data->vendor_arch_ver); + + if (ts->chip_cell_type == CHIP_IS_ON_CELL) { + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "CONFIG_VER = 0x%2.2X\n", ts->ic_data->vendor_config_ver); + } else { + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "FW Touch Config. Version = 0x%2.2X\n", + ts->ic_data->vendor_touch_cfg_ver); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "FW Display Config. Version = 0x%2.2X\n", + ts->ic_data->vendor_display_cfg_ver); + } + + if (ts->ic_data->vendor_cid_maj_ver < 0 && ts->ic_data->vendor_cid_min_ver < 0) { + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "CID = NULL\n"); + } else { + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "CID = 0x%2.2X\n", + (ts->ic_data->vendor_cid_maj_ver << 8 | + ts->ic_data->vendor_cid_min_ver)); + } + + if (ts->ic_data->vendor_panel_ver < 0) { + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "Panel Version = NULL\n"); + } else { + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "Panel Version = 0x%2.2X\n", ts->ic_data->vendor_panel_ver); + } + if (ts->chip_cell_type == CHIP_IS_IN_CELL) { + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "Cusomer = %s\n", ts->ic_data->vendor_cus_info); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "Project Name = %s\n", ts->ic_data->vendor_proj_info); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "Config. Date = %s\n", ts->ic_data->vendor_config_date); + } + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, "\n"); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "Himax Touch Driver Version:\n"); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, "%s\n", + HIMAX_DRIVER_VER); + ts->hx_proc_send_flag = 1; + + if (copy_to_user(buf, ts->debug.buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + } else { + ts->hx_proc_send_flag = 0; + } + + return ret; +} + +static const struct proc_ops himax_proc_vendor_ops = { + .proc_read = himax_vendor_read, +}; + +static ssize_t himax_attn_read(struct himax_ts_data *ts, char *buf, size_t len) +{ + ssize_t ret = 0; + + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, "attn = %x\n", + himax_int_gpio_read(ts->pdata->gpio_irq)); + + return ret; +} + +static ssize_t himax_int_en_read(struct himax_ts_data *ts, char *buf, size_t len) +{ + size_t ret = 0; + + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, "%d\n", + ts->irq_enabled); + + return ret; +} + +static ssize_t himax_int_en_write(struct himax_ts_data *ts, char *buf, size_t len) +{ + int ret = 0; + + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + + if (buf[0] == '0') { + himax_int_enable(ts, 0); + } else if (buf[0] == '1') { + himax_int_enable(ts, 1); + } else if (buf[0] == '2') { + himax_int_enable(ts, 0); + free_irq(ts->hx_irq, ts); + ts->irq_enabled = 0; + } else if (buf[0] == '3') { + ret = himax_int_en_set(ts); + + if (ret == 0) { + ts->irq_enabled = 1; + atomic_set(&ts->irq_state, 1); + } + } else + return -EINVAL; + + return len; +} + +static ssize_t himax_layout_read(struct himax_ts_data *ts, char *buf, size_t len) +{ + size_t ret = 0; + + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, "%d ", + ts->pdata->abs_x_min); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, "%d ", + ts->pdata->abs_x_max); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, "%d ", + ts->pdata->abs_y_min); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, "%d ", + ts->pdata->abs_y_max); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, "\n"); + + return ret; +} + +static ssize_t himax_layout_write(struct himax_ts_data *ts, char *buf, size_t len) +{ + char buf_tmp[5] = { 0 }; + int i = 0, j = 0, k = 0, ret; + unsigned long value; + int layout[4] = { 0 }; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + for (i = 0; i < 20; i++) { + if (buf[i] == ',' || buf[i] == '\n') { + memset(buf_tmp, 0x0, sizeof(buf_tmp)); + + if (i - j <= 5) { + memcpy(buf_tmp, buf + j, i - j); + } else { + I("buffer size is over 5 char\n"); + return len; + } + + j = i + 1; + + if (k < 4) { + ret = kstrtoul(buf_tmp, 10, &value); + I("%s failed to get value, ret:%d\n", __func__, ret); + layout[k++] = value; + } + } + } + + if (k == 4) { + ts->pdata->abs_x_min = layout[0]; + ts->pdata->abs_x_max = (layout[1] - 1); + ts->pdata->abs_y_min = layout[2]; + ts->pdata->abs_y_max = (layout[3] - 1); + I("%d, %d, %d, %d\n", ts->pdata->abs_x_min, ts->pdata->abs_x_max, + ts->pdata->abs_y_min, ts->pdata->abs_y_max); + input_unregister_device(ts->input_dev); + himax_input_register(ts); + } else { + I("ERR@%d, %d, %d, %d\n", ts->pdata->abs_x_min, ts->pdata->abs_x_max, + ts->pdata->abs_y_min, ts->pdata->abs_y_max); + } + + return len; +} + +static ssize_t himax_debug_level_read(struct himax_ts_data *ts, char *buf, size_t len) +{ + size_t ret = 0; + + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, "%u\n", + ts->debug_log_level); + + if (copy_to_user(buf, ts->debug.buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + return ret; +} + +static ssize_t himax_debug_level_write(struct himax_ts_data *ts, char *buf, size_t len) +{ + int i; + + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + + ts->debug_log_level = 0; + + for (i = 0; i < len; i++) { + if (buf[i] >= '0' && buf[i] <= '9') + ts->debug_log_level |= (buf[i] - '0'); + else if (buf[i] >= 'A' && buf[i] <= 'F') + ts->debug_log_level |= (buf[i] - 'A' + 10); + else if (buf[i] >= 'a' && buf[i] <= 'f') + ts->debug_log_level |= (buf[i] - 'a' + 10); + + if (i != len - 1) + ts->debug_log_level <<= 4; + } + I("Now debug level value=%d\n", ts->debug_log_level); + + if (ts->debug_log_level & BIT(4)) { + I("Turn on/Enable Debug Mode for Inspection!\n"); + goto END_FUNC; + } + + if (ts->debug_log_level & BIT(3)) { + if (ts->pdata->screenWidth > 0 && ts->pdata->screenHeight > 0 && + (ts->pdata->abs_x_max - ts->pdata->abs_x_min) > 0 && + (ts->pdata->abs_y_max - ts->pdata->abs_y_min) > 0) { + ts->widthFactor = (ts->pdata->screenWidth << SHIFTBITS) / + (ts->pdata->abs_x_max - ts->pdata->abs_x_min); + ts->heightFactor = (ts->pdata->screenHeight << SHIFTBITS) / + (ts->pdata->abs_y_max - ts->pdata->abs_y_min); + + if (ts->widthFactor > 0 && ts->heightFactor > 0) { + ts->useScreenRes = 1; + } else { + ts->heightFactor = 0; + ts->widthFactor = 0; + ts->useScreenRes = 0; + } + } else { + I("Enable finger debug with raw position mode!\n"); + } + } else { + ts->useScreenRes = 0; + ts->widthFactor = 0; + ts->heightFactor = 0; + } +END_FUNC: + return len; +} + +static ssize_t himax_proc_register_read(struct himax_ts_data *ts, char *buf, size_t len) +{ + int ret = 0; + uint16_t loop_i; + + memset(ts->debug.reg_read_data, 0x00, 128 * sizeof(uint8_t)); + + I("himax_register_show: %02X,%02X,%02X,%02X\n", ts->debug.reg_cmd[3], ts->debug.reg_cmd[2], + ts->debug.reg_cmd[1], ts->debug.reg_cmd[0]); + himax_mcu_register_read(ts, ts->debug.reg_cmd, 128, ts->debug.reg_read_data, + ts->debug.cfg_flag); + + ret += snprintf(ts->debug.buf_tmp + ret, len - ret, "command: %02X,%02X,%02X,%02X\n", + ts->debug.reg_cmd[3], ts->debug.reg_cmd[2], ts->debug.reg_cmd[1], + ts->debug.reg_cmd[0]); + + for (loop_i = 0; loop_i < 128; loop_i++) { + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "0x%2.2X ", ts->debug.reg_read_data[loop_i]); + if ((loop_i % 16) == 15) + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "\n"); + } + + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, "\n"); + + return ret; +} + +static ssize_t himax_proc_register_write(struct himax_ts_data *ts, char *buf, size_t len) +{ + char buff_tmp[16] = { 0 }; + uint8_t length = 0; + unsigned long result = 0; + uint8_t loop_i = 0; + uint16_t base = 2; + char *data_str = NULL; + uint8_t w_data[20] = { 0 }; + uint8_t x_pos[20] = { 0 }; + uint8_t count = 0; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + memset(ts->debug.reg_cmd, 0x0, sizeof(ts->debug.reg_cmd)); + + I("himax %s\n", buf); + + if ((buf[0] == 'r' || buf[0] == 'w') && buf[1] == ':' && buf[2] == 'x') { + length = strlen(buf); + + /* I("%s: length = %d.\n", __func__,length); */ + for (loop_i = 0; loop_i < length; loop_i++) { + /* find postion of 'x' */ + if (buf[loop_i] == 'x') { + x_pos[count] = loop_i; + count++; + } + } + + data_str = strrchr(buf, 'x'); + I("%s: %s.\n", __func__, data_str); + length = strlen(data_str + 1); + + switch (buf[0]) { + case 'r': + if (buf[3] == 'F' && buf[4] == 'E' && length == 4) { + length = length - base; + ts->debug.cfg_flag = 1; + memcpy(buff_tmp, data_str + base + 1, length); + } else { + ts->debug.cfg_flag = 0; + memcpy(buff_tmp, data_str + 1, length); + } + ts->debug.byte_length = length / 2; + if (!kstrtoul(buff_tmp, 16, &result)) { + for (loop_i = 0; loop_i < ts->debug.byte_length; loop_i++) + ts->debug.reg_cmd[loop_i] = (uint8_t)(result >> loop_i * 8); + } + + break; + case 'w': + if (buf[3] == 'F' && buf[4] == 'E') { + ts->debug.cfg_flag = 1; + memcpy(buff_tmp, buf + base + 3, length); + } else { + ts->debug.cfg_flag = 0; + memcpy(buff_tmp, buf + 3, length); + } + + if (count < 3) { + ts->debug.byte_length = length / 2; + + if (!kstrtoul(buff_tmp, 16, &result)) { + /* command */ + for (loop_i = 0; loop_i < ts->debug.byte_length; loop_i++) { + ts->debug.reg_cmd[loop_i] = + (uint8_t)(result >> loop_i * 8); + } + } + + if (!kstrtoul(data_str + 1, 16, &result)) { + /* data */ + for (loop_i = 0; loop_i < ts->debug.byte_length; loop_i++) { + w_data[loop_i] = (uint8_t)(result >> loop_i * 8); + } + } + + himax_mcu_register_write(ts, ts->debug.reg_cmd, + ts->debug.byte_length, w_data, + ts->debug.cfg_flag); + } else { + for (loop_i = 0; loop_i < count; loop_i++) { + /* parsing addr after 'x' */ + memset(buff_tmp, 0x0, sizeof(buff_tmp)); + if (ts->debug.cfg_flag != 0 && loop_i != 0) + ts->debug.byte_length = 2; + else + ts->debug.byte_length = + x_pos[1] - x_pos[0] - 2; /* original */ + + memcpy(buff_tmp, buf + x_pos[loop_i] + 1, + ts->debug.byte_length); + + /* I("%s: buff_tmp = %s\n",*/ + /* __func__, buff_tmp);*/ + if (!kstrtoul(buff_tmp, 16, &result)) { + if (loop_i == 0) + ts->debug.reg_cmd[loop_i] = + (uint8_t)(result); + /* I("%s: + * reg_cmd + * = %X\n", __func__, + * reg_cmd[0]); + */ + else + w_data[loop_i - 1] = (uint8_t)(result); + /* I("%s: w_data[%d] = + * %2X\n", __func__, + * loop_i - 1, + * w_data[loop_i - 1]); + */ + } + } + + ts->debug.byte_length = count - 1; + himax_mcu_register_write(ts, ts->debug.reg_cmd, + ts->debug.byte_length, &w_data[0], + ts->debug.cfg_flag); + } + break; + }; + } + return len; +} + +static void himax_burn_GMA_to_flash(struct himax_ts_data *ts, uint8_t *gma_buf, uint8_t type) +{ + uint16_t CFG_4k_SIZE = 0x1000; + uint32_t start_addr; + uint8_t tmp_addr[4]; + + switch (type) { + case 'V': /*Start to flash VCOM*/ + start_addr = 0x28000; + I("Item : VCOM\n"); + break; + case 'A': /*Start to flash Analog GMA*/ + start_addr = 0x29000; + I("Item : AGMA\n"); + break; + case 'D': /*Start to flash Digital GMA*/ + start_addr = 0x2A000; + I("Item : DGMA\n"); + break; + default: + I("Input cmd is incorrect.\n"); + return; + } + + if (start_addr < 0x100) { + tmp_addr[3] = 0x00; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; + tmp_addr[0] = (uint8_t)start_addr; + } else if (start_addr >= 0x100 && start_addr < 0x10000) { + tmp_addr[3] = 0x00; + tmp_addr[2] = 0x00; + tmp_addr[1] = (uint8_t)(start_addr >> 8); + tmp_addr[0] = (uint8_t)start_addr; + } else if (start_addr >= 0x10000 && start_addr < 0x1000000) { + tmp_addr[3] = 0x00; + tmp_addr[2] = (uint8_t)(start_addr >> 16); + tmp_addr[1] = (uint8_t)(start_addr >> 8); + tmp_addr[0] = (uint8_t)start_addr; + } + + ts->core_fp.fp_sector_erase(ts, start_addr, CFG_4k_SIZE); + ts->core_fp.fp_flash_programming(ts, gma_buf, start_addr, CFG_4k_SIZE); + + if (ts->core_fp.fp_check_CRC(ts, tmp_addr, CFG_4k_SIZE) == 0) + I("Burn GMA Success!\n"); + else + E("Burn GMA FAIL!\n"); +} + +static uint8_t himax_convert_char2int(uint8_t src) +{ + uint8_t result; + if ((src <= '9') && (src >= '0')) { + result = src - '0'; + } else if ((src <= 'F') && (src >= 'A')) { + result = src - 'A' + 0x0a; + } else if ((src <= 'f') && (src >= 'a')) { + result = src - 'a' + 0x0a; + } else { + result = 0xFF; + } + return result; +} + +static uint8_t *himax_convert_GMA_data(struct himax_ts_data *ts, uint8_t *data, size_t data_size) +{ + uint8_t HEX_check[3]; + uint8_t GMA_content = 0; + uint16_t CFG_4k_SIZE = 0x1000; + uint32_t i = 0; + uint32_t GMA_length = 0; + uint32_t crc_value = 0; + + ts->debug.gma_buf = kcalloc(CFG_4k_SIZE, sizeof(uint8_t), GFP_KERNEL); + memset(HEX_check, 0xFF, 3); + memset(ts->debug.gma_buf, 0x00, CFG_4k_SIZE * sizeof(uint8_t)); + + while (i < data_size) { + if (data[i] == ',') { + memset(HEX_check, 0xFF, 3); + goto next_interation; + } else if (data[i] == 'x') { + if (HEX_check[1] == 0xFF) { + HEX_check[1] = data[i]; + goto next_interation; + } + } else if (data[i] == '0') { + if (HEX_check[0] == 0xFF) { + HEX_check[0] = data[i]; + goto next_interation; + } + } else if (himax_convert_char2int(data[i]) == 0xFF) { + goto next_interation; + } + + if ((HEX_check[1] == 'x') && (HEX_check[0] == '0')) { + if (HEX_check[2] == 0xFF) { + HEX_check[2] = data[i]; + GMA_content = himax_convert_char2int(data[i]); + } else { + GMA_content = (GMA_content << 4) + himax_convert_char2int(data[i]); + ts->debug.gma_buf[8 + GMA_length] = GMA_content; + ++GMA_length; + } + } + next_interation: + i++; + } + + ts->debug.gma_buf[0] = (GMA_length) % 0x100; + ts->debug.gma_buf[1] = (GMA_length >> 8) % 0x100; + ts->debug.gma_buf[2] = (GMA_length >> 16) % 0x100; + ts->debug.gma_buf[3] = (GMA_length >> 24) % 0x100; + + crc_value = ts->core_fp.fp_Calculate_CRC_with_AP(ts, ts->debug.gma_buf, 0, CFG_4k_SIZE - 4); + + ts->debug.gma_buf[CFG_4k_SIZE - 4] = (crc_value) % 0x100; + ts->debug.gma_buf[CFG_4k_SIZE - 3] = (crc_value >> 8) % 0x100; + ts->debug.gma_buf[CFG_4k_SIZE - 2] = (crc_value >> 16) % 0x100; + ts->debug.gma_buf[CFG_4k_SIZE - 1] = (crc_value >> 24) % 0x100; + + return ts->debug.gma_buf; +} + +static ssize_t himax_GMA_cmd_write(struct himax_ts_data *ts, char *buf, size_t len) +{ + char *fileName = NULL; + const struct firmware *fw = NULL; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + I("himax %s\n", buf); + + if ((buf[0] == 'A' && buf[1] == 'G' && buf[2] == 'M' && buf[3] == 'A') || + (buf[0] == 'V' && buf[1] == 'C' && buf[2] == 'O' && buf[3] == 'M') || + (buf[0] == 'D' && buf[1] == 'G' && buf[2] == 'M' && buf[3] == 'A')) { + switch (buf[0]) { + case 'A': + fileName = "AGMA.txt"; + break; + case 'V': + fileName = "VCOM.txt"; + break; + case 'D': + fileName = "DGMA.txt"; + break; + }; + } + request_firmware(&fw, fileName, ts->dev); + + if (fw) + himax_convert_GMA_data(ts, (uint8_t *)fw->data, fw->size); + himax_burn_GMA_to_flash(ts, ts->debug.gma_buf, buf[0]); + + kfree(ts->debug.gma_buf); + if (fw) + kfree(fw); + return len; +} + +static int32_t *getMutualBuffer(struct himax_ts_data *ts) +{ + return ts->debug.diag_mutual; +} +static int32_t *getMutualNewBuffer(struct himax_ts_data *ts) +{ + return ts->debug.diag_mutual_new; +} +static int32_t *getMutualOldBuffer(struct himax_ts_data *ts) +{ + return ts->debug.diag_mutual_old; +} +static int32_t *getSelfBuffer(struct himax_ts_data *ts) +{ + return ts->debug.diag_self; +} +static int32_t *getSelfNewBuffer(struct himax_ts_data *ts) +{ + return ts->debug.diag_self_new; +} +static int32_t *getSelfOldBuffer(struct himax_ts_data *ts) +{ + return ts->debug.diag_self_old; +} +static void setMutualBuffer(struct himax_ts_data *ts, uint8_t x_num, uint8_t y_num) +{ + ts->debug.diag_mutual = kzalloc(x_num * y_num * sizeof(int32_t), GFP_KERNEL); +} +static void setMutualNewBuffer(struct himax_ts_data *ts, uint8_t x_num, uint8_t y_num) +{ + ts->debug.diag_mutual_new = kzalloc(x_num * y_num * sizeof(int32_t), GFP_KERNEL); +} +static void setMutualOldBuffer(struct himax_ts_data *ts, uint8_t x_num, uint8_t y_num) +{ + ts->debug.diag_mutual_old = kzalloc(x_num * y_num * sizeof(int32_t), GFP_KERNEL); +} +static void setSelfBuffer(struct himax_ts_data *ts, uint8_t x_num, uint8_t y_num) +{ + ts->debug.diag_self = kzalloc((x_num + y_num) * sizeof(int32_t), GFP_KERNEL); +} +static void setSelfNewBuffer(struct himax_ts_data *ts, uint8_t x_num, uint8_t y_num) +{ + ts->debug.diag_self_new = kzalloc((x_num + y_num) * sizeof(int32_t), GFP_KERNEL); +} +static void setSelfOldBuffer(struct himax_ts_data *ts, uint8_t x_num, uint8_t y_num) +{ + ts->debug.diag_self_old = kzalloc((x_num + y_num) * sizeof(int32_t), GFP_KERNEL); +} + +#if defined(HX_TP_PROC_2T2R) +static int32_t *getMutualBuffer_2(struct himax_ts_data *ts) +{ + return ts->debug.diag_mutual_2; +} +static void setMutualBuffer_2(struct himax_ts_data *ts, uint8_t x_num_2, uint8_t y_num_2) +{ + ts->debug.diag_mutual_2 = kzalloc(x_num_2 * y_num_2 * sizeof(int32_t), GFP_KERNEL); +} +#endif + +static int himax_set_diag_cmd(struct himax_ts_data *ts, struct himax_ic_data *ic_data, + struct himax_report_data *hx_touch_data) +{ + int32_t *mutual_data; + int32_t *self_data; + int mul_num; + int self_num; + /* int RawDataLen = 0; */ + hx_touch_data->diag_cmd = ts->diag_cmd; + + if (hx_touch_data->diag_cmd >= 1 && hx_touch_data->diag_cmd <= 7) { + /* Check event stack CRC */ + if (!ts->core_fp.fp_diag_check_sum(ts, hx_touch_data)) + goto bypass_checksum_failed_packet; + +#if defined(HX_TP_PROC_2T2R) + if (ts->debug.is_2t2r && + (hx_touch_data->diag_cmd >= 4 && hx_touch_data->diag_cmd <= 6)) { + mutual_data = getMutualBuffer_2(ts); + self_data = getSelfBuffer(ts); + /* initiallize the block number of mutual and self */ + mul_num = ic_data->HX_RX_NUM_2 * ic_data->HX_TX_NUM_2; + self_num = ic_data->HX_RX_NUM_2 + ic_data->HX_TX_NUM_2; + } else +#endif + { + mutual_data = getMutualBuffer(ts); + self_data = getSelfBuffer(ts); + /* initiallize the block number of mutual and self */ + mul_num = ic_data->HX_RX_NUM * ic_data->HX_TX_NUM; + self_num = ic_data->HX_RX_NUM + ic_data->HX_TX_NUM; + } + ts->core_fp.fp_diag_parse_raw_data(ts, hx_touch_data, mul_num, self_num, + hx_touch_data->diag_cmd, mutual_data, self_data); + } else if (hx_touch_data->diag_cmd == 8) { + memset(ts->debug.diag_coor, 0x00, sizeof(ts->debug.diag_coor)); + memcpy(&(ts->debug.diag_coor[0]), &hx_touch_data->hx_coord_buf[0], + hx_touch_data->touch_info_size); + } + + /* assign state info data */ + memcpy(&(ts->debug.hx_state_info[0]), &hx_touch_data->hx_state_info[0], 2); + return NO_ERR; +bypass_checksum_failed_packet: + return 1; +} + +/* #if defined(HX_DEBUG_LEVEL) */ +static void himax_log_touch_data(struct himax_ts_data *ts, int start) +{ + int loop_i = 0; + int print_size = 0; + uint8_t *buf = NULL; + + if (start == 1) + return; /* report data when end of ts_work*/ + + if (ts->hx_touch_data->diag_cmd > 0) { + print_size = ts->hx_touch_data->touch_all_size; + buf = kcalloc(print_size, sizeof(uint8_t), GFP_KERNEL); + if (buf == NULL) { + E("%s, Failed to allocate memory\n", __func__); + return; + } + + memcpy(buf, ts->hx_touch_data->hx_coord_buf, ts->hx_touch_data->touch_info_size); + memcpy(&buf[ts->hx_touch_data->touch_info_size], ts->hx_touch_data->hx_rawdata_buf, + print_size - ts->hx_touch_data->touch_info_size); + } + + else if (ts->hx_touch_data->diag_cmd == 0) { + print_size = ts->hx_touch_data->touch_info_size; + buf = kcalloc(print_size, sizeof(uint8_t), GFP_KERNEL); + if (buf == NULL) { + E("%s, Failed to allocate memory\n", __func__); + return; + } + + memcpy(buf, ts->hx_touch_data->hx_coord_buf, print_size); + } else { + E("%s:cmd fault\n", __func__); + return; + } + + for (loop_i = 0; loop_i < print_size; loop_i += 8) { + if ((loop_i + 7) >= print_size) { + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", loop_i, buf[loop_i], loop_i + 1, + buf[loop_i + 1]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X\n", loop_i + 2, buf[loop_i + 2], + loop_i + 3, buf[loop_i + 3]); + break; + } + + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", loop_i, buf[loop_i], loop_i + 1, + buf[loop_i + 1]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", loop_i + 2, buf[loop_i + 2], loop_i + 3, + buf[loop_i + 3]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", loop_i + 4, buf[loop_i + 4], loop_i + 5, + buf[loop_i + 5]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", loop_i + 6, buf[loop_i + 6], loop_i + 7, + buf[loop_i + 7]); + I("\n"); + } + kfree(buf); +} + +#define PRT_LOG "Finger %d=> X:%d, Y:%d W:%d, Z:%d, F:%d, Int_Delay_Cnt:%d\n" +static void himax_log_touch_event(struct himax_ts_data *ts, int start) +{ + int loop_i = 0; + + if (start == 1) + return; /*report data when end of ts_work*/ + + if (ts->target_report_data->finger_on > 0 && ts->target_report_data->finger_num > 0) { + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + if (ts->target_report_data->x[loop_i] >= 0 && + ts->target_report_data->x[loop_i] <= ts->pdata->abs_x_max && + ts->target_report_data->y[loop_i] >= 0 && + ts->target_report_data->y[loop_i] <= ts->pdata->abs_y_max) { + I(PRT_LOG, loop_i + 1, ts->target_report_data->x[loop_i], + ts->target_report_data->y[loop_i], + ts->target_report_data->w[loop_i], + ts->target_report_data->w[loop_i], loop_i + 1, + ts->target_report_data->ig_count); + } + } + } else if (ts->target_report_data->finger_on == 0 && + ts->target_report_data->finger_num == 0) { + I("All Finger leave\n"); + } else { + I("%s : wrong input!\n", __func__); + } +} +static void himax_log_touch_int_devation(struct himax_ts_data *ts, int touched) +{ + if (touched == HX_FINGER_ON) { + ktime_get_real_ts64(&ts->debug.time_start); + /* I(" Irq start time = %ld.%06ld s\n", + * time_start.tv_sec, time_start.tv_nsec/1000); + */ + } else if (touched == HX_FINGER_LEAVE) { + ktime_get_real_ts64(&ts->debug.time_end); + ts->debug.time_delta.tv_nsec = + (ts->debug.time_end.tv_sec * 1000000000 + ts->debug.time_end.tv_nsec) - + (ts->debug.time_start.tv_sec * 1000000000 + ts->debug.time_start.tv_nsec); + /* I("Irq finish time = %ld.%06ld s\n", + * time_end.tv_sec, time_end.tv_nsec/1000); + */ + I("Touch latency = %ld us\n", ts->debug.time_delta.tv_nsec / 1000); + I("bus_speed = %d kHz\n", ts->bus_speed); + if (ts->target_report_data->finger_on == 0 && + ts->target_report_data->finger_num == 0) + I("All Finger leave\n"); + } else { + I("%s : wrong input!\n", __func__); + } +} + +#define RAW_DOWN_STATUS "status: Raw:F:%02d Down, X:%d, Y:%d, W:%d\n" +#define RAW_UP_STATUS "status: Raw:F:%02d Up, X:%d, Y:%d\n" + +static void himax_log_touch_event_detail(struct himax_ts_data *ts, int start) +{ + int loop_i = 0; + + if (start == HX_FINGER_LEAVE) { + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + if (((ts->old_finger >> loop_i & 1) == 0) && + ((ts->pre_finger_mask >> loop_i & 1) == 1)) { + if (ts->target_report_data->x[loop_i] >= 0 && + ts->target_report_data->x[loop_i] <= ts->pdata->abs_x_max && + ts->target_report_data->y[loop_i] >= 0 && + ts->target_report_data->y[loop_i] <= ts->pdata->abs_y_max) { + I(RAW_DOWN_STATUS, loop_i + 1, + ts->target_report_data->x[loop_i], + ts->target_report_data->y[loop_i], + ts->target_report_data->w[loop_i]); + } + } else if ((((ts->old_finger >> loop_i & 1) == 1) && + ((ts->pre_finger_mask >> loop_i & 1) == 0))) { + I(RAW_UP_STATUS, loop_i + 1, ts->pre_finger_data[loop_i][0], + ts->pre_finger_data[loop_i][1]); + } else { + /* I("dbg hx_point_num=%d, old_finger=0x%02X," + * " pre_finger_mask=0x%02X\n", + * ts->hx_point_num, ts->old_finger, + * ts->pre_finger_mask); + */ + } + } + } +} + +static void himax_ts_dbg_func(struct himax_ts_data *ts, int start) +{ + if (ts->debug_log_level & BIT(0)) { + /* I("debug level 1\n"); */ + himax_log_touch_data(ts, start); + } + if (ts->debug_log_level & BIT(1)) { + /* I("debug level 2\n"); */ + himax_log_touch_event(ts, start); + } + if (ts->debug_log_level & BIT(2)) { + /* I("debug level 4\n"); */ + himax_log_touch_int_devation(ts, start); + } + if (ts->debug_log_level & BIT(3)) { + /* I("debug level 8\n"); */ + himax_log_touch_event_detail(ts, start); + } +} + +static int himax_change_mode(struct himax_ts_data *ts, uint8_t str_pw, uint8_t end_pw) +{ + uint8_t data[4] = { 0 }; + int count = 0; + + /*sense off*/ + ts->core_fp.fp_sense_off(ts, true); + /*mode change*/ + data[1] = str_pw; + data[0] = str_pw; + if (ts->core_fp.fp_assign_sorting_mode != NULL) + ts->core_fp.fp_assign_sorting_mode(ts, data); + + /*sense on*/ + ts->core_fp.fp_sense_on(ts, 0x01); + /*wait mode change*/ + do { + if (ts->core_fp.fp_check_sorting_mode != NULL) + ts->core_fp.fp_check_sorting_mode(ts, data); + if ((data[0] == end_pw) && (data[1] == end_pw)) + return 0; + + I("Now retry %d times!\n", count); + count++; + msleep(50); + } while (count < 50); + + return ERR_WORK_OUT; +} + +#define PRT_OK_LOG "%s: change mode 0x%4X. str_pw = %2X, end_pw = %2X\n" +#define PRT_FAIL_LOG "%s: change mode failed. str_pw = %2X, end_pw = %2X\n" +static ssize_t himax_diag_cmd_write(struct himax_ts_data *ts, char *buf, size_t len) +{ + char *dbg_map_str = "mode:"; + char *str_ptr = NULL; + int str_len = 0; + int rst = 0; + uint8_t str_pw = 0; + uint8_t end_pw = 0; + + switch (len) { + case 1: /*raw out select - diag,X*/ + if (!kstrtoint(buf, 16, &rst)) { + ts->diag_cmd = rst; + I("%s: dsram_flag = %d\n", __func__, ts->debug.dsram_flag); + if (ts->debug.dsram_flag) { + /*Cancal work queue and return to stack*/ + ts->debug.process_type = 0; + ts->debug.dsram_flag = false; + cancel_delayed_work(&ts->himax_diag_delay_wrok); + himax_int_enable(ts, 1); + ts->core_fp.fp_return_event_stack(ts); + } + ts->core_fp.fp_diag_register_set(ts, ts->diag_cmd, 0, false); + I("%s: Set raw out select 0x%X.\n", __func__, ts->diag_cmd); + } + if (!ts->diag_cmd) { + if (ts->debug.mode_flag) /*back to normal mode*/ + himax_change_mode(ts, 0x00, 0x99); + } + break; + case 2: /*data processing + rawout select - diag,XY*/ + if (!kstrtoint(buf, 16, &rst)) { + ts->debug.process_type = (rst >> 4) & 0xF; + ts->diag_cmd = rst & 0xF; + } + if (ts->diag_cmd == 0) + break; + else if (ts->debug.process_type > 0 && ts->debug.process_type <= 3) { + if (!ts->debug.dsram_flag) { + /*Start wrok queue*/ + himax_int_enable(ts, 0); + ts->core_fp.fp_diag_register_set(ts, ts->diag_cmd, + ts->debug.process_type, false); + + queue_delayed_work(ts->himax_diag_wq, &ts->himax_diag_delay_wrok, + 2 * HZ / 100); + ts->debug.dsram_flag = true; + + I("%s: Start get raw data in DSRAM\n", __func__); + } else { + ts->core_fp.fp_diag_register_set(ts, ts->diag_cmd, + ts->debug.process_type, false); + } + } + break; + case 4: /*data processing + rawout select - diag,XXYY*/ + /*ex:XXYY=010A=dsram rawdata*/ + I("%s, now case 4\n", __func__); + if (!kstrtoint(buf, 16, &rst)) { + ts->debug.process_type = (rst >> 8) & 0xFF; + ts->diag_cmd = rst & 0xFF; + I("%s:process_type=0x%02X, diag_cmd=0x%02X\n", __func__, + ts->debug.process_type, ts->diag_cmd); + } + if (ts->debug.process_type <= 0 || ts->diag_cmd <= 0) + break; + else if (ts->debug.process_type > 0 && ts->debug.process_type <= 3) { + if (!ts->debug.dsram_flag) { + /*Start wrok queue*/ + himax_int_enable(ts, 0); + ts->core_fp.fp_diag_register_set(ts, ts->diag_cmd, + ts->debug.process_type, true); + + queue_delayed_work(ts->himax_diag_wq, &ts->himax_diag_delay_wrok, + 2 * HZ / 100); + ts->debug.dsram_flag = true; + + I("%s: Start get raw data in DSRAM\n", __func__); + } else { + ts->core_fp.fp_diag_register_set(ts, ts->diag_cmd, + ts->debug.process_type, true); + } + } + break; + case 9: /*change mode - mode:XXYY(start PW,end PW)*/ + str_ptr = strnstr(buf, dbg_map_str, len); + if (str_ptr) { + str_len = strlen(dbg_map_str); + if (!kstrtoint(buf + str_len, 16, &rst)) { + str_pw = (rst >> 8) & 0xFF; + end_pw = rst & 0xFF; + if (!himax_change_mode(ts, str_pw, end_pw)) { + ts->debug.mode_flag = 1; + I(PRT_OK_LOG, __func__, rst, str_pw, end_pw); + } else + I(PRT_FAIL_LOG, __func__, str_pw, end_pw); + } + } else { + I("%s: Can't find string [%s].\n", __func__, dbg_map_str); + } + break; + default: + I("%s: Length is not correct.\n", __func__); + } + return len; +} + +static ssize_t himax_diag_arrange_write(struct himax_ts_data *ts, char *buf, size_t len) +{ + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + ts->debug.diag_arr_num = buf[0] - '0'; + I("%s: diag_arr_num = %d\n", __func__, ts->debug.diag_arr_num); + return len; +} + +static void himax_get_mutual_edge(struct himax_ts_data *ts) +{ + int i = 0; + + for (i = 0; i < (ts->ic_data->HX_RX_NUM * ts->ic_data->HX_TX_NUM); i++) { + if (ts->debug.diag_mutual[i] > ts->debug.max_mutual) + ts->debug.max_mutual = ts->debug.diag_mutual[i]; + + if (ts->debug.diag_mutual[i] < ts->debug.min_mutual) + ts->debug.min_mutual = ts->debug.diag_mutual[i]; + } +} + +static void himax_get_self_edge(struct himax_ts_data *ts) +{ + int i = 0; + + for (i = 0; i < (ts->ic_data->HX_RX_NUM + ts->ic_data->HX_TX_NUM); i++) { + if (ts->debug.diag_self[i] > ts->debug.max_self) + ts->debug.max_self = ts->debug.diag_self[i]; + + if (ts->debug.diag_self[i] < ts->debug.min_self) + ts->debug.min_self = ts->debug.diag_self[i]; + } +} + +static void print_state_info(struct seq_file *s) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + + /* seq_printf(s, "State_info_2bytes:%3d, %3d\n", + * _state_info[0],hx_state_info[1]); + */ + seq_printf(s, "ReCal = %d\t", ts->debug.hx_state_info[0] & 0x01); + seq_printf(s, "Palm = %d\t", ts->debug.hx_state_info[0] >> 1 & 0x01); + seq_printf(s, "AC mode = %d\t", ts->debug.hx_state_info[0] >> 2 & 0x01); + seq_printf(s, "Water = %d\n", ts->debug.hx_state_info[0] >> 3 & 0x01); + seq_printf(s, "Glove = %d\t", ts->debug.hx_state_info[0] >> 4 & 0x01); + seq_printf(s, "TX Hop = %d\t", ts->debug.hx_state_info[0] >> 5 & 0x01); + seq_printf(s, "Base Line = %d\t", ts->debug.hx_state_info[0] >> 6 & 0x01); + seq_printf(s, "OSR Hop = %d\t", ts->debug.hx_state_info[1] >> 3 & 0x01); + seq_printf(s, "KEY = %d\n", ts->debug.hx_state_info[1] >> 4 & 0x0F); +} + +static void himax_diag_arrange_print(struct seq_file *s, int i, int j, int transpose) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + + if (transpose) + seq_printf(s, "%6d", ts->debug.diag_mutual[j + i * ts->ic_data->HX_RX_NUM]); + else + seq_printf(s, "%6d", ts->debug.diag_mutual[i + j * ts->ic_data->HX_RX_NUM]); +} + +/* ready to print second step which is column*/ +static void himax_diag_arrange_inloop(struct seq_file *s, int in_init, int out_init, bool transpose, + int j) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + int x_channel = ts->ic_data->HX_RX_NUM; + int y_channel = ts->ic_data->HX_TX_NUM; + int i; + int in_max = 0; + + if (transpose) + in_max = y_channel; + else + in_max = x_channel; + + if (in_init > 0) { /* bit0 = 1 */ + for (i = in_init - 1; i >= 0; i--) + himax_diag_arrange_print(s, i, j, transpose); + + if (transpose) { + if (out_init > 0) + seq_printf(s, " %5d\n", ts->debug.diag_self[j]); + else + seq_printf(s, " %5d\n", ts->debug.diag_self[x_channel - j - 1]); + } + } else { /* bit0 = 0 */ + for (i = 0; i < in_max; i++) + himax_diag_arrange_print(s, i, j, transpose); + + if (transpose) { + if (out_init > 0) + seq_printf(s, " %5d\n", ts->debug.diag_self[x_channel - j - 1]); + else + seq_printf(s, " %5d\n", ts->debug.diag_self[j]); + } + } +} + +/* print first step which is row */ +static void himax_diag_arrange_outloop(struct seq_file *s, int transpose, int out_init, int in_init) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + int j; + int x_channel = ts->ic_data->HX_RX_NUM; + int y_channel = ts->ic_data->HX_TX_NUM; + int out_max = 0; + int self_cnt = 0; + + if (transpose) + out_max = x_channel; + else + out_max = y_channel; + + if (out_init > 0) { /* bit1 = 1 */ + self_cnt = 1; + + for (j = out_init - 1; j >= 0; j--) { + seq_printf(s, "%3c%02d%c", '[', j + 1, ']'); + himax_diag_arrange_inloop(s, in_init, out_init, transpose, j); + + if (!transpose) { + seq_printf(s, " %5d\n", + ts->debug.diag_self[y_channel + x_channel - self_cnt]); + self_cnt++; + } + } + } else { /* bit1 = 0 */ + /* self_cnt = x_channel; */ + for (j = 0; j < out_max; j++) { + seq_printf(s, "%3c%02d%c", '[', j + 1, ']'); + himax_diag_arrange_inloop(s, in_init, out_init, transpose, j); + + if (!transpose) { + seq_printf(s, " %5d\n", ts->debug.diag_self[j + x_channel]); + } + } + } +} + +/* determin the output format of diag */ +static void himax_diag_arrange(struct seq_file *s) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + int x_channel = ts->ic_data->HX_RX_NUM; + int y_channel = ts->ic_data->HX_TX_NUM; + int bit2, bit1, bit0; + int i; + /* rotate bit */ + bit2 = ts->debug.diag_arr_num >> 2; + /* reverse Y */ + bit1 = ts->debug.diag_arr_num >> 1 & 0x1; + /* reverse X */ + bit0 = ts->debug.diag_arr_num & 0x1; + + if (ts->debug.diag_arr_num < 4) { + for (i = 0; i <= x_channel; i++) + seq_printf(s, "%3c%02d%c", '[', i, ']'); + + seq_puts(s, "\n"); + himax_diag_arrange_outloop(s, bit2, bit1 * y_channel, bit0 * x_channel); + seq_printf(s, "%6c", ' '); + + if (bit0 == 1) { + for (i = x_channel - 1; i >= 0; i--) + seq_printf(s, "%6d", ts->debug.diag_self[i]); + } else { + for (i = 0; i < x_channel; i++) + seq_printf(s, "%6d", ts->debug.diag_self[i]); + } + } else { + for (i = 0; i <= y_channel; i++) + seq_printf(s, "%3c%02d%c", '[', i, ']'); + + seq_puts(s, "\n"); + himax_diag_arrange_outloop(s, bit2, bit1 * x_channel, bit0 * y_channel); + seq_printf(s, "%6c", ' '); + + if (bit1 == 1) { + for (i = x_channel + y_channel - 1; i >= x_channel; i--) + seq_printf(s, "%6d", ts->debug.diag_self[i]); + } else { + for (i = x_channel; i < x_channel + y_channel; i++) + seq_printf(s, "%6d", ts->debug.diag_self[i]); + } + } +} + +static void *himax_diag_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + return (void *)((unsigned long)*pos + 1); +} + +static void *himax_diag_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_diag_seq_stop(struct seq_file *s, void *v) +{ + kfree(v); +} + +/* DSRAM thread */ +static bool himax_ts_diag_func(struct himax_ts_data *ts) +{ + int retry = 3; + int i = 0, j = 0; + unsigned int index = 0; + int x_channel = ts->ic_data->HX_RX_NUM; + int y_channel = ts->ic_data->HX_TX_NUM; + int total_size = (y_channel * x_channel + y_channel + x_channel) * 2; + uint8_t *info_data = NULL; + int32_t *mutual_data = NULL; + int32_t *mutual_data_new = NULL; + int32_t *mutual_data_old = NULL; + int32_t *self_data = NULL; + int32_t *self_data_new = NULL; + int32_t *self_data_old = NULL; + int32_t new_data; + /* 1:common dsram,2:100 frame Max,3:N-(N-1)frame */ + int dsram_type = ts->debug.process_type; + + info_data = kcalloc(total_size, sizeof(uint8_t), GFP_KERNEL); + if (info_data == NULL) { + E("%s: Failed to allocate memory\n", __func__); + return false; + } + + memset(info_data, 0, total_size * sizeof(uint8_t)); + + I("%s: process type=%d!\n", __func__, ts->debug.process_type); + + ts->core_fp.fp_burst_enable(ts, 1); + + if (dsram_type <= 2) { + mutual_data = getMutualBuffer(ts); + self_data = getSelfBuffer(ts); + } else if (dsram_type == 3) { + mutual_data = getMutualBuffer(ts); + mutual_data_new = getMutualNewBuffer(ts); + mutual_data_old = getMutualOldBuffer(ts); + self_data = getSelfBuffer(ts); + self_data_new = getSelfNewBuffer(ts); + self_data_old = getSelfOldBuffer(ts); + } + + do { + if (ts->core_fp.fp_get_DSRAM_data(ts, info_data, ts->debug.dsram_flag)) + break; + } while (retry-- > 0); + + if (retry <= 0) { + E("%s: Get DSRAM data failed\n", __func__); + kfree(info_data); + return false; + } + + index = 0; + + for (i = 0; i < y_channel; i++) { /*mutual data*/ + for (j = 0; j < x_channel; j++) { + new_data = (((int8_t)info_data[index + 1] << 8) | info_data[index]); + + if (dsram_type <= 1) { + mutual_data[i * x_channel + j] = new_data; + } else if (dsram_type == 2) { /* Keep max data */ + if (mutual_data[i * x_channel + j] < new_data) + mutual_data[i * x_channel + j] = new_data; + } else if (dsram_type == 3) { + /* Cal data for [N]-[N-1] frame */ + mutual_data_new[i * x_channel + j] = new_data; + mutual_data[i * x_channel + j] = + mutual_data_new[i * x_channel + j] - + mutual_data_old[i * x_channel + j]; + } + index += 2; + } + } + + for (i = 0; i < x_channel + y_channel; i++) { /*self data*/ + new_data = (((int8_t)info_data[index + 1] << 8) | info_data[index]); + if (dsram_type <= 1) { + self_data[i] = new_data; + } else if (dsram_type == 2) { /* Keep max data */ + if (self_data[i] < new_data) + self_data[i] = new_data; + } else if (dsram_type == 3) { /* Cal data for [N]-[N-1] frame */ + self_data_new[i] = new_data; + self_data[i] = self_data_new[i] - self_data_old[i]; + } + index += 2; + } + + kfree(info_data); + + if (dsram_type == 3) { + memcpy(mutual_data_old, mutual_data_new, x_channel * y_channel * sizeof(int32_t)); + /* copy N data to N-1 array */ + memcpy(self_data_old, self_data_new, (x_channel + y_channel) * sizeof(int32_t)); + /* copy N data to N-1 array */ + } + + ts->debug.diag_max_cnt++; + + if (dsram_type >= 1 && dsram_type <= 3) { + queue_delayed_work(ts->himax_diag_wq, &ts->himax_diag_delay_wrok, 1 / 10 * HZ); + } + return true; +} + +static int himax_diag_print(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + int x_num = ts->ic_data->HX_RX_NUM; + int y_num = ts->ic_data->HX_TX_NUM; + size_t ret = 0; + + /* don't add KEY_COUNT */ + seq_printf(s, "ChannelStart: %4d, %4d\n\n", x_num, y_num); + + /* start to show out the raw data in adb shell */ + himax_diag_arrange(s); + seq_puts(s, "\n"); + seq_puts(s, "ChannelEnd"); + seq_puts(s, "\n"); + + /* print Mutual/Slef Maximum and Minimum */ + himax_get_mutual_edge(ts); + himax_get_self_edge(ts); + seq_printf(s, "Mutual Max:%3d, Min:%3d\n", ts->debug.max_mutual, ts->debug.min_mutual); + seq_printf(s, "Self Max:%3d, Min:%3d\n", ts->debug.max_self, ts->debug.min_self); + /* recovery status after print*/ + ts->debug.max_mutual = 0; + ts->debug.min_mutual = 0xFFFF; + ts->debug.max_self = 0; + ts->debug.min_self = 0xFFFF; + + /*pring state info*/ + print_state_info(s); + + if (s->count >= s->size) + ts->debug.h_overflow++; + + return ret; +} + +static int himax_diag_stack_read(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + + if (ts->diag_cmd) + himax_diag_print(s, v); + else + seq_puts(s, "Please set raw out select 'echo diag,X > debug'\n\n"); + + return 0; +} + +static const struct seq_operations himax_diag_stack_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_stack_read, +}; + +static int himax_diag_stack_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_stack_ops); +}; + +static const struct proc_ops himax_proc_stack_ops = { + //.owner = THIS_MODULE, + .proc_open = himax_diag_stack_open, + .proc_read = seq_read, + .proc_release = seq_release, +}; + +static int himax_sram_read(struct seq_file *s, void *v, uint8_t rs) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + int d_type = 0; + + d_type = (!ts->diag_cmd) ? rs : ts->diag_cmd; + + if (!ts->debug.h_overflow) { + if (!ts->debug.process_type) { + himax_int_enable(ts, 0); + ts->core_fp.fp_diag_register_set(ts, d_type, 0, false); + + if (!himax_ts_diag_func(ts)) + seq_puts(s, "Get sram data failed."); + else + himax_diag_print(s, v); + + ts->diag_cmd = 0; + ts->core_fp.fp_diag_register_set(ts, 0, 0, false); + himax_int_enable(ts, 1); + } + } + + if ((ts->debug.process_type <= 3 && ts->diag_cmd && ts->debug.dsram_flag) || + ts->debug.h_overflow) { + himax_diag_print(s, v); + ts->debug.h_overflow = 0; + } + + return 0; +} + +static int himax_diag_delta_read(struct seq_file *s, void *v) +{ + return himax_sram_read(s, v, 0x09); +} + +static const struct seq_operations himax_diag_delta_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_delta_read, +}; + +static int himax_diag_delta_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_delta_ops); +}; + +static const struct proc_ops himax_proc_delta_ops = { + //.owner = THIS_MODULE, + .proc_open = himax_diag_delta_open, + .proc_read = seq_read, + .proc_release = seq_release, +}; + +static int himax_diag_dc_read(struct seq_file *s, void *v) +{ + return himax_sram_read(s, v, 0x0A); +} + +static const struct seq_operations himax_diag_dc_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_dc_read, +}; +static int himax_diag_dc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_dc_ops); +}; + +static const struct proc_ops himax_proc_dc_ops = { + //.owner = THIS_MODULE, + .proc_open = himax_diag_dc_open, + .proc_read = seq_read, + .proc_release = seq_release, +}; + +static int himax_diag_baseline_read(struct seq_file *s, void *v) +{ + return himax_sram_read(s, v, 0x0B); +} + +static const struct seq_operations himax_diag_baseline_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_baseline_read, +}; +static int himax_diag_baseline_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_baseline_ops); +}; + +static const struct proc_ops himax_proc_baseline_ops = { + //.owner = THIS_MODULE, + .proc_open = himax_diag_baseline_open, + .proc_read = seq_read, + .proc_release = seq_release, +}; + +static ssize_t himax_reset_write(struct himax_ts_data *ts, char *buf, size_t len) +{ + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + +#if defined(HX_RST_PIN_FUNC) + if (buf[0] == '1') + ts->core_fp.fp_ic_reset(ts, false, false); + else if (buf[0] == '2') + ts->core_fp.fp_ic_reset(ts, false, true); + else if (buf[0] == '3') + ts->core_fp.fp_ic_reset(ts, true, false); + else if (buf[0] == '4') + ts->core_fp.fp_ic_reset(ts, true, true); + /* else if (buf[0] == '5') */ + /* ESD_HW_REST(); */ +#endif + return len; +} + +static ssize_t himax_proc_FW_debug_read(struct himax_ts_data *ts, char *buf, size_t len) +{ + ssize_t ret = 0; + uint8_t i = 0; + uint8_t addr[4] = { 0 }; + uint8_t data[4] = { 0 }; + + len = (size_t)(sizeof(himax_dbg_reg_ary) / sizeof(uint32_t)); + + for (i = 0; i < len; i++) { + himax_parse_assign_cmd(himax_dbg_reg_ary[i], addr, 4); + himax_mcu_register_read(ts, addr, DATA_LEN_4, data, 0); + + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "reg[0-3] : 0x%08X = 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", + himax_dbg_reg_ary[i], data[0], data[1], data[2], data[3]); + I("reg[0-3] : 0x%08X = 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", himax_dbg_reg_ary[i], data[0], + data[1], data[2], data[3]); + } + + return ret; +} + +static void setFlashBuffer(struct himax_ts_data *ts) +{ + ts->debug.flash_buffer = kcalloc(ts->debug.flash_size, sizeof(uint8_t), GFP_KERNEL); +} + +static void flash_dump_prog_set(struct himax_ts_data *ts, uint8_t prog) +{ + ts->debug.flash_progress = prog; + if (prog == ONGOING) + ts->debug.flash_dump_going = ONGOING; + else + ts->debug.flash_dump_going = START; +} + +static int himax_proc_flash_read(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = pde_data(file_inode(s->file)); + ssize_t ret = 0; + int i; + uint8_t flash_progress = ts->debug.flash_progress; + uint8_t flash_cmd = ts->debug.flash_cmd; + bool flash_rst = ts->debug.flash_dump_rst; + + I("flash_progress = %d\n", flash_progress); + + if (!flash_rst) { + seq_puts(s, "FlashStart:Fail\n"); + seq_puts(s, "FlashEnd\n"); + return ret; + } + + if (flash_progress == START) + seq_puts(s, "Flash dump - Start\n"); + else if (flash_progress == ONGOING) + seq_puts(s, "Flash dump - On-going\n"); + else if (flash_progress == FINISHED) + seq_puts(s, "Flash dump - Finished\n"); + + /*print flash dump data*/ + if (flash_cmd == 1 && flash_progress == FINISHED) { + seq_puts(s, "Start to print flash dump data\n"); + for (i = 0; i < ts->debug.flash_size; i++) { + seq_printf(s, "0x%02X,", ts->debug.flash_buffer[i]); + if (i % 16 == 15) + seq_puts(s, "\n"); + } + } + + seq_puts(s, "FlashEnd\n"); + + return ret; +} + +static ssize_t himax_proc_flash_write(struct file *filp, const char __user *buff, size_t len, + loff_t *data) +{ + struct himax_ts_data *ts = pde_data(file_inode(filp)); + char buf[80] = { 0 }; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + I("%s: buf = %s\n", __func__, buf); + + if (ts->debug.flash_progress == ONGOING) { + E("%s: process is busy , return!\n", __func__); + return len; + } + + if ((buf[1] == '_') && (buf[2] == '6')) { + if (buf[3] == '4') + ts->debug.flash_size = FW_SIZE_64k; + + } else if ((buf[1] == '_') && (buf[2] == '2')) { + if (buf[3] == '8') + ts->debug.flash_size = FW_SIZE_128k; + } + + /*1 : print flash to window, 2 : dump to sdcard*/ + if (buf[0] == '1') { + /* 1_64,1_28 for flash size: + * 64k,128k + */ + ts->debug.flash_cmd = 1; + flash_dump_prog_set(ts, START); + ts->debug.flash_dump_rst = true; + queue_work(ts->flash_wq, &ts->flash_work); + } else if (buf[0] == '2') { + /* 2_64,2_28 for flash size: + * 64k,128k + */ + ts->debug.flash_cmd = 2; + flash_dump_prog_set(ts, START); + ts->debug.flash_dump_rst = true; + queue_work(ts->flash_wq, &ts->flash_work); + } + + return len; +} + +static void *himax_flash_dump_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + return (void *)((unsigned long)*pos + 1); +} + +static void *himax_flash_dump_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_flash_dump_seq_stop(struct seq_file *s, void *v) +{ +} + +static const struct seq_operations himax_flash_dump_seq_ops = { + .start = himax_flash_dump_seq_start, + .next = himax_flash_dump_seq_next, + .stop = himax_flash_dump_seq_stop, + .show = himax_proc_flash_read, +}; +static int himax_flash_dump_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_flash_dump_seq_ops); +}; + +static const struct proc_ops himax_proc_flash_ops = { + //.owner = THIS_MODULE, + .proc_open = himax_flash_dump_proc_open, + .proc_read = seq_read, + .proc_write = himax_proc_flash_write, +}; + +static void himax_ts_flash_func(struct himax_ts_data *ts) +{ + uint8_t flash_command = ts->debug.flash_cmd; + + himax_int_enable(ts, 0); + flash_dump_prog_set(ts, ONGOING); + + /*msleep(100);*/ + I("%s: flash_command = %d enter.\n", __func__, flash_command); + + if (flash_command == 1 || flash_command == 2) { + ts->core_fp.fp_flash_dump_func(ts, flash_command, ts->debug.flash_size, + ts->debug.flash_buffer); + ts->debug.flash_dump_rst = true; + } + + I("Complete~~~~~~~~~~~~~~~~~~~~~~~\n"); + + /* if (flash_command == 2) { + * struct file *fn; + * struct filename *vts_name; + * + * vts_name = getname_kernel(FLASH_DUMP_FILE); + * fn = file_open_name(vts_name, O_CREAT | O_WRONLY, 0); + * + * if (!IS_ERR(fn)) { + * I("%s create file and ready to write\n", __func__); + * fn->f_op->write(fn, flash_buffer, + * Flash_Size * sizeof(uint8_t), &fn->f_pos); + * filp_close(fn, NULL); + * } else { + * E("%s Open file failed!\n", __func__); + * g_flash_dump_rst = false; + * } + * } + */ + himax_int_enable(ts, 1); + flash_dump_prog_set(ts, FINISHED); +} + +static ssize_t himax_sense_on_off_write(struct himax_ts_data *ts, char *buf, size_t len) +{ + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (buf[0] == '0') { + ts->core_fp.fp_sense_off(ts, true); + I("Sense off\n"); + } else if (buf[0] == '1') { + if (buf[1] == 's') { + ts->core_fp.fp_sense_on(ts, 0x00); + I("Sense on re-map on, run sram\n"); + } else { + ts->core_fp.fp_sense_on(ts, 0x01); + I("Sense on re-map off, run flash\n"); + } + } else { + I("Do nothing\n"); + } + + return len; +} + +static ssize_t himax_debug_read(struct file *file, char *buf, size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = pde_data(file_inode(file)); + ssize_t ret = 0; + int i = 0; + + if (!ts->hx_proc_send_flag) { + I("%s, Enter\n", __func__); + + memset(ts->debug.buf_tmp, 0, sizeof(ts->debug.buf_tmp)); + if (dbg_cmd_flag) { + if (dbg_func_ptr_r[dbg_cmd_flag]) + ret += dbg_func_ptr_r[dbg_cmd_flag](ts, buf, len); + else + goto END_FUNC_R; + } + + if (ts->debug.debug_level_cmd == 't') { + if (!ts->debug.fw_update_going) { + if (ts->debug.fw_update_complete) + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "FW Update Complete "); + else + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "FW Update Fail "); + } else { + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "FW Update Ongoing "); + } + } else if (ts->debug.debug_level_cmd == 'h') { + if (ts->debug.handshaking_result == 0) + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Handshaking Result = %d (MCU Running)\n", + ts->debug.handshaking_result); + else if (ts->debug.handshaking_result == 1) + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Handshaking Result = %d (MCU Stop)\n", + ts->debug.handshaking_result); + else if (ts->debug.handshaking_result == 2) + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Handshaking Result = %d (I2C Error)\n", + ts->debug.handshaking_result); + else + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Handshaking Result = error\n"); + + } else if (ts->debug.debug_level_cmd == 'v') { + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "Architecture Version = 0x%2.2X\n", + ts->ic_data->vendor_arch_ver); + + if (ts->chip_cell_type == CHIP_IS_ON_CELL) + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "CONFIG_VER = 0x%2.2X\n", + ts->ic_data->vendor_config_ver); + else { + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "FW Touch Config. Version = 0x%2.2X\n", + ts->ic_data->vendor_touch_cfg_ver); + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "FW Display Config. Version = 0x%2.2X\n", + ts->ic_data->vendor_display_cfg_ver); + } + if (ts->ic_data->vendor_cid_maj_ver < 0 && + ts->ic_data->vendor_cid_min_ver < 0) + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, "CID = NULL\n"); + else + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, "CID = 0x%2.2X\n", + (ts->ic_data->vendor_cid_maj_ver << 8 | + ts->ic_data->vendor_cid_min_ver)); + + if (ts->ic_data->vendor_panel_ver < 0) + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Panel Version = NULL\n"); + else + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Panel Version = 0x%2.2X\n", + ts->ic_data->vendor_panel_ver); + if (ts->chip_cell_type == CHIP_IS_IN_CELL) { + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, "Cusomer = %s\n", + ts->ic_data->vendor_cus_info); + + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Project Name = %s\n", + ts->ic_data->vendor_proj_info); + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Config. Date = %s\n", + ts->ic_data->vendor_config_date); + } + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "\n"); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "Himax Touch Driver Version:\n"); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "%s\n", HIMAX_DRIVER_VER); + } else if (ts->debug.debug_level_cmd == 'd') { + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "Himax Touch IC Information :\n"); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "%s\n", ts->chip_name); + + switch (ts->ic_checksum) { + case HX_TP_BIN_CHECKSUM_SW: + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "IC Checksum : SW\n"); + break; + + case HX_TP_BIN_CHECKSUM_HW: + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "IC Checksum : HW\n"); + break; + + case HX_TP_BIN_CHECKSUM_CRC: + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "IC Checksum : CRC\n"); + break; + + default: + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "IC Checksum error.\n"); + } + + if (ts->ic_data->HX_INT_IS_EDGE) + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Driver register Interrupt : EDGE TIRGGER\n"); + else + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Driver register Interrupt : LEVEL TRIGGER\n"); + + if (ts->protocol_type == PROTOCOL_TYPE_A) + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Protocol : TYPE_A\n"); + else + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Protocol : TYPE_B\n"); + + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "RX Num : %d\n", ts->ic_data->HX_RX_NUM); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "TX Num : %d\n", ts->ic_data->HX_TX_NUM); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "BT Num : %d\n", ts->ic_data->HX_BT_NUM); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "X Resolution : %d\n", ts->ic_data->HX_X_RES); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "Y Resolution : %d\n", ts->ic_data->HX_Y_RES); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "Max Point : %d\n", ts->ic_data->HX_MAX_PT); + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "XY reverse : %d\n", ts->ic_data->HX_XY_REVERSE); +#if defined(HX_TP_PROC_2T2R) + if (ts->debug.is_2t2r) { + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, "2T2R panel\n"); + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, "RX Num_2 : %d\n", + ts->debug.hx_rx_num_2); + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, "TX Num_2 : %d\n", + ts->debug.hx_tx_num_2); + } +#endif + } else if (ts->debug.debug_level_cmd == 'n') { + /* Edgd = 1, Level = 0 */ + if (ts->core_fp.fp_read_ic_trigger_type(ts) == 1) + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "IC Interrupt type is edge trigger.\n"); + else if (ts->core_fp.fp_read_ic_trigger_type(ts) == 0) + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "IC Interrupt type is level trigger.\n"); + else + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Unkown IC trigger type.\n"); + + if (ts->ic_data->HX_INT_IS_EDGE) + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Driver register Interrupt : EDGE TIRGGER\n"); + else + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, + "Driver register Interrupt : LEVEL TRIGGER\n"); + } else if (ts->debug.debug_level_cmd == 'l') { + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "LotID : "); + for (i = 0; i < 13; i++) { + ret += snprintf(ts->debug.buf_tmp + ret, + sizeof(ts->debug.buf_tmp) - ret, "%02X", + ts->ic_data->vendor_ic_id[i]); + } + ret += snprintf(ts->debug.buf_tmp + ret, sizeof(ts->debug.buf_tmp) - ret, + "\n"); + } + + END_FUNC_R: + if (copy_to_user(buf, ts->debug.buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + ts->hx_proc_send_flag = 1; + } else { + ts->hx_proc_send_flag = 0; + } + + return ret; +} + +static ssize_t himax_debug_write(struct file *file, const char *buff, size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = pde_data(file_inode(file)); + char fileName[128]; + char buf[80] = "\0"; + int result = 0; + int fw_type = 0; + const struct firmware *fw = NULL; + + char *str_ptr = NULL; + int str_len = 0; + int i = 0; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + str_len = len; + buf[str_len - 1] = 0; /*remove \n*/ + + while (dbg_cmd_str[i]) { + str_ptr = strnstr(buf, dbg_cmd_str[i], len); + if (str_ptr) { + str_len = strlen(dbg_cmd_str[i]); + dbg_cmd_flag = i + 1; + ts->debug.debug_level_cmd = 0; + I("Cmd is correct :%s, dbg_cmd = %d\n", str_ptr, dbg_cmd_flag); + break; + } + i++; + } + if (!str_ptr) { + I("Cmd is not correct\n"); + dbg_cmd_flag = 0; + } + + if (buf[str_len] == ',') { + dbg_cmd_par = buf + str_len + 1; + if (dbg_func_ptr_w[dbg_cmd_flag]) + /* 2 => '/n' + ','*/ + dbg_func_ptr_w[dbg_cmd_flag](ts, dbg_cmd_par, len - str_len - 2); + + I("string of paremeter is %s, dbg_cmd_par = %s\n", buf + str_len + 1, dbg_cmd_par); + } else { + I("No paremeter of this cmd\n"); + } + + if (dbg_cmd_flag) + return len; + + if (buf[0] == 'v') { /* firmware version */ + himax_int_enable(ts, 0); + ts->debug.debug_level_cmd = buf[0]; + ts->core_fp.fp_reload_disable(ts, 0); + ts->core_fp.fp_power_on_init(ts); + ts->core_fp.fp_read_FW_ver(ts); + himax_int_enable(ts, 1); + return len; + } else if (buf[0] == 'd') { /* ic information */ + ts->debug.debug_level_cmd = buf[0]; + return len; + } else if (buf[0] == 't') { + if (buf[1] == 's' && buf[2] == 'd' && buf[3] == 'b' && buf[4] == 'g') { + if (buf[5] == '1') { + I("Open Ts Debug!\n"); + ts->ts_dbg = 1; + } else if (buf[5] == '0') { + I("Close Ts Debug!\n"); + ts->ts_dbg = 0; + } else { + E("Parameter fault for ts debug\n"); + } + goto ENDFUCTION; + } + himax_int_enable(ts, 0); + ts->debug.debug_level_cmd = buf[0]; + ts->debug.fw_update_complete = false; + ts->debug.fw_update_going = true; + memset(fileName, 0, 128); + /* parse the file name */ + snprintf(fileName, len - 2, "%s", &buf[2]); + + I("NOW Running common flow update!\n"); + I("%s: upgrade from file(%s) start!\n", __func__, fileName); + result = request_firmware(&fw, fileName, ts->dev); + + if (result < 0) { + I("fail to request_firmware fwpath: %s (ret:%d)\n", fileName, result); + return result; + } + + I("%s: FW image: %02X, %02X, %02X, %02X\n", __func__, fw->data[0], fw->data[1], + fw->data[2], fw->data[3]); + fw_type = (fw->size) / 1024; + /* start to upgrade */ + himax_int_enable(ts, 0); + I("Now FW size is : %dk\n", fw_type); + + switch (fw_type) { + case 64: + if (ts->core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k( + ts, (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", __func__, __LINE__); + ts->debug.fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__); + ts->debug.fw_update_complete = true; + } + break; + case 128: + if (ts->core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k( + ts, (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", __func__, __LINE__); + ts->debug.fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__); + ts->debug.fw_update_complete = true; + } + break; + + default: + E("%s: Flash command fail: %d\n", __func__, __LINE__); + ts->debug.fw_update_complete = false; + break; + } + release_firmware(fw); + goto firmware_upgrade_done; + + } else if (buf[0] == 'i' && buf[1] == 'n' && buf[2] == 't') { + /* INT trigger */ + ts->debug.debug_level_cmd = 'n'; + return len; + } else if (buf[0] == 'l' && buf[1] == 'o' && buf[2] == 't') { + ts->debug.debug_level_cmd = buf[0]; + ts->core_fp.fp_sense_off(ts, true); + ts->core_fp.fp_ic_id_read(ts); + ts->core_fp.fp_sense_on(ts, 0x01); + return len; + } + /* others,do nothing */ + ts->debug.debug_level_cmd = 0; + return len; + +firmware_upgrade_done: + ts->debug.fw_update_going = false; + ts->core_fp.fp_reload_disable(ts, 0); + + ts->core_fp.fp_reload_disable(ts, 0); + ts->core_fp.fp_power_on_init(ts); + + /* need to be review with FW update flow */ + result = himax_mcu_WP_BP_enable(ts); + + if (result < 0) { + I("%s: WP BP enable fail\n", __func__); + } + ts->core_fp.fp_read_FW_ver(ts); + ts->core_fp.fp_touch_information(ts); + + himax_int_enable(ts, 1); + /* todo himax_chip->tp_firmware_upgrade_proceed = 0; + * todo himax_chip->suspend_state = 0; + * todo enable_irq(himax_chip->irq); + */ +ENDFUCTION: + return len; +} + +static const struct proc_ops himax_proc_debug_ops = { + .proc_read = himax_debug_read, + .proc_write = himax_debug_write, +}; + +static void himax_himax_data_init(struct himax_ts_data *ts) +{ + ts->debug.ops.fp_ts_dbg_func = himax_ts_dbg_func; + ts->debug.ops.fp_set_diag_cmd = himax_set_diag_cmd; + ts->debug.flash_dump_going = false; +} + +static void himax_ts_flash_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, flash_work); + + himax_ts_flash_func(ts); +} + +static void himax_ts_diag_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = + container_of(work, struct himax_ts_data, himax_diag_delay_wrok.work); + himax_ts_diag_func(ts); +} + +static void dbg_func_ptr_init(void) +{ + /*debug function ptr init*/ + dbg_func_ptr_r[1] = himax_crc_test_read; + dbg_func_ptr_r[2] = himax_proc_FW_debug_read; + dbg_func_ptr_r[3] = himax_attn_read; + dbg_func_ptr_r[4] = himax_layout_read; + dbg_func_ptr_w[4] = himax_layout_write; + dbg_func_ptr_w[5] = himax_sense_on_off_write; + dbg_func_ptr_r[6] = himax_debug_level_read; + dbg_func_ptr_w[6] = himax_debug_level_write; + dbg_func_ptr_r[7] = himax_int_en_read; + dbg_func_ptr_w[7] = himax_int_en_write; + dbg_func_ptr_w[8] = himax_proc_register_write; + dbg_func_ptr_r[8] = himax_proc_register_read; + dbg_func_ptr_w[9] = himax_reset_write; + dbg_func_ptr_w[10] = himax_diag_arrange_write; + dbg_func_ptr_w[11] = himax_diag_cmd_write; + dbg_func_ptr_w[12] = himax_GMA_cmd_write; +} + +int himax_touch_proc_init(struct himax_ts_data *ts) +{ + ts->debug.procfs.diag_dir = proc_mkdir(HIMAX_PROC_DIAG_FOLDER, ts->debug.procfs.proc_dir); + + if (ts->debug.procfs.diag_dir == NULL) { + E(" %s: diag_dir file create failed!\n", __func__); + return -ENOMEM; + } + + ts->debug.procfs.vendor = + proc_create_data(HIMAX_PROC_VENDOR_FILE, 0444, ts->debug.procfs.proc_dir, + &himax_proc_vendor_ops, ts); + if (ts->debug.procfs.vendor == NULL) { + E(" %s: proc vendor file create failed!\n", __func__); + goto fail_1; + } + + ts->debug.procfs.stack = proc_create_data( + HIMAX_PROC_STACK_FILE, 0444, ts->debug.procfs.diag_dir, &himax_proc_stack_ops, ts); + if (ts->debug.procfs.stack == NULL) { + E(" %s: proc stack file create failed!\n", __func__); + goto fail_2_1; + } + + ts->debug.procfs.delta = proc_create_data( + HIMAX_PROC_DELTA_FILE, 0444, ts->debug.procfs.diag_dir, &himax_proc_delta_ops, ts); + if (ts->debug.procfs.delta == NULL) { + E(" %s: proc delta file create failed!\n", __func__); + goto fail_2_2; + } + + ts->debug.procfs.dc = proc_create_data(HIMAX_PROC_DC_FILE, 0444, ts->debug.procfs.diag_dir, + &himax_proc_dc_ops, ts); + if (ts->debug.procfs.dc == NULL) { + E(" %s: proc dc file create failed!\n", __func__); + goto fail_2_3; + } + + ts->debug.procfs.baseline = + proc_create_data(HIMAX_PROC_BASELINE_FILE, 0444, ts->debug.procfs.diag_dir, + &himax_proc_baseline_ops, ts); + if (ts->debug.procfs.baseline == NULL) { + E(" %s: proc baseline file create failed!\n", __func__); + goto fail_2_4; + } + + ts->debug.procfs.debug = proc_create_data( + HIMAX_PROC_DEBUG_FILE, 0644, ts->debug.procfs.proc_dir, &himax_proc_debug_ops, ts); + if (ts->debug.procfs.debug == NULL) { + E(" %s: proc debug file create failed!\n", __func__); + goto fail_3; + } + dbg_func_ptr_init(); + + ts->debug.procfs.flash_dump = + proc_create_data(HIMAX_PROC_FLASH_DUMP_FILE, 0644, ts->debug.procfs.proc_dir, + &himax_proc_flash_ops, ts); + if (ts->debug.procfs.flash_dump == NULL) { + E(" %s: proc flash dump file create failed!\n", __func__); + goto fail_4; + } + + return 0; + + /* remove_proc_entry(HIMAX_PROC_PEN_POS_FILE, ts->debug.procfs.proc_dir); */ +fail_4: + remove_proc_entry(HIMAX_PROC_DEBUG_FILE, ts->debug.procfs.proc_dir); +fail_3: + remove_proc_entry(HIMAX_PROC_BASELINE_FILE, ts->debug.procfs.diag_dir); +fail_2_4: + remove_proc_entry(HIMAX_PROC_DC_FILE, ts->debug.procfs.diag_dir); +fail_2_3: + remove_proc_entry(HIMAX_PROC_DELTA_FILE, ts->debug.procfs.diag_dir); +fail_2_2: + remove_proc_entry(HIMAX_PROC_STACK_FILE, ts->debug.procfs.diag_dir); +fail_2_1: + remove_proc_entry(HIMAX_PROC_VENDOR_FILE, ts->debug.procfs.proc_dir); +fail_1: + return -ENOMEM; +} + +void himax_touch_proc_deinit(struct himax_ts_data *ts) +{ + remove_proc_entry(HIMAX_PROC_FLASH_DUMP_FILE, ts->debug.procfs.proc_dir); + remove_proc_entry(HIMAX_PROC_DEBUG_FILE, ts->debug.procfs.proc_dir); + remove_proc_entry(HIMAX_PROC_BASELINE_FILE, ts->debug.procfs.diag_dir); + remove_proc_entry(HIMAX_PROC_DC_FILE, ts->debug.procfs.diag_dir); + remove_proc_entry(HIMAX_PROC_DELTA_FILE, ts->debug.procfs.diag_dir); + remove_proc_entry(HIMAX_PROC_STACK_FILE, ts->debug.procfs.diag_dir); + remove_proc_entry(HIMAX_PROC_VENDOR_FILE, ts->debug.procfs.proc_dir); + remove_proc_entry(HIMAX_PROC_DIAG_FOLDER, ts->debug.procfs.proc_dir); +} + +int himax_debug_init(struct himax_ts_data *ts) +{ + I("%s:Enter\n", __func__); + + if (ts == NULL) { + E("%s: ts struct is NULL\n", __func__); + return -EPROBE_DEFER; + } + + ts->debug.min_mutual = 0xFFFF; + ts->debug.min_self = 0xFFFF; + ts->debug.flash_size = 0x2B000; /*0x20000;*/ + + ts->debug.reg_read_data = kzalloc(128 * sizeof(uint8_t), GFP_KERNEL); + if (ts->debug.reg_read_data == NULL) { + E("%s: reg_read_data allocate failed\n", __func__); + goto err_alloc_reg_read_data_fail; + } + + himax_himax_data_init(ts); + + ts->flash_wq = create_singlethread_workqueue("himax_flash_wq"); + + if (!ts->flash_wq) { + E("%s: create flash workqueue failed\n", __func__); + goto err_create_flash_dump_wq_failed; + } + + INIT_WORK(&ts->flash_work, himax_ts_flash_work_func); + + ts->debug.flash_progress = START; + setFlashBuffer(ts); + if (ts->debug.flash_buffer == NULL) { + E("%s: flash buffer allocate fail failed\n", __func__); + goto err_flash_buf_alloc_failed; + } + + ts->himax_diag_wq = create_singlethread_workqueue("himax_diag"); + + if (!ts->himax_diag_wq) { + E("%s: create diag workqueue failed\n", __func__); + goto err_create_diag_wq_failed; + } + + INIT_DELAYED_WORK(&ts->himax_diag_delay_wrok, himax_ts_diag_work_func); + + setSelfBuffer(ts, ts->ic_data->HX_RX_NUM, ts->ic_data->HX_TX_NUM); + if (getSelfBuffer(ts) == NULL) { + E("%s: self buffer allocate failed\n", __func__); + goto err_self_buf_alloc_failed; + } + + setSelfNewBuffer(ts, ts->ic_data->HX_RX_NUM, ts->ic_data->HX_TX_NUM); + if (getSelfNewBuffer(ts) == NULL) { + E("%s: self new buffer allocate failed\n", __func__); + goto err_self_new_alloc_failed; + } + + setSelfOldBuffer(ts, ts->ic_data->HX_RX_NUM, ts->ic_data->HX_TX_NUM); + if (getSelfOldBuffer(ts) == NULL) { + E("%s: self old buffer allocate failed\n", __func__); + goto err_self_old_alloc_failed; + } + + setMutualBuffer(ts, ts->ic_data->HX_RX_NUM, ts->ic_data->HX_TX_NUM); + if (getMutualBuffer(ts) == NULL) { + E("%s: mutual buffer allocate failed\n", __func__); + goto err_mut_buf_alloc_failed; + } + + setMutualNewBuffer(ts, ts->ic_data->HX_RX_NUM, ts->ic_data->HX_TX_NUM); + if (getMutualNewBuffer(ts) == NULL) { + E("%s: mutual new buffer allocate failed\n", __func__); + goto err_mut_new_alloc_failed; + } + + setMutualOldBuffer(ts, ts->ic_data->HX_RX_NUM, ts->ic_data->HX_TX_NUM); + if (getMutualOldBuffer(ts) == NULL) { + E("%s: mutual old buffer allocate failed\n", __func__); + goto err_mut_old_alloc_failed; + } + +#if defined(HX_TP_PROC_2T2R) + + if (ts->debug.is_2t2r) { + setMutualBuffer_2(ts, ts->ic_data->HX_RX_NUM_2, ts->ic_data->HX_TX_NUM_2); + + if (getMutualBuffer_2(ts) == NULL) { + E("%s: mutual buffer 2 allocate failed\n", __func__); + goto err_mut_buf2_alloc_failed; + } + } +#endif + + if (himax_touch_proc_init(ts)) + goto err_proc_init_failed; + + return 0; + +err_proc_init_failed: +#if defined(HX_TP_PROC_2T2R) + kfree(ts->debug.diag_mutual_2); + ts->debug.diag_mutual_2 = NULL; +err_mut_buf2_alloc_failed: +#endif + kfree(ts->debug.diag_mutual_old); + ts->debug.diag_mutual_old = NULL; +err_mut_old_alloc_failed: + kfree(ts->debug.diag_mutual_new); + ts->debug.diag_mutual_new = NULL; +err_mut_new_alloc_failed: + kfree(ts->debug.diag_mutual); + ts->debug.diag_mutual = NULL; +err_mut_buf_alloc_failed: + kfree(ts->debug.diag_self_old); + ts->debug.diag_self_old = NULL; +err_self_old_alloc_failed: + kfree(ts->debug.diag_self_new); + ts->debug.diag_self_new = NULL; +err_self_new_alloc_failed: + kfree(ts->debug.diag_self); + ts->debug.diag_self = NULL; +err_self_buf_alloc_failed: + cancel_delayed_work_sync(&ts->himax_diag_delay_wrok); + destroy_workqueue(ts->himax_diag_wq); +err_create_diag_wq_failed: + + kfree(ts->debug.flash_buffer); + ts->debug.flash_buffer = NULL; +err_flash_buf_alloc_failed: + destroy_workqueue(ts->flash_wq); +err_create_flash_dump_wq_failed: + kfree(ts->debug.reg_read_data); + ts->debug.reg_read_data = NULL; +err_alloc_reg_read_data_fail: + + return -ENOMEM; +} +EXPORT_SYMBOL(himax_debug_init); + +int himax_debug_remove(struct himax_ts_data *ts) +{ + himax_touch_proc_deinit(ts); + cancel_delayed_work_sync(&ts->himax_diag_delay_wrok); + + destroy_workqueue(ts->himax_diag_wq); + destroy_workqueue(ts->flash_wq); + + if (ts->debug.reg_read_data != NULL) + kfree(ts->debug.reg_read_data); + + return 0; +} +EXPORT_SYMBOL(himax_debug_remove); diff --git a/drivers/input/touchscreen/hxchipset/himax_debug.h b/drivers/input/touchscreen/hxchipset/himax_debug.h new file mode 100644 index 000000000000..12e0a34ef5d9 --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/himax_debug.h @@ -0,0 +1,68 @@ +/* Himax Android Driver Sample Code for debug nodes + * + * Copyright (C) 2021 Himax Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef H_HIMAX_DEBUG +#define H_HIMAX_DEBUG + +#include "himax_platform.h" +#include "himax_common.h" + +int himax_touch_proc_init(struct himax_ts_data *ts); +void himax_touch_proc_deinit(struct himax_ts_data *ts); + +#define HIMAX_PROC_VENDOR_FILE "vendor" +#define HIMAX_PROC_PEN_POS_FILE "pen_pos" +#define HIMAX_PROC_DIAG_FOLDER "diag" +#define HIMAX_PROC_STACK_FILE "stack" +#define HIMAX_PROC_DELTA_FILE "delta_s" +#define HIMAX_PROC_DC_FILE "dc_s" +#define HIMAX_PROC_BASELINE_FILE "baseline_s" +#define HIMAX_PROC_DEBUG_FILE "debug" +#define HIMAX_PROC_FLASH_DUMP_FILE "flash_dump" + +enum flash_dump_prog { + START, + ONGOING, + FINISHED, +}; + +extern uint8_t X_NUM4; + +#if defined(HX_RST_PIN_FUNC) +extern void himax_ic_reset(uint8_t loadconfig, uint8_t int_off); +#endif + +/* Moved from debug.c end */ +#define CMD_NUM 16 +static const char *dbg_cmd_str[] = { + "crc_test", + "fw_debug", + "attn", + "layout", + "senseonoff", + "debug_level", + "int_en", + "register", + "reset", + "diag_arr", + "diag", + "GMA", + NULL +}; + +static int dbg_cmd_flag; +static char *dbg_cmd_par; +static ssize_t (*dbg_func_ptr_r[CMD_NUM])(struct himax_ts_data *ts, char *buf, size_t len); +static ssize_t (*dbg_func_ptr_w[CMD_NUM])(struct himax_ts_data *ts, char *buf, size_t len); +#endif diff --git a/drivers/input/touchscreen/hxchipset/himax_ic_HX83192.c b/drivers/input/touchscreen/hxchipset/himax_ic_HX83192.c new file mode 100644 index 000000000000..35ae23c28a12 --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/himax_ic_HX83192.c @@ -0,0 +1,578 @@ +/* Himax Android Driver Sample Code for HX83192 chipset + * + * Copyright (C) 2021 Himax Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define hx83192_data_adc_num 120 + +#include "himax.h" +#include "himax_ic_core.h" + +static void hx83192_chip_init(struct himax_ts_data *ts) +{ + ts->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, ts->chip_cell_type); + ts->ic_checksum = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + ts->fw_ver_maj_flash_addr = 49157; /*0x00C005*/ + ts->fw_ver_min_flash_addr = 49158; /*0x00C006*/ + ts->cfg_ver_maj_flash_addr = 49408; /*0x00C100*/ + ts->cfg_ver_min_flash_addr = 49409; /*0x00C101*/ + ts->cid_ver_maj_flash_addr = 49154; /*0x00C002*/ + ts->cid_ver_min_flash_addr = 49155; /*0x00C003*/ + ts->cfg_table_flash_addr = 0x10000; +} + +static bool himax_get_ic_Amount(struct himax_ts_data *ts) +{ + bool result = false; + uint8_t tmp_addr[DATA_LEN_4] = { 0 }; + uint8_t tmp_data[DATA_LEN_4] = { 0 }; + int cascadeenb; + + tmp_addr[3] = 0x90; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0xEC; + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + if (result) { + cascadeenb = (tmp_data[1] >> 2); + switch (cascadeenb) { + case 0: + ts->hx_ic_amount = 3; + break; + case 2: + ts->hx_ic_amount = 2; + break; + case 3: + ts->hx_ic_amount = 1; + break; + default: + result = false; + break; + } + } + I("hx_ic_Amount : %d\n", ts->hx_ic_amount); + + return result; +} + +static void hx83192_sense_on(struct himax_ts_data *ts, uint8_t FlashMode) +{ + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + int retry = 0; + int ret = 0; + + I("Enter %s\n", __func__); + ts->core_fp.fp_interface_on(ts); + + if (!FlashMode) { +#if defined(HX_RST_PIN_FUNC) + if (HX_SYSTEM_RESET == 0) + ts->core_fp.fp_ic_reset(ts, false, false); + else + ts->core_fp.fp_system_reset(ts); +#endif + } else { + do { + himax_parse_assign_cmd(addr_ctrl_fw, tmp_addr, sizeof(tmp_addr)); + himax_parse_assign_cmd(data_clear, tmp_data, sizeof(tmp_data)); + himax_mcu_register_write(ts, tmp_addr, DATA_LEN_4, tmp_data, 0); + + msleep(20); + + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, 0); + + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while (tmp_data[0] != 0x00 && retry++ < 5); + + if (retry >= 5) { + E("%s: Fail:\n", __func__); +#if defined(HX_RST_PIN_FUNC) + if (HX_SYSTEM_RESET == 0) + ts->core_fp.fp_ic_reset(ts, false, false); + else + ts->core_fp.fp_system_reset(ts); +#endif + } else { + I("%s:OK and Read status from IC = %X,%X\n", __func__, tmp_data[0], + tmp_data[1]); + /* reset code*/ + tmp_data[0] = 0x00; + tmp_data[1] = 0x00; + + ret = himax_bus_write(ts, addr_sense_on_off_0, tmp_data, 2, + HIMAX_I2C_RETRY_TIMES); + if (ret < 0) + E("%s: i2c access fail!\n", __func__); + + /*ret = himax_bus_write(ts, ts->ic_incell.pfw_op + ->adr_i2c_psw_ub[0], + tmp_data, 1, HIMAX_I2C_RETRY_TIMES); + if (ret < 0) + E("%s: i2c access fail!\n", __func__);*/ + } + msleep(280); + +#if defined(HIMAX_I2C_PLATFORM) + ret = himax_bus_read(ts, addr_AHB_rdata_byte_0, tmp_data, DATA_LEN_4, + HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + } +#endif + } +} + +static bool hx83192_sense_off(struct himax_ts_data *ts, bool check_en) +{ + bool result = true; + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4] = { 0 }; + uint8_t tmp_data[DATA_LEN_4] = { 0 }; + uint8_t cMax = 7; + uint8_t check = 0x87; + int ret = 0; + + msleep(280); + + himax_parse_assign_cmd(addr_cs_central_state, tmp_addr, sizeof(tmp_addr)); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + + if (tmp_data[0] != 0x0C) { + tmp_addr[3] = 0x90; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x5C; + cnt = 0; + do { + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0xA5; + himax_mcu_register_write(ts, tmp_addr, DATA_LEN_4, tmp_data, 0); + msleep(20); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, 0); + I("%s: Check 9000005C data[0]=%X\n", __func__, tmp_data[0]); + if (cnt++ >= cMax) + break; + } while (tmp_data[0] != check); + } + + do { + tmp_data[0] = para_sense_off_0; + + ret = himax_bus_write(ts, addr_sense_on_off_0, tmp_data, 1, HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + tmp_data[0] = para_sense_off_1; + + ret = himax_bus_write(ts, addr_sense_on_off_1, tmp_data, 1, HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + himax_parse_assign_cmd(addr_cs_central_state, tmp_addr, sizeof(tmp_addr)); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + return true; + } else if (cnt == 6) { + usleep_range(10000, 11000); +#if defined(HX_RST_PIN_FUNC) + if (HX_SYSTEM_RESET == 0) + ts->core_fp.fp_ic_reset(ts, false, false); + else + ts->core_fp.fp_system_reset(ts); +#endif + } + + } while (cnt++ < 15); + + return result; +} + +static int hx83192_mcu_register_read(struct himax_ts_data *ts, uint8_t *read_addr, + uint32_t read_length, uint8_t *read_data, uint8_t cfg_flag) +{ + uint8_t tmp_data[DATA_LEN_4]; + int i = 0; + int address = 0; + int ret = 0; + + if (cfg_flag == false) { + if (read_length > FLASH_RW_MAX_LEN) { + E("%s: read len over %d!\n", __func__, FLASH_RW_MAX_LEN); + return LENGTH_FAIL; + } + + address = (read_addr[3] << 24) + (read_addr[2] << 16) + (read_addr[1] << 8) + + read_addr[0]; + i = address; + tmp_data[0] = (uint8_t)i; + tmp_data[1] = (uint8_t)(i >> 8); + tmp_data[2] = (uint8_t)(i >> 16); + tmp_data[3] = (uint8_t)(i >> 24); + + ret = himax_bus_write(ts, addr_AHB_address_byte_0, tmp_data, DATA_LEN_4, + HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + tmp_data[0] = para_AHB_access_direction_read; + + ret = himax_bus_write(ts, addr_AHB_access_direction, tmp_data, 1, + HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + ret = himax_bus_read(ts, addr_AHB_rdata_byte_0, read_data, read_length, + HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + } else { + ret = himax_bus_read(ts, read_addr[0], read_data, read_length, + HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } + return NO_ERR; +} + +static void hx83192_mcu_flash_dump_func(struct himax_ts_data *ts, uint8_t local_flash_command, + int Flash_Size, uint8_t *flash_buffer) +{ + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + int page_prog_start = 0, retry_cnt = 0; + + I("%s,Entering\n", __func__); + + ts->core_fp.fp_sense_off(ts, true); + ts->core_fp.fp_burst_enable(ts, 0); + + /* ===SPI RX-FIFO Reset===*/ + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_fifo_rst, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_rxfifo_rst, 0); + + do { + himax_mcu_register_read(ts, ts->ic_incell.pflash_op->addr_spi200_fifo_rst, + DATA_LEN_4, tmp_data, 0); + + if (retry_cnt > 50) { + E("%s: Polling SPI Status FAIL", __func__); + return; + } + retry_cnt++; + } while ((tmp_data[0] & 0x02) != 0); + + /* ===SPI Transfer Control===*/ + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_7, 0); + + for (page_prog_start = 0; page_prog_start < Flash_Size; page_prog_start += 16) { + tmp_addr[0] = page_prog_start % 0x100; + tmp_addr[1] = (page_prog_start >> 8) % 0x100; + tmp_addr[2] = (page_prog_start >> 16) % 0x100; + tmp_addr[3] = page_prog_start / 0x1000000; + + /* ===Set SPI Address ===*/ + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_addr, DATA_LEN_4, + tmp_addr, 0); + /* ===SPI Transfer Control===*/ + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_cmd, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_cmd_7, 0); + retry_cnt = 0; + do { + himax_mcu_register_read(ts, ts->ic_incell.pflash_op->addr_spi200_rst_status, + DATA_LEN_4, tmp_data, 0); + + if (retry_cnt > 50) { + E("%s: Polling SPI Status FAIL", __func__); + return; + } + retry_cnt++; + } while ((tmp_data[1] & 0x80) == 0); + + hx83192_mcu_register_read(ts, ts->ic_incell.pflash_op->addr_spi200_data, 16, + &flash_buffer[page_prog_start], false); + } + + ts->core_fp.fp_sense_on(ts, 0x01); +} + +static void hx83192_mcu_flash_programming(struct himax_ts_data *ts, uint8_t *FW_content, + int start_addr, int length) +{ + int page_prog_start = 0, i = 0, j = 0, k = 0, retry_cnt = 0; + uint8_t tmp_data[DATA_LEN_4]; + uint8_t buring_data[FLASH_RW_MAX_LEN]; /* Read for flash data, 128K*/ + + I("%s", __func__); + /* 4 bytes for padding*/ + ts->core_fp.fp_interface_on(ts); + + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_flash_speed, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_cmd_8, 0); + + /* ===SPI TX-FIFO Reset===*/ + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_fifo_rst, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_txfifo_rst, 0); + + /* ===Polling Reset Status ===*/ + retry_cnt = 0; + + do { + himax_mcu_register_read(ts, ts->ic_incell.pflash_op->addr_spi200_fifo_rst, + DATA_LEN_4, tmp_data, 0); + + if (retry_cnt > 50) { + E("%s: Polling SPI Status Active FAIL", __func__); + return; + } + retry_cnt++; + } while (((tmp_data[0] & 0x04) >> 2) == 1); + + /* ===SPI Transfer Format===*/ + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_trans_fmt, 0); + + for (page_prog_start = start_addr; page_prog_start < start_addr + length; + page_prog_start += FLASH_RW_MAX_LEN) { + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_trans_ctrl, + DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_2, 0); + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_cmd, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_cmd_2, 0); + + /* ===Polling SPI Status Active ===*/ + retry_cnt = 0; + do { + himax_mcu_register_read(ts, ts->ic_incell.pflash_op->addr_spi200_rst_status, + 4, tmp_data, 0); + + if (retry_cnt > 50) { + E("%s: Polling FAIL", __func__); + return; + } + retry_cnt++; + } while ((tmp_data[0] & 0x01) == 1); + + /* ===WEL Write Enable ===*/ + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_trans_ctrl, + DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_6, 0); + + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_cmd, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_cmd_1, 0); + + /* ===Polling SPI Status Active ===*/ + retry_cnt = 0; + do { + himax_mcu_register_read(ts, ts->ic_incell.pflash_op->addr_spi200_rst_status, + DATA_LEN_4, tmp_data, 0); + if (retry_cnt > 50) { + E("%s: Polling FAIL", __func__); + return; + } + retry_cnt++; + } while ((tmp_data[0] & 0x01) == 1); + + himax_mcu_register_read(ts, ts->ic_incell.pflash_op->addr_spi200_data, DATA_LEN_4, + tmp_data, 0); + //WEL Fail + if (((tmp_data[0] & 0x02) >> 1) == 0) { + I("%s:SPI 0x8000002c = %d\n", __func__, tmp_data[0]); + break; + } + + /*Programmable size = 256 bytes, word_number = 256/4 = 64*/ + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_trans_ctrl, + DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_4, 0); + + /* Flash start address 1st : 0x0000_0000*/ + if (page_prog_start < 0x100) { + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = (uint8_t)page_prog_start; + } else if (page_prog_start >= 0x100 && page_prog_start < 0x10000) { + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = (uint8_t)(page_prog_start >> 8); + tmp_data[0] = (uint8_t)page_prog_start; + } else if (page_prog_start >= 0x10000 && page_prog_start < 0x1000000) { + tmp_data[3] = 0x00; + tmp_data[2] = (uint8_t)(page_prog_start >> 16); + tmp_data[1] = (uint8_t)(page_prog_start >> 8); + tmp_data[0] = (uint8_t)page_prog_start; + } + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_addr, DATA_LEN_4, + tmp_data, 0); + + for (i = 0; i < ADDR_LEN_4; i++) + buring_data[i] = ts->ic_incell.pflash_op->addr_spi200_data[i]; + + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_cmd, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_cmd_6, 0); + + for (j = 0; j < 16; j++) { + for (i = (page_prog_start + (j * 16)), k = 0; + i < (page_prog_start + (j * 16)) + 16; i++, k++) + + buring_data[k + ADDR_LEN_4] = FW_content[i - start_addr]; + /*I("FW_content[%d] = 0x%02X", i - start_addr, FW_content[i - start_addr]);*/ + + if (himax_bus_write(ts, addr_AHB_address_byte_0, buring_data, + ADDR_LEN_4 + 16, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + /* ===Polling SPI Status Active ===*/ + retry_cnt = 0; + do { + himax_mcu_register_read( + ts, ts->ic_incell.pflash_op->addr_spi200_rst_status, + DATA_LEN_4, tmp_data, 0); + + if (retry_cnt > 50) { + E("%s: Polling FAIL", __func__); + return; + } + retry_cnt++; + } while ((tmp_data[2] & 0x40) == 0); + } + + if (!ts->core_fp.fp_wait_wip(ts, 1)) + E("%s:Flash_Programming Fail\n", __func__); + } +} + +static bool hx83192_mcu_ic_id_read(struct himax_ts_data *ts) +{ + I("%s: [HX83192-A]", __func__); + return true; +} + +static bool hx83192_mcu_dd_clk_set(struct himax_ts_data *ts, bool enable) +{ + uint8_t data[4] = { 0 }; + + data[0] = (enable) ? 0xDD : 0x00; + return (himax_mcu_register_write(ts, ts->ic_incell.pfw_op->addr_osc_en, + sizeof(ts->ic_incell.pfw_op->addr_osc_en), data, + 0) == NO_ERR); +} + +static void hx83192_mcu_dd_reg_en(struct himax_ts_data *ts, bool enable) +{ + uint8_t data[4] = { 0 }; + + data[0] = 0xA5; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + himax_mcu_register_write(ts, ts->ic_incell.pfw_op->addr_osc_pw, + sizeof(ts->ic_incell.pfw_op->addr_osc_pw), data, 0); + data[0] = 0x00; + data[1] = 0x55; + data[2] = 0x66; + data[3] = 0xCC; + + ts->core_fp.fp_dd_reg_write(ts, 0xEB, 0, 4, data, 0); + data[0] = 0x00; + data[1] = 0x83; + data[2] = 0x19; + data[3] = 0x2A; + ts->core_fp.fp_dd_reg_write(ts, 0xB9, 0, 4, data, 0); +} + +static void hx83192_func_re_init(struct himax_ts_data *ts) +{ + ts->core_fp.fp_sense_on = hx83192_sense_on; + ts->core_fp.fp_sense_off = hx83192_sense_off; + ts->core_fp.fp_chip_init = hx83192_chip_init; + ts->core_fp.fp_ic_id_read = hx83192_mcu_ic_id_read; + ts->core_fp.fp_dd_clk_set = hx83192_mcu_dd_clk_set; + ts->core_fp.fp_dd_reg_en = hx83192_mcu_dd_reg_en; + + ts->core_fp.fp_flash_dump_func = hx83192_mcu_flash_dump_func; + ts->core_fp.fp_flash_programming = hx83192_mcu_flash_programming; +} + +bool hx83192_chip_detect(struct himax_ts_data *ts) +{ + uint8_t tmp_data[DATA_LEN_4] = { 0 }; + uint8_t tmp_addr[DATA_LEN_4] = { 0 }; + bool ret_data = false; + int ret = 0; + int i = 0; + + ret = himax_mcu_in_cmd_struct_init(ts); + if (ret < 0) { + ret_data = false; + E("%s:cmd_struct_init Fail:\n", __func__); + return ret_data; + } + + himax_mcu_in_cmd_init(ts); + + hx83192_func_re_init(ts); + + ts->core_fp.fp_sense_off(ts, true); + + for (i = 0; i < 5; i++) { + himax_parse_assign_cmd(addr_icid_addr, tmp_addr, sizeof(tmp_addr)); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + I("%s:Read driver IC ID = %X,%X,%X\n", __func__, tmp_data[3], tmp_data[2], + tmp_data[1]); + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x19) && (tmp_data[1] == 0x2a)) { + strlcpy(ts->chip_name, HX_83192A_SERIES_PWON, 30); + I("%s:IC name = %s\n", __func__, ts->chip_name); + I("Himax IC package %x%x%x in\n", tmp_data[3], tmp_data[2], tmp_data[1]); + + ret_data = true; + ts->ic_data->ic_adc_num = hx83192_data_adc_num; + himax_get_ic_Amount(ts); + return ret_data; + } + } + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + E("Could NOT find Himax Chipset\n"); + E("Please check 1.VCCD,VCCA,VSP,VSN\n"); + E("2.LCM_RST,TP_RST\n"); + E("3.Power On Sequence\n"); + + return ret_data; +} +EXPORT_SYMBOL(hx83192_chip_detect); + +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/hxchipset/himax_ic_core.h b/drivers/input/touchscreen/hxchipset/himax_ic_core.h new file mode 100644 index 000000000000..7588e2c42968 --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/himax_ic_core.h @@ -0,0 +1,267 @@ +/* Himax Android Driver Sample Code for ic core functions + * + * Copyright (C) 2021 Himax Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __HIMAX_IC_CORE_H__ +#define __HIMAX_IC_CORE_H__ + +#include "himax_platform.h" +#include "himax_common.h" +#include + +#define DATA_LEN_8 8 +#define DATA_LEN_4 4 +#define ADDR_LEN_4 4 +#define FLASH_RW_MAX_LEN 256 +#define FLASH_WRITE_BURST_SZ 8 +#define MAX_I2C_TRANS_SZ 128 +#define HIMAX_TOUCH_DATA_SIZE 128 + +#define FW_SECTOR_PER_BLOCK 8 +#define FW_PAGE_PER_SECTOR 64 +#define FW_PAGE_SZ 128 +#define HX1K 0x400 +#define HX64K 0x10000 + +#define HX_RW_REG_FAIL (-1) +#define HX_DRIVER_MAX_IC_NUM 5 + +#if defined(__HIMAX_HX83191_MOD__) +#define HX_MOD_KSYM_HX83191 HX_MOD_KSYM_HX83191 +#endif + +#if defined(__HIMAX_HX83192_MOD__) +#define HX_MOD_KSYM_HX83192 HX_MOD_KSYM_HX83192 +#endif + +#if defined(__HIMAX_HX83193_MOD__) +#define HX_MOD_KSYM_HX83193 HX_MOD_KSYM_HX83193 +#endif + +/* CORE_INIT */ +/* CORE_FW */ +/* CORE_FLASH */ +/* CORE_SRAM */ + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_INCELL) +void himax_mcu_in_cmd_struct_free(struct himax_ts_data *ts); +#endif + +#if defined(HX_RST_PIN_FUNC) +void himax_gpio_set(int pinnum, uint8_t value); +#endif + +int himax_report_data_init(struct himax_ts_data *ts); + +/* CORE_INIT */ +int himax_mcu_in_cmd_struct_init(struct himax_ts_data *ts); +void himax_mcu_in_cmd_init(struct himax_ts_data *ts); +void himax_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len); +int himax_mcu_WP_BP_disable(struct himax_ts_data *ts); +int himax_mcu_WP_BP_enable(struct himax_ts_data *ts); +int himax_mcu_WP_BP_status(struct himax_ts_data *ts); +int himax_mcu_flash_id_check(struct himax_ts_data *ts); +int himax_mcu_register_write(struct himax_ts_data *ts, uint8_t *write_addr, uint32_t write_length, + uint8_t *write_data, uint8_t cfg_flag); +int himax_mcu_register_read(struct himax_ts_data *ts, uint8_t *read_addr, uint32_t read_length, + uint8_t *read_data, uint8_t cfg_flag); +void himax_mcu_tp_lcm_pin_reset(struct himax_ts_data *ts); +/* CORE_INIT */ + +enum AHB_Interface_Command_Table { + addr_AHB_address_byte_0 = 0x00, + addr_AHB_rdata_byte_0 = 0x08, + addr_AHB_access_direction = 0x0C, + addr_AHB_continous = 0x13, + addr_AHB_INC4 = 0x0D, + addr_sense_on_off_0 = 0x31, + addr_sense_on_off_1 = 0x32, + addr_read_event_stack = 0x30, + para_AHB_access_direction_read = 0x00, + para_AHB_continous = 0x31, + para_AHB_INC4 = 0x10, + para_sense_off_0 = 0x27, + para_sense_off_1 = 0x95, +}; + +/* CORE_FW */ +#define addr_fw_state 0x800204DC +#define addr_psl 0x900000A0 +#define addr_cs_central_state 0x900000A8 +#define addr_flag_reset_event 0x900000E4 +#define addr_chk_dd_status 0x900000E8 +#define fw_addr_osc_en 0x9000009C +#define fw_addr_osc_pw 0x90000280 +#define addr_system_reset 0x90000018 +#define addr_ctrl_fw 0x9000005C +#define addr_icid_addr 0x900000D0 +#define fw_addr_program_reload_from 0x00000000 +#define fw_addr_reload_status 0x80050000 +#define fw_addr_reload_crc32_result 0x80050018 +#define fw_addr_reload_addr_from 0x80050020 +#define fw_addr_reload_addr_cmd_beat 0x80050028 +#define data_system_reset 0x00000055 +#define data_clear 0x00000000 +#define addr_raw_out_sel 0x100072EC +#define addr_set_frame_addr 0x10007294 +#define addr_sorting_mode_en 0x10007F04 +#define addr_fw_mode_status 0x10007088 +#define addr_fw_architecture_version 0x10007004 +#define addr_fw_config_date 0x10007038 +#define addr_fw_config_version 0x10007084 +#define addr_fw_CID 0x10007000 +#define addr_fw_customer 0x10007008 +#define addr_fw_project_name 0x10007014 +#define addr_fw_dbg_msg_addr 0x10007F40 +#define addr_HX_ID_EN 0x10007134 +#define addr_mkey 0x100070E8 +#define addr_fw_define_flash_reload 0x10007f00 +#define addr_fw_define_2nd_flash_reload 0x100072c0 +#define data_fw_define_flash_reload_dis 0x0000a55a +#define data_fw_define_flash_reload_en 0x00000000 +#define addr_fw_define_int_is_edge 0x10007088 +#define addr_fw_define_rxnum_txnum_maxpt 0x100070f4 +#define addr_fw_define_xy_res_enable 0x100070f8 + +#define fw_data_rawdata_ready_hb 0xa3 +#define fw_data_rawdata_ready_lb 0x3a +/* CORE_FW */ + +/* CORE_FLASH */ +#define flash_addr_ctrl_base 0x80000000 +#define flash_addr_spi200_trans_fmt (flash_addr_ctrl_base + 0x10) +#define flash_addr_spi200_trans_ctrl (flash_addr_ctrl_base + 0x20) +#define flash_addr_spi200_cmd (flash_addr_ctrl_base + 0x24) +#define flash_addr_spi200_addr (flash_addr_ctrl_base + 0x28) +#define flash_addr_spi200_data (flash_addr_ctrl_base + 0x2c) +#define flash_addr_spi200_fifo_rst (flash_addr_ctrl_base + 0x30) +#define flash_addr_spi200_rst_status (flash_addr_ctrl_base + 0x34) +#define flash_addr_spi200_flash_speed (flash_addr_ctrl_base + 0x40) +#define flash_addr_spi200_bt_num (flash_addr_ctrl_base + 0xe8) +#define flash_data_spi200_txfifo_rst 0x00000004 +#define flash_data_spi200_rxfifo_rst 0x00000002 +#define flash_data_spi200_trans_fmt 0x00020780 +#define flash_data_spi200_trans_ctrl_1 0x42000003 +#define flash_data_spi200_trans_ctrl_2 0x47000000 +#define flash_data_spi200_trans_ctrl_3 0x67000000 +#define flash_data_spi200_trans_ctrl_4 0x610ff000 +#define flash_data_spi200_trans_ctrl_6 0x42000000 +#define flash_data_spi200_trans_ctrl_7 0x6940020f +#define flash_data_spi200_cmd_1 0x00000005 +#define flash_data_spi200_cmd_2 0x00000006 +#define flash_data_spi200_cmd_3 0x000000C7 +#define flash_data_spi200_cmd_4 0x000000D8 +#define flash_data_spi200_cmd_6 0x00000002 +#define flash_data_spi200_cmd_7 0x0000003b +#define flash_data_spi200_cmd_8 0x00000003 +/* CORE_FLASH */ + +/* CORE_SRAM */ +#define sram_adr_rawdata_addr 0x10000000 +#define sram_adr_rawdata_end 0x00000000 +#define sram_passwrd_start 0x5AA5 +#define sram_passwrd_end 0xA55A +/* CORE_SRAM */ + +enum bin_desc_map_table { + TP_CONFIG_TABLE = 0x0000000A, + FW_CID = 0x10000000, + FW_VER = 0x10000100, + CFG_VER = 0x30000000, //0x10000005, +}; + +extern uint32_t himax_dbg_reg_ary[4]; + +struct fw_operation { + uint8_t addr_osc_en[4]; + uint8_t addr_osc_pw[4]; + uint8_t flash_lock_type[1]; + uint8_t addr_program_reload_from[4]; + uint8_t addr_reload_status[4]; + uint8_t addr_reload_crc32_result[4]; + uint8_t addr_reload_addr_from[4]; + uint8_t addr_reload_addr_cmd_beat[4]; + uint8_t data_rawdata_ready_hb[1]; + uint8_t data_rawdata_ready_lb[1]; +}; + +struct flash_operation { + uint8_t addr_spi200_trans_fmt[4]; + uint8_t addr_spi200_trans_ctrl[4]; + uint8_t addr_spi200_fifo_rst[4]; + uint8_t addr_spi200_rst_status[4]; + uint8_t addr_spi200_flash_speed[4]; + uint8_t addr_spi200_cmd[4]; + uint8_t addr_spi200_addr[4]; + uint8_t addr_spi200_data[4]; + uint8_t addr_spi200_bt_num[4]; + uint8_t data_spi200_txfifo_rst[4]; + uint8_t data_spi200_rxfifo_rst[4]; + uint8_t data_spi200_trans_fmt[4]; + uint8_t data_spi200_trans_ctrl_1[4]; + uint8_t data_spi200_trans_ctrl_2[4]; + uint8_t data_spi200_trans_ctrl_3[4]; + uint8_t data_spi200_trans_ctrl_4[4]; + uint8_t data_spi200_trans_ctrl_5[4]; + uint8_t data_spi200_trans_ctrl_6[4]; + uint8_t data_spi200_trans_ctrl_7[4]; + uint8_t data_spi200_cmd_1[4]; + uint8_t data_spi200_cmd_2[4]; + uint8_t data_spi200_cmd_3[4]; + uint8_t data_spi200_cmd_4[4]; + uint8_t data_spi200_cmd_5[4]; + uint8_t data_spi200_cmd_6[4]; + uint8_t data_spi200_cmd_7[4]; + uint8_t data_spi200_cmd_8[4]; +}; + +struct sram_operation { + uint8_t addr_rawdata_addr[4]; + uint8_t addr_rawdata_end[4]; + uint8_t passwrd_start[2]; + uint8_t passwrd_end[2]; +}; + +struct himax_core_command_operation { + struct fw_operation *fw_op; + struct flash_operation *flash_op; + struct sram_operation *sram_op; +}; + +struct himax_chip_ops { + bool (*detect)(struct himax_ts_data *ts); +}; + +struct himax_chip_entry { + struct himax_chip_ops ops; + struct list_head list; +}; + +#define DECLARE_HIMAX_CHIP(_name, _detect) \ + struct himax_chip_entry _name = { \ + .ops = { \ + .detect = _detect, \ + }, \ + .list = LIST_HEAD_INIT((_name).list), \ + } + +#endif + +#define Flash_list \ + { \ + { 0xEF, 0x30, 0x12 }, { 0xEF, 0x60, 0x12 }, { 0xC8, 0x60, 0x13 }, \ + { 0xC8, 0x60, 0x12 }, { 0xC2, 0x28, 0x11 }, { 0xC2, 0x28, 0x12 }, \ + { 0xC2, 0x25, 0x32 }, { 0x85, 0x60, 0x13 }, { 0x85, 0x60, 0x12 }, \ + { 0x85, 0x40, 0x12 }, { 0x7F, 0x11, 0x52 }, { 0x5E, 0x60, 0x13 }, \ + { 0x1C, 0x38, 0x13 }, { 0x1C, 0x38, 0x12 }, { 0x9D, 0x40, 0x12 } \ + } diff --git a/drivers/input/touchscreen/hxchipset/himax_ic_incell_core.c b/drivers/input/touchscreen/hxchipset/himax_ic_incell_core.c new file mode 100644 index 000000000000..13cf07fa674d --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/himax_ic_incell_core.c @@ -0,0 +1,2496 @@ +/* Himax Android Driver Sample Code for incell ic core functions + * + * Copyright (C) 2021 Himax Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "himax.h" +#include "himax_ic_core.h" + +uint32_t himax_dbg_reg_ary[4] = { addr_fw_dbg_msg_addr, addr_cs_central_state, addr_chk_dd_status, + addr_flag_reset_event }; + +static void himax_mcu_burst_enable(struct himax_ts_data *ts, uint8_t auto_add_4_byte) +{ + uint8_t tmp_data[DATA_LEN_4]; + int ret; + + /*I("%s,Entering\n", __func__);*/ + + tmp_data[0] = (para_AHB_INC4 | auto_add_4_byte); + + ret = himax_bus_write(ts, addr_AHB_INC4, tmp_data, 1, HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } +} + +int himax_mcu_register_read(struct himax_ts_data *ts, uint8_t *read_addr, uint32_t read_length, + uint8_t *read_data, uint8_t cfg_flag) +{ + uint8_t tmp_data[DATA_LEN_4]; + int i = 0; + int address = 0; + int ret = 0; + + /*I("%s,Entering\n",__func__);*/ + + if (cfg_flag == false) { + if (read_length > FLASH_RW_MAX_LEN) { + E("%s: read len over %d!\n", __func__, FLASH_RW_MAX_LEN); + return LENGTH_FAIL; + } + + if (read_length > DATA_LEN_4) + ts->core_fp.fp_burst_enable(ts, 1); + else + ts->core_fp.fp_burst_enable(ts, 0); + + address = (read_addr[3] << 24) + (read_addr[2] << 16) + (read_addr[1] << 8) + + read_addr[0]; + i = address; + tmp_data[0] = (uint8_t)i; + tmp_data[1] = (uint8_t)(i >> 8); + tmp_data[2] = (uint8_t)(i >> 16); + tmp_data[3] = (uint8_t)(i >> 24); + + ret = himax_bus_write(ts, addr_AHB_address_byte_0, tmp_data, DATA_LEN_4, + HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + tmp_data[0] = para_AHB_access_direction_read; + + ret = himax_bus_write(ts, addr_AHB_access_direction, tmp_data, 1, + HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + ret = himax_bus_read(ts, addr_AHB_rdata_byte_0, read_data, read_length, + HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (read_length > DATA_LEN_4) + ts->core_fp.fp_burst_enable(ts, 0); + + } else { + ret = himax_bus_read(ts, read_addr[0], read_data, read_length, + HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } + return NO_ERR; +} +EXPORT_SYMBOL_GPL(himax_mcu_register_read); + +static int himax_mcu_flash_write_burst_lenth(struct himax_ts_data *ts, uint8_t *reg_byte, + uint8_t *write_data, uint32_t length) +{ + uint8_t *data_byte; + int ret = 0; + + if (!ts->internal_buffer) { + E("%s: internal buffer not initialized!\n", __func__); + return MEM_ALLOC_FAIL; + } + data_byte = ts->internal_buffer; + + /* assign addr 4bytes */ + memcpy(data_byte, reg_byte, ADDR_LEN_4); + /* assign data n bytes */ + memcpy(data_byte + ADDR_LEN_4, write_data, length); + + ret = himax_bus_write(ts, addr_AHB_address_byte_0, data_byte, length + ADDR_LEN_4, + HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: xfer fail!\n", __func__); + return I2C_FAIL; + } + + return NO_ERR; +} + +int himax_mcu_register_write(struct himax_ts_data *ts, uint8_t *write_addr, uint32_t write_length, + uint8_t *write_data, uint8_t cfg_flag) +{ + int address; + uint8_t tmp_addr[4]; + uint8_t *tmp_data; + int total_read_times = 0; + uint32_t max_bus_size = MAX_I2C_TRANS_SZ; + uint32_t total_size_temp = 0; + unsigned int i = 0; + int ret = 0; + + /*I("%s,Entering\n", __func__);*/ + if (cfg_flag == 0) { + total_size_temp = write_length; + + tmp_addr[3] = write_addr[3]; + tmp_addr[2] = write_addr[2]; + tmp_addr[1] = write_addr[1]; + tmp_addr[0] = write_addr[0]; + + if (total_size_temp % max_bus_size == 0) + total_read_times = total_size_temp / max_bus_size; + else + total_read_times = total_size_temp / max_bus_size + 1; + + if (write_length > DATA_LEN_4) + ts->core_fp.fp_burst_enable(ts, 1); + else + ts->core_fp.fp_burst_enable(ts, 0); + + for (i = 0; i < (total_read_times); i++) { + /* I("[log]write %d time start!\n", i); + * I("[log]addr[3]=0x%02X, addr[2]=0x%02X, + addr[1]=0x%02X, addr[0]=0x%02X!\n", + tmp_addr[3], tmp_addr[2], + tmp_addr[1], tmp_addr[0]); + * I("%s, write addr = 0x%02X%02X%02X%02X\n", + __func__, tmp_addr[3], tmp_addr[2], + tmp_addr[1], tmp_addr[0]); + */ + + if (total_size_temp >= max_bus_size) { + tmp_data = write_data + (i * max_bus_size); + + ret = himax_mcu_flash_write_burst_lenth(ts, tmp_addr, tmp_data, + max_bus_size); + if (ret < 0) { + I("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + total_size_temp = total_size_temp - max_bus_size; + } else { + tmp_data = write_data + (i * max_bus_size); + /* I("last total_size_temp=%d\n", + * total_size_temp % max_bus_size); + */ + ret = himax_mcu_flash_write_burst_lenth(ts, tmp_addr, tmp_data, + total_size_temp); + if (ret < 0) { + I("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } + + /*I("[log]write %d time end!\n", i);*/ + address = ((i + 1) * max_bus_size); + tmp_addr[0] = write_addr[0] + (uint8_t)((address)&0x00FF); + + if (tmp_addr[0] < write_addr[0]) + tmp_addr[1] = + write_addr[1] + (uint8_t)((address >> 8) & 0x00FF) + 1; + else + tmp_addr[1] = write_addr[1] + (uint8_t)((address >> 8) & 0x00FF); + + udelay(100); + } + } else if (cfg_flag == 1) { + ret = himax_bus_write(ts, write_addr[0], write_data, write_length, + HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } else + E("%s: cfg_flag = %d, value is wrong!\n", __func__, cfg_flag); + + return NO_ERR; +} +EXPORT_SYMBOL_GPL(himax_mcu_register_write); + +static int himax_write_read_reg(struct himax_ts_data *ts, uint8_t *tmp_addr, uint8_t *tmp_data, + uint8_t hb, uint8_t lb) +{ + int cnt = 0; + + do { + himax_mcu_register_write(ts, tmp_addr, DATA_LEN_4, tmp_data, 0); + usleep_range(10000, 11000); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, 0); + /* I("%s:Now tmp_data[0]=0x%02X,[1]=0x%02X, + * [2]=0x%02X,[3]=0x%02X\n", + * __func__, tmp_data[0], + * tmp_data[1], tmp_data[2], tmp_data[3]); + */ + } while ((tmp_data[1] != hb && tmp_data[0] != lb) && cnt++ < 100); + + if (cnt >= 100) + return HX_RW_REG_FAIL; + + I("Now register 0x%08X : high byte=0x%02X,low byte=0x%02X\n", tmp_addr[3], tmp_data[1], + tmp_data[0]); + return NO_ERR; +} + +static void himax_mcu_interface_on(struct himax_ts_data *ts) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_data2[DATA_LEN_4]; + int cnt = 0; + int ret = 0; + + /* Read a dummy register to wake up I2C.*/ + ret = himax_bus_read(ts, addr_AHB_rdata_byte_0, tmp_data, DATA_LEN_4, + HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { /* to knock I2C*/ + E("%s: i2c access fail!\n", __func__); + return; + } + + do { + tmp_data[0] = para_AHB_continous; + + ret = himax_bus_write(ts, addr_AHB_continous, tmp_data, 1, HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = para_AHB_INC4; + + ret = himax_bus_write(ts, addr_AHB_INC4, tmp_data, 1, HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + /*Check cmd*/ + himax_bus_read(ts, addr_AHB_continous, tmp_data, 1, HIMAX_I2C_RETRY_TIMES); + himax_bus_read(ts, addr_AHB_INC4, tmp_data2, 1, HIMAX_I2C_RETRY_TIMES); + + if (tmp_data[0] == para_AHB_continous && tmp_data2[0] == para_AHB_INC4) + break; + + usleep_range(1000, 1100); + } while (++cnt < 10); + + if (cnt > 0) + I("%s:Polling burst mode: %d times\n", __func__, cnt); +} + +#define WIP_PRT_LOG "%s: retry:%d, bf[0]=%d, bf[1]=%d,bf[2]=%d, bf[3]=%d\n" +static bool himax_mcu_wait_wip(struct himax_ts_data *ts, int Timing) +{ + uint8_t tmp_data[DATA_LEN_4]; + int retry_cnt = 0; + + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_trans_fmt, 0); + tmp_data[0] = 0x01; + + do { + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_trans_ctrl, + DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_1, 0); + + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_cmd, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_cmd_1, 0); + tmp_data[0] = tmp_data[1] = tmp_data[2] = tmp_data[3] = 0xFF; + himax_mcu_register_read(ts, ts->ic_incell.pflash_op->addr_spi200_data, DATA_LEN_4, + tmp_data, 0); + + if ((tmp_data[0] & 0x01) == 0x00) + return true; + + retry_cnt++; + + if (tmp_data[0] != 0x00 || tmp_data[1] != 0x00 || tmp_data[2] != 0x00 || + tmp_data[3] != 0x00) + I(WIP_PRT_LOG, __func__, retry_cnt, tmp_data[0], tmp_data[1], tmp_data[2], + tmp_data[3]); + + if (retry_cnt > 100) { + E("%s: Wait wip error!\n", __func__); + return false; + } + + msleep(Timing); + } while ((tmp_data[0] & 0x01) == 0x01); + + return true; +} + +static void himax_mcu_sense_on(struct himax_ts_data *ts, uint8_t FlashMode) +{ + /*Overwrite*/ + return; +} + +static bool himax_mcu_sense_off(struct himax_ts_data *ts, bool check_en) +{ + /*Overwrite*/ + return true; +} + +/*power saving level*/ +static void himax_mcu_init_psl(struct himax_ts_data *ts) +{ + uint8_t addr[DATA_LEN_4] = { 0 }; + uint8_t data[DATA_LEN_4] = { 0 }; + himax_parse_assign_cmd(addr_psl, addr, sizeof(addr)); + himax_parse_assign_cmd(data_clear, data, sizeof(data)); + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + I("%s: power saving level reset OK!\n", __func__); +} + +static void himax_mcu_resume_ic_action(struct himax_ts_data *ts) +{ + /* Nothing to do */ +} + +static void himax_mcu_suspend_ic_action(struct himax_ts_data *ts) +{ + /* Nothing to do */ +} + +static void himax_mcu_power_on_init(struct himax_ts_data *ts) +{ + uint8_t addr[DATA_LEN_4] = { 0 }; + uint8_t data[DATA_LEN_4] = { 0 }; + himax_parse_assign_cmd(data_clear, data, sizeof(data)); + + ts->core_fp.fp_touch_information(ts); + /*RawOut select initial*/ + himax_parse_assign_cmd(addr_raw_out_sel, addr, sizeof(addr)); + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + /*DSRAM func initial*/ + ts->core_fp.fp_assign_sorting_mode(ts, data); + /* N frame initial : reset N frame back to default value 1 for normal mode */ + himax_parse_assign_cmd(addr_set_frame_addr, addr, sizeof(addr)); + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + /*FW reload done initial*/ + himax_parse_assign_cmd(addr_fw_define_2nd_flash_reload, addr, sizeof(addr)); + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + ts->core_fp.fp_sense_on(ts, 0x00); + + I("%s: waiting for FW reload data\n", __func__); +} + +static bool himax_mcu_dd_clk_set(struct himax_ts_data *ts, bool enable) +{ + /*Overwrite*/ + return true; +} + +static void himax_mcu_dd_reg_en(struct himax_ts_data *ts, bool enable) +{ + /*Overwrite*/ + return; +} + +static bool himax_mcu_dd_reg_write(struct himax_ts_data *ts, uint8_t addr, uint8_t pa_num, int len, + uint8_t *data, uint8_t bank) +{ + /*Calculate total write length*/ + uint32_t data_len = (((len + pa_num - 1) / 4 - pa_num / 4) + 1) * 4; + uint8_t *w_data; + uint8_t tmp_addr[4] = { 0 }; + uint8_t tmp_data[4] = { 0 }; + bool *chk_data; + uint32_t chk_idx = 0; + int i = 0; + int ret = 0; + + w_data = kmalloc(sizeof(uint8_t) * data_len, GFP_KERNEL); + if (w_data == NULL) { + return false; + } + + chk_data = kcalloc(data_len, sizeof(bool), GFP_KERNEL); + if (chk_data == NULL) { + E("%s Allocate chk buf failed\n", __func__); + kfree(w_data); + return false; + } + + memset(w_data, 0, data_len * sizeof(uint8_t)); + + /*put input data*/ + chk_idx = pa_num % 4; + for (i = 0; i < len; i++) { + w_data[chk_idx] = data[i]; + chk_data[chk_idx++] = true; + } + + /*get original data*/ + chk_idx = (pa_num / 4) * 4; + for (i = 0; i < data_len; i++) { + if (!chk_data[i]) { + ts->core_fp.fp_dd_reg_read(ts, addr, (uint8_t)(chk_idx + i), 1, tmp_data, + bank); + + w_data[i] = tmp_data[0]; + chk_data[i] = true; + } + D("%s w_data[%d] = %2X\n", __func__, i, w_data[i]); + } + + tmp_addr[3] = 0x30; + tmp_addr[2] = addr >> 4; + tmp_addr[1] = (addr << 4) | (bank * 4); + tmp_addr[0] = chk_idx; + D("%s Addr = %02X%02X%02X%02X.\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], + tmp_addr[0]); + kfree(chk_data); + + ret = (himax_mcu_register_write(ts, tmp_addr, data_len, w_data, 0) == NO_ERR); + kfree(w_data); + return ret ? false : true; +} + +static bool himax_mcu_dd_reg_read(struct himax_ts_data *ts, uint8_t addr, uint8_t pa_num, int len, + uint8_t *data, uint8_t bank) +{ + uint8_t tmp_addr[4] = { 0 }; + uint8_t tmp_data[4] = { 0 }; + int i = 0; + + for (i = 0; i < len; i++) { + tmp_addr[3] = 0x30; + tmp_addr[2] = addr >> 4; + tmp_addr[1] = (addr << 4) | (bank * 4); + tmp_addr[0] = pa_num + i; + + if (himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, 0)) + goto READ_FAIL; + + data[i] = tmp_data[0]; + + D("%s Addr = %02X%02X%02X%02X.data = %2X\n", __func__, tmp_addr[3], tmp_addr[2], + tmp_addr[1], tmp_addr[0], data[i]); + } + return true; + +READ_FAIL: + E("%s Read DD reg Failed.\n", __func__); + return false; +} + +/* CORE_FW */ +/* FW side start*/ +static void diag_mcu_parse_raw_data(struct himax_ts_data *ts, + struct himax_report_data *hx_touch_data, int mul_num, + int self_num, uint8_t diag_cmd, int32_t *mutual_data, + int32_t *self_data) +{ + int RawDataLen_word; + int index = 0; + int temp1, temp2, i; + + if (hx_touch_data->hx_rawdata_buf[0] == ts->ic_incell.pfw_op->data_rawdata_ready_lb[0] && + hx_touch_data->hx_rawdata_buf[1] == ts->ic_incell.pfw_op->data_rawdata_ready_hb[0] && + hx_touch_data->hx_rawdata_buf[2] > 0 && hx_touch_data->hx_rawdata_buf[3] == diag_cmd) { + RawDataLen_word = hx_touch_data->rawdata_size / 2; + index = (hx_touch_data->hx_rawdata_buf[2] - 1) * RawDataLen_word; + + /* I("Header[%d]: %x, %x, %x, %x, mutual: %d, self: %d\n",index, + * buf[56], buf[57], buf[58], buf[59], mul_num, self_num); + * I("RawDataLen=%d , RawDataLen_word=%d , + * hx_touch_info_size=%d\n", + * RawDataLen, RawDataLen_word, hx_touch_info_size); + */ + for (i = 0; i < RawDataLen_word; i++) { + temp1 = index + i; + + if (temp1 < mul_num) { /*mutual*/ + mutual_data[index + i] = + ((int8_t)hx_touch_data->hx_rawdata_buf[i * 2 + 4 + 1]) * + 256 + + hx_touch_data->hx_rawdata_buf[i * 2 + 4]; + } else { /*self*/ + temp1 = i + index; + temp2 = self_num + mul_num; + + if (temp1 >= temp2) + break; + + self_data[i + index - mul_num] = + (((int8_t)hx_touch_data->hx_rawdata_buf[i * 2 + 4 + 1]) + << 8) + + hx_touch_data->hx_rawdata_buf[i * 2 + 4]; + } + } + } +} + +/*------------------------------------------------------------------------- +* +* Create: Unknown +* +* Using: Do software reset by setting addr 0x90000018 register with value +* 0x55, then read dummy 4 bytes. +* +* param: None +* +* Dependency function: None +* +*/ +static void himax_mcu_system_reset(struct himax_ts_data *ts) +{ + uint8_t addr[DATA_LEN_4] = { 0 }; + uint8_t data[DATA_LEN_4] = { 0 }; +#if defined(HIMAX_I2C_PLATFORM) + int ret = 0; +#endif + himax_parse_assign_cmd(addr_system_reset, addr, sizeof(addr)); + himax_parse_assign_cmd(data_system_reset, data, sizeof(data)); + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + msleep(280); + +#if defined(HIMAX_I2C_PLATFORM) + ret = himax_bus_read(ts, addr_AHB_rdata_byte_0, data, DATA_LEN_4, HIMAX_I2C_RETRY_TIMES); + if (ret < 0) { + E("%s: i2c access fail!\n", __func__); + } +#endif +} + +static int himax_mcu_Calculate_CRC_with_AP(struct himax_ts_data *ts, unsigned char *FW_content, + int CRC_from_FW, int len) +{ + int i, j, length = 0; + int fw_data; + int fw_data_2; + int CRC = 0xFFFFFFFF; + int PolyNomial = 0x82F63B78; + + length = len / 4; + + for (i = 0; i < length; i++) { + fw_data = FW_content[i * 4]; + + for (j = 1; j < 4; j++) { + fw_data_2 = FW_content[i * 4 + j]; + fw_data += (fw_data_2) << (8 * j); + } + CRC = fw_data ^ CRC; + for (j = 0; j < 32; j++) { + if ((CRC % 2) != 0) + CRC = ((CRC >> 1) & 0x7FFFFFFF) ^ PolyNomial; + else + CRC = (((CRC >> 1) & 0x7FFFFFFF)); + } + } + + return CRC; +} + +static uint32_t himax_mcu_check_CRC(struct himax_ts_data *ts, uint8_t *start_addr, + int reload_length) +{ + uint32_t result = 0; + uint8_t tmp_data[DATA_LEN_4]; + int cnt = 0, ret = 0; + int length = reload_length / DATA_LEN_4; + + ret = himax_mcu_register_write(ts, ts->ic_incell.pfw_op->addr_reload_addr_from, DATA_LEN_4, + start_addr, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + + tmp_data[3] = 0x00; + tmp_data[2] = 0x99; + tmp_data[1] = (length >> 8); + tmp_data[0] = length; + ret = himax_mcu_register_write(ts, ts->ic_incell.pfw_op->addr_reload_addr_cmd_beat, + DATA_LEN_4, tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + cnt = 0; + + do { + ret = himax_mcu_register_read(ts, ts->ic_incell.pfw_op->addr_reload_status, + DATA_LEN_4, tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + + if ((tmp_data[0] & 0x01) != 0x01) { + ret = himax_mcu_register_read( + ts, ts->ic_incell.pfw_op->addr_reload_crc32_result, DATA_LEN_4, + tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + I("%s:data[3]=%X,data[2]=%X,data[1]=%X,data[0]=%X\n", __func__, tmp_data[3], + tmp_data[2], tmp_data[1], tmp_data[0]); + result = ((tmp_data[3] << 24) + (tmp_data[2] << 16) + (tmp_data[1] << 8) + + tmp_data[0]); + goto END; + } else { + I("Waiting for HW ready!\n"); + usleep_range(1000, 1100); + if (cnt >= 100) + ts->core_fp.fp_read_FW_status(ts); + } + + } while (cnt++ < 100); +END: + return result; +} + +#define PRT_DATA "%s:[3]=0x%2X, [2]=0x%2X, [1]=0x%2X, [0]=0x%2X\n" +static void himax_mcu_diag_register_set(struct himax_ts_data *ts, uint8_t diag_command, + uint8_t storage_type, bool is_dirly) +{ + uint8_t addr[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t cnt = 50; + + if (diag_command > 0 && storage_type % 8 > 0 && !is_dirly) + tmp_data[0] = diag_command + 0x08; + else + tmp_data[0] = diag_command; + I("diag_command = %d, tmp_data[0] = %X\n", diag_command, tmp_data[0]); + ts->core_fp.fp_interface_on(ts); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + himax_parse_assign_cmd(addr_raw_out_sel, addr, sizeof(addr)); + do { + himax_mcu_register_write(ts, addr, DATA_LEN_4, tmp_data, 0); + himax_mcu_register_read(ts, addr, DATA_LEN_4, back_data, 0); + I(PRT_DATA, __func__, back_data[3], back_data[2], back_data[1], back_data[0]); + cnt--; + } while (tmp_data[0] != back_data[0] && cnt > 0); +} + +static int himax_mcu_chip_self_test(struct himax_ts_data *ts, struct seq_file *s, void *v) +{ + /*Overwrite*/ + return 0x00; +} + +#define PRT_TMP_DATA "%s:[0]=0x%2X,[1]=0x%2X, [2]=0x%2X,[3]=0x%2X\n" +static void himax_mcu_reload_disable(struct himax_ts_data *ts, int disable) +{ + uint8_t addr[DATA_LEN_4] = { 0 }; + uint8_t data[DATA_LEN_4] = { 0 }; + + I("%s:entering\n", __func__); + himax_parse_assign_cmd(addr_fw_define_flash_reload, addr, sizeof(addr)); + + if (disable) { /*reload disable*/ + himax_parse_assign_cmd(data_fw_define_flash_reload_dis, data, sizeof(data)); + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + } else { /*reload enable*/ + himax_parse_assign_cmd(data_fw_define_flash_reload_en, data, sizeof(data)); + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + } + + I("%s: setting OK!\n", __func__); +} + +static int himax_mcu_read_ic_trigger_type(struct himax_ts_data *ts) +{ + uint8_t addr[DATA_LEN_4] = { 0 }; + uint8_t data[DATA_LEN_4] = { 0 }; + int trigger_type = false; + himax_parse_assign_cmd(addr_fw_define_int_is_edge, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, DATA_LEN_4, data, 0); + + if ((data[1] & 0x01) == 1) + trigger_type = true; + + return trigger_type; +} + +static void himax_mcu_read_FW_ver(struct himax_ts_data *ts) +{ + uint8_t addr[DATA_LEN_4] = { 0 }; + uint8_t data[12] = { 0 }; + uint8_t data_2[DATA_LEN_4] = { 0 }; + uint8_t retry = 0; + uint8_t reload_status = 0; + + I("%s: waiting for FW reload done\n", __func__); + + while (reload_status == 0) { + himax_parse_assign_cmd(addr_fw_define_flash_reload, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, DATA_LEN_4, data, 0); + himax_parse_assign_cmd(addr_fw_define_2nd_flash_reload, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, DATA_LEN_4, data_2, 0); + + if ((data[2] == 0x9A && data[3] == 0xA9) || + (data_2[1] == 0x72 && data_2[0] == 0xC0)) { + I("%s: FW finish reload done\n", __func__); + reload_status = 1; + break; + } else if (retry == 200) { + E("%s: FW fail reload done !!!!!\n", __func__); + ts->core_fp.fp_read_FW_status(ts); + ts->ic_data->vendor_panel_ver = 0; + ts->ic_data->vendor_arch_ver = 0; + ts->ic_data->vendor_config_ver = 0; + ts->ic_data->vendor_touch_cfg_ver = 0; + ts->ic_data->vendor_display_cfg_ver = 0; + ts->ic_data->vendor_cid_maj_ver = 0; + ts->ic_data->vendor_cid_min_ver = 0; + goto END; + } else { + retry++; + usleep_range(10000, 11000); + I("%s: wait reload done %d times\n", __func__, retry); + } + } + + I("%s:data[2]=0x%2.2X,data[3]=0x%2.2X\n", __func__, data[2], data[3]); + I("%s:data_2[0]=0x%2.2X,data_2[1]=0x%2.2X,reload_status=%d\n", __func__, data_2[0], + data_2[1], reload_status); + /** + * Read FW version + */ + himax_parse_assign_cmd(addr_fw_architecture_version, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, DATA_LEN_4, data, 0); + ts->ic_data->vendor_panel_ver = data[0]; + ts->ic_data->vendor_arch_ver = data[1] << 8 | data[2]; + + himax_parse_assign_cmd(addr_fw_config_version, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, DATA_LEN_4, data, 0); + ts->ic_data->vendor_config_ver = data[2] << 8 | data[3]; + /*I("CFG_VER : %X\n",ts->ic_data->vendor_config_ver);*/ + ts->ic_data->vendor_touch_cfg_ver = data[2]; + ts->ic_data->vendor_display_cfg_ver = data[3]; + + himax_parse_assign_cmd(addr_fw_CID, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, DATA_LEN_4, data, 0); + ts->ic_data->vendor_cid_maj_ver = data[2]; + ts->ic_data->vendor_cid_min_ver = data[3]; + + himax_parse_assign_cmd(addr_fw_customer, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, 12, data, 0); + memcpy(ts->ic_data->vendor_cus_info, data, 12); + + himax_parse_assign_cmd(addr_fw_project_name, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, 12, data, 0); + memcpy(ts->ic_data->vendor_proj_info, data, 12); + + himax_parse_assign_cmd(addr_fw_config_date, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, 12, data, 0); + memcpy(ts->ic_data->vendor_config_date, data, 12); + + I("Architecture Version : %X\n", ts->ic_data->vendor_arch_ver); + I("CID : %04X\n", (ts->ic_data->vendor_cid_maj_ver << 8 | ts->ic_data->vendor_cid_min_ver)); + I("FW Display Config. Version : %X\n", ts->ic_data->vendor_display_cfg_ver); + I("FW Touch Config. Version : %X\n", ts->ic_data->vendor_touch_cfg_ver); + I("Panel Version : %X\n", ts->ic_data->vendor_panel_ver); + I("Config. Date = %s\n", ts->ic_data->vendor_config_date); + I("Project Name = %s\n", ts->ic_data->vendor_proj_info); + I("Cusomer = %s\n", ts->ic_data->vendor_cus_info); + +END: + return; +} + +static bool himax_mcu_read_event_stack(struct himax_ts_data *ts, uint8_t *buf, uint8_t length) +{ + struct timespec64 t_start, t_end, t_delta; + int len = length; + int i2c_speed = 0; + + if (ts->debug_log_level & BIT(2)) { + ktime_get_real_ts64(&t_start); + + himax_bus_read(ts, addr_read_event_stack, buf, length, HIMAX_I2C_RETRY_TIMES); + + ktime_get_real_ts64(&t_end); + t_delta.tv_nsec = (t_end.tv_sec * 1000000000 + t_end.tv_nsec) - + (t_start.tv_sec * 1000000000 + t_start.tv_nsec); + + i2c_speed = (len * 9 * 1000000 / (int)t_delta.tv_nsec) * 13 / 10; + ts->bus_speed = (int)i2c_speed; + } else { + himax_bus_read(ts, addr_read_event_stack, buf, length, HIMAX_I2C_RETRY_TIMES); + } + + return 1; +} + +static void himax_mcu_return_event_stack(struct himax_ts_data *ts) +{ + int retry = 20, i; + uint8_t tmp_data[DATA_LEN_4]; + + I("%s:entering\n", __func__); + + do { + I("now %d times!\n", retry); + + for (i = 0; i < DATA_LEN_4; i++) + tmp_data[i] = ts->psram_op->addr_rawdata_end[i]; + + himax_mcu_register_write(ts, ts->psram_op->addr_rawdata_addr, DATA_LEN_4, tmp_data, + 0); + himax_mcu_register_read(ts, ts->psram_op->addr_rawdata_addr, DATA_LEN_4, tmp_data, + 0); + retry--; + usleep_range(10000, 11000); + } while ((tmp_data[1] != ts->psram_op->addr_rawdata_end[1] && + tmp_data[0] != ts->psram_op->addr_rawdata_end[0]) && + retry > 0); + + I("%s: End of setting!\n", __func__); +} + +static bool himax_mcu_calculateChecksum(struct himax_ts_data *ts, bool change_iref, uint32_t size) +{ + uint8_t CRC_result = 0, i; + uint8_t tmp_data[DATA_LEN_4]; + + I("%s:Now size=%d\n", __func__, size); + for (i = 0; i < DATA_LEN_4; i++) + tmp_data[i] = ts->psram_op->addr_rawdata_end[i]; + + CRC_result = ts->core_fp.fp_check_CRC(ts, tmp_data, size); + msleep(50); + + if (CRC_result != 0) + I("%s: CRC Fail=%d\n", __func__, CRC_result); + + return (CRC_result == 0) ? true : false; +} + +static void himax_mcu_read_FW_status(struct himax_ts_data *ts) +{ + uint8_t len = 0; + uint8_t i = 0; + uint8_t addr[4] = { 0 }; + uint8_t data[4] = { 0 }; + + len = (uint8_t)(sizeof(himax_dbg_reg_ary) / sizeof(uint32_t)); + + for (i = 0; i < len; i++) { + himax_parse_assign_cmd(himax_dbg_reg_ary[i], addr, 4); + himax_mcu_register_read(ts, addr, DATA_LEN_4, data, 0); + + I("reg[0-3] : 0x%08X = 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", himax_dbg_reg_ary[i], data[0], + data[1], data[2], data[3]); + } +} + +static void himax_mcu_irq_switch(struct himax_ts_data *ts, int switch_on) +{ + if (switch_on) { + if (ts->use_irq) + himax_int_enable(ts, switch_on); + else + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + + } else { + if (ts->use_irq) + himax_int_enable(ts, switch_on); + else { + hrtimer_cancel(&ts->timer); + cancel_work_sync(&ts->work); + } + } +} + +static int himax_mcu_assign_sorting_mode(struct himax_ts_data *ts, uint8_t *tmp_data) +{ + uint8_t addr[DATA_LEN_4] = { 0 }; + himax_parse_assign_cmd(addr_sorting_mode_en, addr, sizeof(addr)); + I("%s:Now data[3]=0x%2X,data[2]=0x%2X,data[1]=0x%2X,data[0]=0x%2X\n", __func__, tmp_data[3], + tmp_data[2], tmp_data[1], tmp_data[0]); + + himax_mcu_register_write(ts, addr, DATA_LEN_4, tmp_data, 0); + + return NO_ERR; +} + +static int himax_mcu_check_sorting_mode(struct himax_ts_data *ts, uint8_t *tmp_data) +{ + uint8_t addr[DATA_LEN_4] = { 0 }; + himax_parse_assign_cmd(addr_sorting_mode_en, addr, sizeof(addr)); + + himax_mcu_register_read(ts, addr, DATA_LEN_4, tmp_data, 0); + //I("%s: tmp_data[0]=%x,tmp_data[1]=%x\n", __func__, + // tmp_data[0], tmp_data[1]); + + return NO_ERR; +} + +/* FW side end*/ +/* CORE_FW */ + +/* CORE_FLASH */ +/* FLASH side start*/ +static void himax_mcu_chip_erase(struct himax_ts_data *ts) +{ + ts->core_fp.fp_interface_on(ts); + + /* Reset power saving level */ + if (ts->core_fp.fp_init_psl != NULL) + ts->core_fp.fp_init_psl(ts); + + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_trans_fmt, 0); + + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_2, 0); + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_cmd, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_cmd_2, 0); + + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_cmd, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_cmd_3, 0); + msleep(2000); + + if (!ts->core_fp.fp_wait_wip(ts, 100)) + E("%s: Chip_Erase Fail\n", __func__); +} + +static bool himax_mcu_block_erase(struct himax_ts_data *ts, int start_addr, int length) +{ + uint32_t page_prog_start = 0; + uint32_t block_size = 0x10000; + + uint8_t tmp_data[4] = { 0 }; + + ts->core_fp.fp_interface_on(ts); + + ts->core_fp.fp_init_psl(ts); + + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_trans_fmt, 0); + + for (page_prog_start = start_addr; page_prog_start < start_addr + length; + page_prog_start = page_prog_start + block_size) { + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_trans_ctrl, + DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_2, 0); + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_cmd, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_cmd_2, 0); + + tmp_data[3] = (page_prog_start >> 24) & 0xFF; + tmp_data[2] = (page_prog_start >> 16) & 0xFF; + tmp_data[1] = (page_prog_start >> 8) & 0xFF; + tmp_data[0] = page_prog_start & 0xFF; + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_addr, DATA_LEN_4, + tmp_data, 0); + + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_trans_ctrl, + DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_3, 0); + himax_mcu_register_write(ts, ts->ic_incell.pflash_op->addr_spi200_cmd, DATA_LEN_4, + ts->ic_incell.pflash_op->data_spi200_cmd_4, 0); + msleep(1000); + + if (!ts->core_fp.fp_wait_wip(ts, 100)) { + E("%s:Erase Fail\n", __func__); + return false; + } + } + + I("%s:END\n", __func__); + return true; +} + +static bool himax_mcu_sector_erase(struct himax_ts_data *ts, int start_addr, int length) +{ + uint8_t addr[4]; + uint8_t data[4]; + uint32_t page_prog_start = 0; + uint32_t sector_size = 0x1000; + + ts->core_fp.fp_init_psl(ts); + + /*===================================== + SPI Transfer Format : 0x8000_0010 ==> 0x0002_0780 + =====================================*/ + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x10; + data[3] = 0x00; + data[2] = 0x02; + data[1] = 0x07; + data[0] = 0x80; + + himax_mcu_register_write(ts, addr, 4, data, 0); + + for (page_prog_start = start_addr; page_prog_start < start_addr + length; + page_prog_start = page_prog_start + sector_size) { + /*===================================== + Write Enable : 1. 0x8000_0020 ==> 0x4700_0000 //control + 2. 0x8000_0024 ==> 0x0000_0006 //WREN + =====================================*/ + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x20; + data[3] = 0x47; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x00; + himax_mcu_register_write(ts, addr, 4, data, 0); + + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x24; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x06; + himax_mcu_register_write(ts, addr, 4, data, 0); + + /*===================================== + Sector Erase + Erase Command : 0x8000_0028 ==> 0x0000_0000 //SPI addr + 0x8000_0020 ==> 0x6700_0000 //control + 0x8000_0024 ==> 0x0000_0020 //SE + =====================================*/ + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x28; + data[3] = (uint8_t)(page_prog_start >> 24); + data[2] = (uint8_t)(page_prog_start >> 16); + data[1] = (uint8_t)(page_prog_start >> 8); + data[0] = (uint8_t)page_prog_start; + himax_mcu_register_write(ts, addr, 4, data, 0); + + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x20; + data[3] = 0x67; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x00; + himax_mcu_register_write(ts, addr, 4, data, 0); + + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x24; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x20; + himax_mcu_register_write(ts, addr, 4, data, 0); + + if (!ts->core_fp.fp_wait_wip(ts, 100)) { + return false; + } + } + + I("%s:END\n", __func__); + return true; +} + +static void himax_mcu_flash_programming(struct himax_ts_data *ts, uint8_t *FW_content, + int start_addr, int FW_Size) +{ + /*Overwrite*/ + return; +} + +static void himax_mcu_flash_page_write(struct himax_ts_data *ts, uint8_t *write_addr, int length, + uint8_t *write_data) +{ +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_64k(struct himax_ts_data *ts, + unsigned char *fw, int len, + bool change_iref) +{ + int burnFW_success = 0; + + if (len != FW_SIZE_64k) { + E("%s: The file size is not 64K bytes\n", __func__); + return false; + } + +#if defined(HX_RST_PIN_FUNC) + if (HX_SYSTEM_RESET == 0) + ts->core_fp.fp_ic_reset(ts, false, false); + else + ts->core_fp.fp_system_reset(ts); +#endif + ts->core_fp.fp_sense_off(ts, true); + ts->core_fp.fp_block_erase(ts, 0x00, FW_SIZE_64k); + ts->core_fp.fp_flash_programming(ts, fw, 0, FW_SIZE_64k); + + if (ts->core_fp.fp_check_CRC(ts, ts->ic_incell.pfw_op->addr_program_reload_from, + FW_SIZE_64k) == 0) + burnFW_success = 1; + +#if defined(HX_RST_PIN_FUNC) + if (HX_SYSTEM_RESET == 0) + ts->core_fp.fp_ic_reset(ts, false, false); + else + ts->core_fp.fp_system_reset(ts); +#endif + return burnFW_success; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_128k(struct himax_ts_data *ts, + unsigned char *fw, int len, + bool change_iref) +{ + int burnFW_success = 0; + int result = 0; + + if (len != FW_SIZE_128k) { + E("%s: The file size is not 128K bytes\n", __func__); + return false; + } + +#if defined(HX_RST_PIN_FUNC) + if (HX_SYSTEM_RESET == 0) + ts->core_fp.fp_ic_reset(ts, false, false); + else + ts->core_fp.fp_system_reset(ts); +#endif + ts->core_fp.fp_sense_off(ts, true); + + result = himax_mcu_WP_BP_disable(ts); + + if (result != 0) { + /* WP BP disable fail */ + burnFW_success = -1; + + return burnFW_success; + } + + ts->core_fp.fp_block_erase(ts, 0x00, FW_SIZE_128k); + ts->core_fp.fp_flash_programming(ts, fw, 0, FW_SIZE_128k); + + if (ts->core_fp.fp_check_CRC(ts, ts->ic_incell.pfw_op->addr_program_reload_from, + FW_SIZE_128k) == 0) + burnFW_success = 1; + + return burnFW_success; +} + +static void himax_mcu_flash_dump_func(struct himax_ts_data *ts, uint8_t local_flash_command, + int Flash_Size, uint8_t *flash_buffer) +{ + /*Overwrite*/ + return; +} + +static bool himax_mcu_flash_lastdata_check(struct himax_ts_data *ts, uint32_t size) +{ + uint8_t tmp_addr[4]; + /* 64K - 0x80, which is the address of + * the last 128bytes in 64K, default value + */ + uint32_t start_addr = 0xFFFFFFFF; + uint32_t temp_addr = 0; + uint32_t flash_page_len = 0x80; + uint8_t flash_tmp_buffer[128] = { 0 }; + + if (size < flash_page_len) { + E("%s: flash size is wrong, terminated\n", __func__); + E("%s: flash size = %08X; flash page len = %08X\n", __func__, size, flash_page_len); + goto FAIL; + } + + /* In order to match other size of fw */ + start_addr = size - flash_page_len; + I("%s: Now size is %d, the start_addr is 0x%08X\n", __func__, size, start_addr); + for (temp_addr = start_addr; temp_addr < (start_addr + flash_page_len); + temp_addr = temp_addr + flash_page_len) { + /*I("temp_addr=%d,tmp_addr[0]=0x%2X, tmp_addr[1]=0x%2X, + * tmp_addr[2]=0x%2X,tmp_addr[3]=0x%2X\n", + * temp_addr,tmp_addr[0], tmp_addr[1], + * tmp_addr[2],tmp_addr[3]); + */ + tmp_addr[0] = temp_addr % 0x100; + tmp_addr[1] = (temp_addr >> 8) % 0x100; + tmp_addr[2] = (temp_addr >> 16) % 0x100; + tmp_addr[3] = temp_addr / 0x1000000; + himax_mcu_register_read(ts, tmp_addr, flash_page_len, &flash_tmp_buffer[0], 0); + } + + I("FLASH[%08X] ~ FLASH[%08X] = %02X%02X%02X%02X\n", size - 4, size - 1, + flash_tmp_buffer[flash_page_len - 4], flash_tmp_buffer[flash_page_len - 3], + flash_tmp_buffer[flash_page_len - 2], flash_tmp_buffer[flash_page_len - 1]); + + if ((!flash_tmp_buffer[flash_page_len - 4]) && (!flash_tmp_buffer[flash_page_len - 3]) && + (!flash_tmp_buffer[flash_page_len - 2]) && (!flash_tmp_buffer[flash_page_len - 1])) { + I("Fail, Last four Bytes are all 0x00:\n"); + goto FAIL; + } else if ((flash_tmp_buffer[flash_page_len - 4] == 0xFF) && + (flash_tmp_buffer[flash_page_len - 3] == 0xFF) && + (flash_tmp_buffer[flash_page_len - 2] == 0xFF) && + (flash_tmp_buffer[flash_page_len - 1] == 0xFF)) { + I("Fail, Last four Bytes are all 0xFF:\n"); + goto FAIL; + } else { + return 0; + } + +FAIL: + return 1; +} + +static bool hx_bin_desc_data_get(struct himax_ts_data *ts, uint32_t addr, uint8_t *flash_buf) +{ + uint8_t data_sz = 0x10; + uint32_t i = 0, j = 0; + uint16_t chk_end = 0; + uint16_t chk_sum = 0; + uint32_t map_code = 0; + unsigned long flash_addr = 0; + + for (i = 0; i < FW_PAGE_SZ; i = i + data_sz) { + for (j = i; j < (i + data_sz); j++) { + chk_end |= flash_buf[j]; + chk_sum += flash_buf[j]; + } + if (!chk_end) { /*1. Check all zero*/ + I("%s: End in %X\n", __func__, i + addr); + return false; + } else if (chk_sum % 0x100) /*2. Check sum*/ + I("%s: chk sum failed in %X\n", __func__, i + addr); + else { /*3. get data*/ + map_code = flash_buf[i] + (flash_buf[i + 1] << 8) + + (flash_buf[i + 2] << 16) + (flash_buf[i + 3] << 24); + flash_addr = flash_buf[i + 4] + (flash_buf[i + 5] << 8) + + (flash_buf[i + 6] << 16) + (flash_buf[i + 7] << 24); + switch (map_code) { + case FW_CID: + ts->cid_ver_maj_flash_addr = flash_addr; + ts->cid_ver_min_flash_addr = flash_addr + 1; + I("%s: CID in %lX\n", __func__, ts->cid_ver_maj_flash_addr); + break; + case FW_VER: + ts->fw_ver_maj_flash_addr = flash_addr; + ts->fw_ver_min_flash_addr = flash_addr + 1; + I("%s: FW_VER in %lX\n", __func__, ts->fw_ver_maj_flash_addr); + break; + case CFG_VER: + ts->cfg_ver_maj_flash_addr = flash_addr; + ts->cfg_ver_min_flash_addr = flash_addr + 1; + I("%s: CFG_VER in = %lX\n", __func__, ts->cfg_ver_maj_flash_addr); + break; + case TP_CONFIG_TABLE: + ts->cfg_table_flash_addr = flash_addr; + I("%s: CONFIG_TABLE in %X\n", __func__, ts->cfg_table_flash_addr); + break; + } + } + chk_end = 0; + chk_sum = 0; + } + + return true; +} + +static bool hx_mcu_bin_desc_get(struct himax_ts_data *ts, unsigned char *fw, uint32_t max_sz) +{ + uint32_t addr_t = 0; + unsigned char *fw_buf = NULL; + bool keep_on_flag = false; + bool g_bin_desc_flag = false; + + do { + fw_buf = &fw[addr_t]; + + /*Check bin is with description table or not*/ + if (!g_bin_desc_flag) { + if (fw_buf[0x00] == 0x00 && fw_buf[0x01] == 0x00 && fw_buf[0x02] == 0x00 && + fw_buf[0x03] == 0x00 && fw_buf[0x04] == 0x00 && fw_buf[0x05] == 0x00 && + fw_buf[0x06] == 0x00 && fw_buf[0x07] == 0x00 && fw_buf[0x0E] == 0x87) + g_bin_desc_flag = true; + } + if (!g_bin_desc_flag) { + I("%s: fw_buf[0x00] = %2X, fw_buf[0x0E] = %2X\n", __func__, fw_buf[0x00], + fw_buf[0x0E]); + I("%s: No description table\n", __func__); + break; + } + + /*Get related data*/ + keep_on_flag = hx_bin_desc_data_get(ts, addr_t, fw_buf); + + addr_t = addr_t + FW_PAGE_SZ; + } while (max_sz > addr_t && keep_on_flag); + + return g_bin_desc_flag; +} + +/* FLASH side end*/ +/* CORE_FLASH */ + +/* CORE_SRAM */ +/* SRAM side start*/ +static bool himax_mcu_get_DSRAM_data(struct himax_ts_data *ts, uint8_t *info_data, bool DSRAM_Flag) +{ + unsigned int i = 0; + unsigned char tmp_addr[ADDR_LEN_4]; + unsigned char tmp_data[DATA_LEN_4]; + uint8_t max_i2c_size = MAX_I2C_TRANS_SZ; + uint8_t x_num = ts->ic_data->HX_RX_NUM; + uint8_t y_num = ts->ic_data->HX_TX_NUM; + /*int m_key_num = 0;*/ + int total_size = (x_num * y_num + x_num + y_num) * 2 + 4; + int total_data_size = (x_num * y_num + x_num + y_num) * 2; + int total_size_temp; + /*int mutual_data_size = x_num * y_num * 2;*/ + int total_read_times = 0; + int address = 0; + uint8_t *temp_info_data = NULL; /*max mkey size = 8*/ + uint16_t check_sum_cal = 0; + int fw_run_flag = -1; + + temp_info_data = kcalloc((total_size + 8), sizeof(uint8_t), GFP_KERNEL); + if (temp_info_data == NULL) { + E("%s, Failed to allocate memory\n", __func__); + return false; + } + /* 1. Read number of MKey R100070E8H to determin data size */ + /* m_key_num = ts->ic_data->HX_BT_NUM; */ + /* I("%s,m_key_num=%d\n",__func__ ,m_key_num); */ + /* total_size += m_key_num * 2; */ + /* 2. Start DSRAM Rawdata and Wait Data Ready */ + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = ts->psram_op->passwrd_start[1]; + tmp_data[0] = ts->psram_op->passwrd_start[0]; + fw_run_flag = + himax_write_read_reg(ts, ts->psram_op->addr_rawdata_addr, tmp_data, + ts->psram_op->passwrd_end[1], ts->psram_op->passwrd_end[0]); + + if (fw_run_flag < 0) { + I("%s Data NOT ready => bypass\n", __func__); + ts->core_fp.fp_read_FW_status(ts); + goto FAIL; + } + + /* 3. Read RawData */ + total_size_temp = total_size; + I("%s:data[0]=0x%2X,data[1]=0x%2X,data[2]=0x%2X,data[3]=0x%2X\n", __func__, + ts->psram_op->addr_rawdata_addr[0], ts->psram_op->addr_rawdata_addr[1], + ts->psram_op->addr_rawdata_addr[2], ts->psram_op->addr_rawdata_addr[3]); + + tmp_addr[0] = ts->psram_op->addr_rawdata_addr[0]; + tmp_addr[1] = ts->psram_op->addr_rawdata_addr[1]; + tmp_addr[2] = ts->psram_op->addr_rawdata_addr[2]; + tmp_addr[3] = ts->psram_op->addr_rawdata_addr[3]; + + if (total_size % max_i2c_size == 0) + total_read_times = total_size / max_i2c_size; + else + total_read_times = total_size / max_i2c_size + 1; + + for (i = 0; i < total_read_times; i++) { + address = (ts->psram_op->addr_rawdata_addr[3] << 24) + + (ts->psram_op->addr_rawdata_addr[2] << 16) + + (ts->psram_op->addr_rawdata_addr[1] << 8) + + ts->psram_op->addr_rawdata_addr[0] + i * max_i2c_size; + /*I("%s address = %08X\n", __func__, address);*/ + + tmp_addr[3] = (uint8_t)((address >> 24) & 0x00FF); + tmp_addr[2] = (uint8_t)((address >> 16) & 0x00FF); + tmp_addr[1] = (uint8_t)((address >> 8) & 0x00FF); + tmp_addr[0] = (uint8_t)((address)&0x00FF); + + if (total_size_temp >= max_i2c_size) { + himax_mcu_register_read(ts, tmp_addr, max_i2c_size, + &temp_info_data[i * max_i2c_size], 0); + total_size_temp = total_size_temp - max_i2c_size; + } else { + /*I("last total_size_temp=%d\n",total_size_temp);*/ + himax_mcu_register_read(ts, tmp_addr, total_size_temp % max_i2c_size, + &temp_info_data[i * max_i2c_size], 0); + } + } + + /* 4. FW stop outputing */ + tmp_data[3] = temp_info_data[3]; + tmp_data[2] = temp_info_data[2]; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + himax_mcu_register_write(ts, ts->psram_op->addr_rawdata_addr, DATA_LEN_4, tmp_data, 0); + + /* 5. Data Checksum Check */ + for (i = 2; i < total_size; i += 2) /* 2:PASSWORD NOT included */ + check_sum_cal += (temp_info_data[i + 1] * 256 + temp_info_data[i]); + + if (check_sum_cal % 0x10000 != 0) { + I("%s check_sum_cal fail=%2X\n", __func__, check_sum_cal); + goto FAIL; + } else { + memcpy(info_data, &temp_info_data[4], total_data_size * sizeof(uint8_t)); + /*I("%s checksum PASS\n", __func__);*/ + } + kfree(temp_info_data); + return true; +FAIL: + kfree(temp_info_data); + return false; +} +/* SRAM side end*/ +/* CORE_SRAM */ + +static void himax_mcu_init_ic(struct himax_ts_data *ts) +{ + I("%s: use default incell init.\n", __func__); +} + +#if defined(HX_BOOT_UPGRADE) +/*------------------------------------------------------------------------- +* +* Create: Unknown +* +* Using: Read FW_VER and CFG_VER value from FW file +* +* param: None +* +* Dependency function: himax_auto_update_check +* +*/ +static int himax_mcu_fw_ver_bin(struct himax_ts_data *ts) +{ + I("%s: use default incell address.\n", __func__); + if (ts->hxfw != NULL) { + I("Catch fw version in bin file!\n"); + ts->fw_ver = (ts->hxfw->data[ts->fw_ver_maj_flash_addr] << 8) | + ts->hxfw->data[ts->fw_ver_min_flash_addr]; + ts->cfg_ver = (ts->hxfw->data[ts->cfg_ver_maj_flash_addr] << 8) | + ts->hxfw->data[ts->cfg_ver_min_flash_addr]; + ts->cid_maj = ts->hxfw->data[ts->cid_ver_maj_flash_addr]; + ts->cid_min = ts->hxfw->data[ts->cid_ver_min_flash_addr]; + } else { + I("FW data is null!\n"); + return 1; + } + return NO_ERR; +} +#endif + +void himax_mcu_tp_lcm_pin_reset(struct himax_ts_data *ts) +{ + I("%s: Brian debug \n", __func__); + I("%s: Now reset the Touch chip and LCM.\n", __func__); + himax_gpio_set(ts->rst_gpio, 0); + himax_gpio_set(ts->lcm_gpio, 0); + usleep_range(500, 600); + himax_gpio_set(ts->rst_gpio, 1); + msleep(100); + himax_gpio_set(ts->lcm_gpio, 1); +} + +#if defined(HX_RST_PIN_FUNC) +static void himax_mcu_pin_reset(struct himax_ts_data *ts) +{ + I("%s: Now reset the Touch chip.\n", __func__); + himax_gpio_set(ts->rst_gpio, 0); + msleep(20); + himax_gpio_set(ts->rst_gpio, 1); + msleep(50); +} + +static void himax_mcu_ic_reset(struct himax_ts_data *ts, uint8_t loadconfig, uint8_t int_off) +{ + ts->hx_hw_reset_activate = 0; + I("%s,status: loadconfig=%d,int_off=%d\n", __func__, loadconfig, int_off); + + if (ts->rst_gpio >= 0) { + if (int_off) + ts->core_fp.fp_irq_switch(ts, 0); + + ts->core_fp.fp_pin_reset(ts); + + if (loadconfig) + ts->core_fp.fp_reload_config(ts); + + if (int_off) + ts->core_fp.fp_irq_switch(ts, 1); + } +} +#endif + +/*------------------------------------------------------------------------- +* +* Create: Unknown +* +* Using: Read related touch information from mcu or assign fixed values +* to ic_data value. +* +* param: None +* +* Dependency function: moduleID_mcu_ic_id_read +*- +*/ +static void himax_mcu_touch_information(struct himax_ts_data *ts) +{ +#if !defined(HX_FIX_TOUCH_INFO) + uint8_t addr[DATA_LEN_4] = { 0 }; + uint8_t data[DATA_LEN_8] = { 0 }; + himax_parse_assign_cmd(addr_fw_define_rxnum_txnum_maxpt, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, DATA_LEN_8, data, 0); + ts->ic_data->HX_RX_NUM = data[2]; + ts->ic_data->HX_TX_NUM = data[3]; + ts->ic_data->HX_MAX_PT = data[4]; + /*I("%s : HX_RX_NUM=%d,ic_data->HX_TX_NUM=%d,ic_data->HX_MAX_PT=%d\n", + * __func__,ts->ic_data->HX_RX_NUM, + * ts->ic_data->HX_TX_NUM,ts->ic_data->HX_MAX_PT); + */ + himax_parse_assign_cmd(addr_fw_define_xy_res_enable, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, DATA_LEN_4, data, 0); + + /*I("%s : c_data->HX_XY_REVERSE=0x%2.2X\n",__func__,data[1]);*/ + if ((data[1] & 0x04) == 0x04) + ts->ic_data->HX_XY_REVERSE = true; + else + ts->ic_data->HX_XY_REVERSE = false; + himax_parse_assign_cmd(addr_fw_define_int_is_edge, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, DATA_LEN_4, data, 0); + /*I("%s : data[0]=0x%2.2X,data[1]=0x%2.2X,data[2]=0x%2.2X, + * data[3]=0x%2.2X\n",__func__,data[0],data[1],data[2],data[3]); + */ + /*I("data[0] & 0x01 = %d\n",(data[0] & 0x01));*/ + + if ((data[1] & 0x01) == 1) + ts->ic_data->HX_INT_IS_EDGE = true; + else + ts->ic_data->HX_INT_IS_EDGE = false; + + /* add HX_ID_EN check */ + himax_parse_assign_cmd(addr_HX_ID_EN, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, DATA_LEN_4, data, 0); + + if ((data[1] & 0x02) == 0x02) { + ts->ic_data->HX_IS_ID_EN = true; + } else { + ts->ic_data->HX_IS_ID_EN = false; + } + + /*Read number of MKey R100070E8H to determin data size*/ + himax_parse_assign_cmd(addr_mkey, addr, sizeof(addr)); + himax_mcu_register_read(ts, addr, DATA_LEN_4, data, 0); + + ts->ic_data->HX_BT_NUM = data[0] & 0x03; + +#else + ts->ic_data->HX_RX_NUM = FIX_HX_RX_NUM; + ts->ic_data->HX_TX_NUM = FIX_HX_TX_NUM; + ts->ic_data->HX_BT_NUM = FIX_HX_BT_NUM; + ts->ic_data->HX_MAX_PT = FIX_HX_MAX_PT; + ts->ic_data->HX_XY_REVERSE = FIX_HX_XY_REVERSE; + ts->ic_data->HX_INT_IS_EDGE = FIX_HX_INT_IS_EDGE; +#endif + ts->ic_data->HX_Y_RES = ts->pdata->screenHeight; + ts->ic_data->HX_X_RES = ts->pdata->screenWidth; + + ts->core_fp.fp_ic_id_read(ts); + I("%s:HX_RX_NUM =%d,HX_TX_NUM =%d\n", __func__, ts->ic_data->HX_RX_NUM, + ts->ic_data->HX_TX_NUM); + I("%s:HX_MAX_PT=%d,HX_XY_REVERSE =%d\n", __func__, ts->ic_data->HX_MAX_PT, + ts->ic_data->HX_XY_REVERSE); + I("%s:HX_Y_RES=%d,HX_X_RES =%d\n", __func__, ts->ic_data->HX_Y_RES, ts->ic_data->HX_X_RES); +} + +static void himax_mcu_reload_config(struct himax_ts_data *ts) +{ + if (himax_report_data_init(ts)) + E("%s: allocate data fail\n", __func__); + + ts->core_fp.fp_sense_on(ts, 0x00); +} + +static int himax_mcu_get_touch_data_size(struct himax_ts_data *ts) +{ + return HIMAX_TOUCH_DATA_SIZE; +} + +static int himax_mcu_cal_data_len(struct himax_ts_data *ts, int raw_cnt_rmd, int HX_MAX_PT, + int raw_cnt_max) +{ + int RawDataLen; + + if (raw_cnt_rmd != 0x00) + RawDataLen = MAX_I2C_TRANS_SZ - ((HX_MAX_PT + raw_cnt_max + 3) * 4) - 1; + else + RawDataLen = MAX_I2C_TRANS_SZ - ((HX_MAX_PT + raw_cnt_max + 2) * 4) - 1; + + return RawDataLen; +} + +static bool himax_mcu_diag_check_sum(struct himax_ts_data *ts, + struct himax_report_data *hx_touch_data) +{ + uint16_t check_sum_cal = 0; + int i; + + /* Check 128th byte CRC */ + for (i = 0, check_sum_cal = 0; + i < (hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); i += 2) { + check_sum_cal += (hx_touch_data->hx_rawdata_buf[i + 1] * FLASH_RW_MAX_LEN + + hx_touch_data->hx_rawdata_buf[i]); + } + + if (check_sum_cal % HX64K != 0) { + I("%s fail=%2X\n", __func__, check_sum_cal); + return 0; + } + + return 1; +} + +static void himax_mcu_diag_parse_raw_data(struct himax_ts_data *ts, + struct himax_report_data *hx_touch_data, int mul_num, + int self_num, uint8_t diag_cmd, int32_t *mutual_data, + int32_t *self_data) +{ + diag_mcu_parse_raw_data(ts, hx_touch_data, mul_num, self_num, diag_cmd, mutual_data, + self_data); +} + +/* CORE_INIT */ +/* init start */ +static void himax_mcu_fp_init(struct himax_ts_data *ts) +{ + /* CORE_FW */ + ts->core_fp.fp_burst_enable = himax_mcu_burst_enable; + ts->core_fp.fp_interface_on = himax_mcu_interface_on; + ts->core_fp.fp_sense_on = himax_mcu_sense_on; + ts->core_fp.fp_sense_off = himax_mcu_sense_off; + ts->core_fp.fp_wait_wip = himax_mcu_wait_wip; + ts->core_fp.fp_init_psl = himax_mcu_init_psl; + ts->core_fp.fp_resume_ic_action = himax_mcu_resume_ic_action; + ts->core_fp.fp_suspend_ic_action = himax_mcu_suspend_ic_action; + ts->core_fp.fp_power_on_init = himax_mcu_power_on_init; + ts->core_fp.fp_dd_clk_set = himax_mcu_dd_clk_set; + ts->core_fp.fp_dd_reg_en = himax_mcu_dd_reg_en; + ts->core_fp.fp_dd_reg_write = himax_mcu_dd_reg_write; + ts->core_fp.fp_dd_reg_read = himax_mcu_dd_reg_read; + ts->core_fp.fp_system_reset = himax_mcu_system_reset; + ts->core_fp.fp_Calculate_CRC_with_AP = himax_mcu_Calculate_CRC_with_AP; + ts->core_fp.fp_check_CRC = himax_mcu_check_CRC; + ts->core_fp.fp_diag_register_set = himax_mcu_diag_register_set; + ts->core_fp.fp_chip_self_test = himax_mcu_chip_self_test; + ts->core_fp.fp_reload_disable = himax_mcu_reload_disable; + ts->core_fp.fp_read_ic_trigger_type = himax_mcu_read_ic_trigger_type; + ts->core_fp.fp_read_FW_ver = himax_mcu_read_FW_ver; + ts->core_fp.fp_read_event_stack = himax_mcu_read_event_stack; + ts->core_fp.fp_return_event_stack = himax_mcu_return_event_stack; + ts->core_fp.fp_calculateChecksum = himax_mcu_calculateChecksum; + ts->core_fp.fp_read_FW_status = himax_mcu_read_FW_status; + ts->core_fp.fp_irq_switch = himax_mcu_irq_switch; + ts->core_fp.fp_assign_sorting_mode = himax_mcu_assign_sorting_mode; + ts->core_fp.fp_check_sorting_mode = himax_mcu_check_sorting_mode; + /* CORE_FW */ + /* CORE_FLASH */ + ts->core_fp.fp_chip_erase = himax_mcu_chip_erase; + ts->core_fp.fp_block_erase = himax_mcu_block_erase; + ts->core_fp.fp_sector_erase = himax_mcu_sector_erase; + ts->core_fp.fp_flash_programming = himax_mcu_flash_programming; + ts->core_fp.fp_flash_page_write = himax_mcu_flash_page_write; + ts->core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k = + himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_64k; + ts->core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k = + himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_128k; + ts->core_fp.fp_flash_dump_func = himax_mcu_flash_dump_func; + ts->core_fp.fp_flash_lastdata_check = himax_mcu_flash_lastdata_check; + ts->core_fp.fp_bin_desc_get = hx_mcu_bin_desc_get; + + /* CORE_FLASH */ + /* CORE_SRAM */ + ts->core_fp.fp_get_DSRAM_data = himax_mcu_get_DSRAM_data; + /* CORE_SRAM */ + /* CORE_DRIVER */ + ts->core_fp.fp_chip_init = himax_mcu_init_ic; +#if defined(HX_BOOT_UPGRADE) + ts->core_fp.fp_fw_ver_bin = himax_mcu_fw_ver_bin; +#endif +#if defined(HX_RST_PIN_FUNC) + ts->core_fp.fp_pin_reset = himax_mcu_pin_reset; + ts->core_fp.fp_ic_reset = himax_mcu_ic_reset; +#endif + ts->core_fp.fp_touch_information = himax_mcu_touch_information; + ts->core_fp.fp_reload_config = himax_mcu_reload_config; + ts->core_fp.fp_get_touch_data_size = himax_mcu_get_touch_data_size; + ts->core_fp.fp_cal_data_len = himax_mcu_cal_data_len; + ts->core_fp.fp_diag_check_sum = himax_mcu_diag_check_sum; + ts->core_fp.fp_diag_parse_raw_data = himax_mcu_diag_parse_raw_data; +} + +int himax_mcu_in_cmd_struct_init(struct himax_ts_data *ts) +{ + int err = 0; + + I("%s: Entering!\n", __func__); + + ts->core_cmd_op = kzalloc(sizeof(struct himax_core_command_operation), GFP_KERNEL); + if (ts->core_cmd_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_fail; + } + + ts->core_cmd_op->fw_op = kzalloc(sizeof(struct fw_operation), GFP_KERNEL); + if (ts->core_cmd_op->fw_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_fw_op_fail; + } + + ts->core_cmd_op->flash_op = kzalloc(sizeof(struct flash_operation), GFP_KERNEL); + if (ts->core_cmd_op->flash_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_flash_op_fail; + } + + ts->core_cmd_op->sram_op = kzalloc(sizeof(struct sram_operation), GFP_KERNEL); + if (ts->core_cmd_op->sram_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_sram_op_fail; + } + + ts->ic_incell.pfw_op = ts->core_cmd_op->fw_op; + ts->ic_incell.pflash_op = ts->core_cmd_op->flash_op; + ts->psram_op = ts->core_cmd_op->sram_op; + + ts->internal_buffer = kzalloc(sizeof(uint8_t) * HX_MAX_WRITE_SZ, GFP_KERNEL); + + if (ts->internal_buffer == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_g_internal_buffer_fail; + } + himax_mcu_fp_init(ts); + + return NO_ERR; + +err_g_core_cmd_op_g_internal_buffer_fail: + kfree(ts->core_cmd_op->sram_op); + ts->core_cmd_op->sram_op = NULL; +err_g_core_cmd_op_sram_op_fail: + kfree(ts->core_cmd_op->flash_op); + ts->core_cmd_op->flash_op = NULL; +err_g_core_cmd_op_flash_op_fail: + kfree(ts->core_cmd_op->fw_op); + ts->core_cmd_op->fw_op = NULL; +err_g_core_cmd_op_fw_op_fail: + kfree(ts->core_cmd_op); + ts->core_cmd_op = NULL; +err_g_core_cmd_op_fail: + + return err; +} +EXPORT_SYMBOL(himax_mcu_in_cmd_struct_init); + +void himax_mcu_in_cmd_struct_free(struct himax_ts_data *ts) +{ + ts->ic_incell.pfw_op = NULL; + ts->ic_incell.pflash_op = NULL; + ts->psram_op = NULL; + kfree(ts->internal_buffer); + ts->internal_buffer = NULL; + kfree(ts->core_cmd_op->sram_op); + ts->core_cmd_op->sram_op = NULL; + kfree(ts->core_cmd_op->flash_op); + ts->core_cmd_op->flash_op = NULL; + kfree(ts->core_cmd_op->fw_op); + ts->core_cmd_op->fw_op = NULL; + ts->core_cmd_op = NULL; + + I("%s: release completed\n", __func__); +} + +void himax_mcu_in_cmd_init(struct himax_ts_data *ts) +{ + I("%s: Entering!\n", __func__); + /* CORE_FW */ + himax_parse_assign_cmd(fw_addr_osc_en, ts->ic_incell.pfw_op->addr_osc_en, + sizeof(ts->ic_incell.pfw_op->addr_osc_en)); + himax_parse_assign_cmd(fw_addr_osc_pw, ts->ic_incell.pfw_op->addr_osc_pw, + sizeof(ts->ic_incell.pfw_op->addr_osc_pw)); + himax_parse_assign_cmd(fw_addr_program_reload_from, + ts->ic_incell.pfw_op->addr_program_reload_from, + sizeof(ts->ic_incell.pfw_op->addr_program_reload_from)); + himax_parse_assign_cmd(fw_addr_reload_status, ts->ic_incell.pfw_op->addr_reload_status, + sizeof(ts->ic_incell.pfw_op->addr_reload_status)); + himax_parse_assign_cmd(fw_addr_reload_crc32_result, + ts->ic_incell.pfw_op->addr_reload_crc32_result, + sizeof(ts->ic_incell.pfw_op->addr_reload_crc32_result)); + himax_parse_assign_cmd(fw_addr_reload_addr_from, + ts->ic_incell.pfw_op->addr_reload_addr_from, + sizeof(ts->ic_incell.pfw_op->addr_reload_addr_from)); + himax_parse_assign_cmd(fw_addr_reload_addr_cmd_beat, + ts->ic_incell.pfw_op->addr_reload_addr_cmd_beat, + sizeof(ts->ic_incell.pfw_op->addr_reload_addr_cmd_beat)); + himax_parse_assign_cmd(fw_data_rawdata_ready_hb, + ts->ic_incell.pfw_op->data_rawdata_ready_hb, + sizeof(ts->ic_incell.pfw_op->data_rawdata_ready_hb)); + himax_parse_assign_cmd(fw_data_rawdata_ready_lb, + ts->ic_incell.pfw_op->data_rawdata_ready_lb, + sizeof(ts->ic_incell.pfw_op->data_rawdata_ready_lb)); + /* CORE_FW */ + /* CORE_FLASH */ + himax_parse_assign_cmd(flash_addr_spi200_trans_fmt, + ts->ic_incell.pflash_op->addr_spi200_trans_fmt, + sizeof(ts->ic_incell.pflash_op->addr_spi200_trans_fmt)); + himax_parse_assign_cmd(flash_addr_spi200_trans_ctrl, + ts->ic_incell.pflash_op->addr_spi200_trans_ctrl, + sizeof(ts->ic_incell.pflash_op->addr_spi200_trans_ctrl)); + himax_parse_assign_cmd(flash_addr_spi200_fifo_rst, + ts->ic_incell.pflash_op->addr_spi200_fifo_rst, + sizeof(ts->ic_incell.pflash_op->addr_spi200_fifo_rst)); + himax_parse_assign_cmd(flash_addr_spi200_flash_speed, + ts->ic_incell.pflash_op->addr_spi200_flash_speed, + sizeof(ts->ic_incell.pflash_op->addr_spi200_flash_speed)); + himax_parse_assign_cmd(flash_addr_spi200_rst_status, + ts->ic_incell.pflash_op->addr_spi200_rst_status, + sizeof(ts->ic_incell.pflash_op->addr_spi200_rst_status)); + himax_parse_assign_cmd(flash_addr_spi200_cmd, ts->ic_incell.pflash_op->addr_spi200_cmd, + sizeof(ts->ic_incell.pflash_op->addr_spi200_cmd)); + himax_parse_assign_cmd(flash_addr_spi200_addr, ts->ic_incell.pflash_op->addr_spi200_addr, + sizeof(ts->ic_incell.pflash_op->addr_spi200_addr)); + himax_parse_assign_cmd(flash_addr_spi200_data, ts->ic_incell.pflash_op->addr_spi200_data, + sizeof(ts->ic_incell.pflash_op->addr_spi200_data)); + himax_parse_assign_cmd(flash_addr_spi200_bt_num, + ts->ic_incell.pflash_op->addr_spi200_bt_num, + sizeof(ts->ic_incell.pflash_op->addr_spi200_bt_num)); + himax_parse_assign_cmd(flash_data_spi200_trans_fmt, + ts->ic_incell.pflash_op->data_spi200_trans_fmt, + sizeof(ts->ic_incell.pflash_op->data_spi200_trans_fmt)); + himax_parse_assign_cmd(flash_data_spi200_txfifo_rst, + ts->ic_incell.pflash_op->data_spi200_txfifo_rst, + sizeof(ts->ic_incell.pflash_op->data_spi200_txfifo_rst)); + himax_parse_assign_cmd(flash_data_spi200_rxfifo_rst, + ts->ic_incell.pflash_op->data_spi200_rxfifo_rst, + sizeof(ts->ic_incell.pflash_op->data_spi200_rxfifo_rst)); + himax_parse_assign_cmd(flash_data_spi200_trans_ctrl_1, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_1, + sizeof(ts->ic_incell.pflash_op->data_spi200_trans_ctrl_1)); + himax_parse_assign_cmd(flash_data_spi200_trans_ctrl_2, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_2, + sizeof(ts->ic_incell.pflash_op->data_spi200_trans_ctrl_2)); + himax_parse_assign_cmd(flash_data_spi200_trans_ctrl_3, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_3, + sizeof(ts->ic_incell.pflash_op->data_spi200_trans_ctrl_3)); + himax_parse_assign_cmd(flash_data_spi200_trans_ctrl_4, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_4, + sizeof(ts->ic_incell.pflash_op->data_spi200_trans_ctrl_4)); + himax_parse_assign_cmd(flash_data_spi200_trans_ctrl_6, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_6, + sizeof(ts->ic_incell.pflash_op->data_spi200_trans_ctrl_6)); + himax_parse_assign_cmd(flash_data_spi200_trans_ctrl_7, + ts->ic_incell.pflash_op->data_spi200_trans_ctrl_7, + sizeof(ts->ic_incell.pflash_op->data_spi200_trans_ctrl_7)); + himax_parse_assign_cmd(flash_data_spi200_cmd_1, ts->ic_incell.pflash_op->data_spi200_cmd_1, + sizeof(ts->ic_incell.pflash_op->data_spi200_cmd_1)); + himax_parse_assign_cmd(flash_data_spi200_cmd_2, ts->ic_incell.pflash_op->data_spi200_cmd_2, + sizeof(ts->ic_incell.pflash_op->data_spi200_cmd_2)); + himax_parse_assign_cmd(flash_data_spi200_cmd_3, ts->ic_incell.pflash_op->data_spi200_cmd_3, + sizeof(ts->ic_incell.pflash_op->data_spi200_cmd_3)); + himax_parse_assign_cmd(flash_data_spi200_cmd_4, ts->ic_incell.pflash_op->data_spi200_cmd_4, + sizeof(ts->ic_incell.pflash_op->data_spi200_cmd_4)); + himax_parse_assign_cmd(flash_data_spi200_cmd_6, ts->ic_incell.pflash_op->data_spi200_cmd_6, + sizeof(ts->ic_incell.pflash_op->data_spi200_cmd_6)); + himax_parse_assign_cmd(flash_data_spi200_cmd_7, ts->ic_incell.pflash_op->data_spi200_cmd_7, + sizeof(ts->ic_incell.pflash_op->data_spi200_cmd_7)); + himax_parse_assign_cmd(flash_data_spi200_cmd_8, ts->ic_incell.pflash_op->data_spi200_cmd_8, + sizeof(ts->ic_incell.pflash_op->data_spi200_cmd_8)); + /* CORE_FLASH */ + /* CORE_SRAM */ + /* sram start*/ + himax_parse_assign_cmd(sram_adr_rawdata_addr, ts->psram_op->addr_rawdata_addr, + sizeof(ts->psram_op->addr_rawdata_addr)); + himax_parse_assign_cmd(sram_adr_rawdata_end, ts->psram_op->addr_rawdata_end, + sizeof(ts->psram_op->addr_rawdata_end)); + himax_parse_assign_cmd(sram_passwrd_start, ts->psram_op->passwrd_start, + sizeof(ts->psram_op->passwrd_start)); + himax_parse_assign_cmd(sram_passwrd_end, ts->psram_op->passwrd_end, + sizeof(ts->psram_op->passwrd_end)); + /* sram end*/ + /* CORE_SRAM */ +} +EXPORT_SYMBOL(himax_mcu_in_cmd_init); + +/*------------------------------------------------------------------------- +* +* Create: 2021/07 +* +* Using: Disable WP BP protection, or FW cannot be updated succesfully. +* +* param: None +* +* Dependency function: himax_mcu_WP_BP_enable, himax_mcu_flash_id_check +* +* +-----------------+-----------+----+------+-----------+ +* | ID | BP | WP | type | Lock code | +* +-----------------+-----------+----+------+-----------+ +* | 0 | 2_3 | 7 | 1 | 0x8C | +* | 1/6/11 | 2_3_4 | 7 | 2 | 0x9C | +* | 4/5/10/12/13/14 | 2_3_4_5 | 7 | 3 | 0xBC | +* | 2/3/7/8/9 | 2_3_4_5_6 | 7 | 4 | 0xFC | +* +-----------------+-----------+----+------+-----------+ +* +*/ +int himax_mcu_WP_BP_disable(struct himax_ts_data *ts) +{ + uint8_t addr[4]; + uint8_t data[4]; + uint8_t lock_code = 0; + + himax_mcu_flash_id_check(ts); + + /*high priority unlock*/ + lock_code = 0xFC; + +#if defined(WP_GPIO4) + /*WP addr */ + addr[3] = 0x90; + addr[2] = 0x02; + addr[1] = 0x80; + addr[0] = 0x00; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x01; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x04; + data[0] = 0x10; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x1C; + data[0] = 0x01; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); +#endif + +#if defined(WP_GPIO0) + /*WP addr */ + addr[3] = 0x90; + addr[2] = 0x02; + addr[1] = 0x80; + addr[0] = 0x00; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x01; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x04; + data[0] = 0x01; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x0C; + data[0] = 0x01; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); +#endif + + /*BP addr */ + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x00 + 0x10; + data[3] = 0x00; + data[2] = 0x02; + data[1] = 0x07; + data[0] = 0x80; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x20; + data[3] = 0x47; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x00; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x24; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x06; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x20; + data[3] = 0x41; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x00; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x2C; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x00; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x24; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x01; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + msleep(40); //msleep(10); + + /*Check BP */ + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x00 + 0x20; + data[3] = 0x42; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x00; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x24; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x05; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + /*Read Addr*/ + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x00 + 0x2C; + himax_mcu_register_read(ts, addr, 1, data, 0); + + /*Addr 0x8000_002C value should be 0x00*/ + I("%s: WP BP disable check, Addr 0x8000_002C, read value is %02X\n", __func__, data[0]); + + data[0] = (~data[0]) & lock_code; + + if (data[0] != lock_code) { + E("%s: Fail to disable WP BP lock with wrong reversed data :%x, lock code %x.\n", + __func__, data[0], lock_code); + return -1; + } + + I("%s: Disable WP BP lock finish.\n", __func__); + return 0; +} + +/*------------------------------------------------------------------------- +* +* Create: 2021/07 +* +* Using: Enable WP BP protection after FW update. +* +* param: None +* +* Dependency function: himax_mcu_WP_BP_disable, himax_mcu_flash_id_check +* +* +-----------------+-----------+----+------+-----------+ +* | ID | BP | WP | type | Lock code | +* +-----------------+-----------+----+------+-----------+ +* | 0 | 2_3 | 7 | 1 | 0x8C | +* | 1/6/11 | 2_3_4 | 7 | 2 | 0x9C | +* | 4/5/10/12/13/14 | 2_3_4_5 | 7 | 3 | 0xBC | +* | 2/3/7/8/9 | 2_3_4_5_6 | 7 | 4 | 0xFC | +* +-----------------+-----------+----+------+-----------+ +* +*/ +int himax_mcu_WP_BP_enable(struct himax_ts_data *ts) +{ + uint8_t addr[4]; + uint8_t data[4]; + int ret = 0; + uint8_t lock_code = 0; + + ret = himax_mcu_flash_id_check(ts); + + if (ret == -1) { + E("%s: Cannot recognize flash id type \n", __func__); + return -1; + } + + switch (ts->ic_incell.pfw_op->flash_lock_type[0]) { + case 1: + lock_code = 0x8C; + break; + case 2: + lock_code = 0x9C; + break; + case 3: + lock_code = 0xBC; + break; + case 4: + lock_code = 0xFC; + break; + default: + E("%s: Unknown lock type with value : %x \n", __func__, + ts->ic_incell.pfw_op->flash_lock_type[0]); + break; + } + + /*Check Addr 0x8000_002C value, if the same as lock_code BP is lock*/ + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x00 + 0x10; + data[3] = 0x00; + data[2] = 0x02; + data[1] = 0x07; + data[0] = 0x80; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x20; + data[3] = 0x42; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x00; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x24; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x05; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x2C; + himax_mcu_register_read(ts, addr, 1, data, 0); + + /* bit 2_3_4 and bit 7 should be 1 */ + I("%s: BP lock check before enable WP BP, Addr 0x8000_002C, read value is %02X\n", __func__, + data[0]); + + data[0] = data[0] & lock_code; + + /* fast check need to judge according to flash type */ + if (data[0] == lock_code) { + I("%s: Enable fast check pass with value %02X\n", __func__, data[0]); + return 0; + } + + /*BP addr */ + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x00 + 0x10; + data[3] = 0x00; + data[2] = 0x02; + data[1] = 0x07; + data[0] = 0x80; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x20; + data[3] = 0x47; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x00; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x24; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x06; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x20; + data[3] = 0x41; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x00; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x2C; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = lock_code; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x24; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x01; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + msleep(10); + + /*Check BP */ + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x00 + 0x20; + data[3] = 0x42; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x00; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x24; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x05; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + /*Read Addr*/ + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x00 + 0x2C; + himax_mcu_register_read(ts, addr, 1, data, 0); + + /*Addr 0x8000_002C value should be the same as lock_code*/ + I("%s: WP BP enable check, Addr 0x8000_002C, read value is %02X\n", __func__, data[0]); + + if (data[0] != lock_code) { + E("%s: Fail to enable WP BP lock with wrong data :%x, lock code %x.\n", __func__, + data[0], lock_code); + return -1; + } + +#if defined(WP_GPIO0) + /*WP addr */ + addr[3] = 0x90; + addr[2] = 0x02; + addr[1] = 0x80; + addr[0] = 0x00; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x01; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x04; + data[0] = 0x01; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x0C; + data[0] = 0x00; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); +#endif + +#if defined(WP_GPIO4) + /*WP addr */ + addr[3] = 0x90; + addr[2] = 0x02; + addr[1] = 0x80; + addr[0] = 0x00; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x01; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x04; + data[0] = 0x10; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x1C; + data[0] = 0x00; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); +#endif + + I("%s: Enable WP BP lock finish.\n", __func__); + return 0; +} + +/*------------------------------------------------------------------------- +* +* Create: 2021/08 +* +* Using: Check WP BP protection status. +* +* param: None +* +* Dependency function: himax_mcu_flash_id_check +* +* +-----------------+-----------+----+------+-----------+ +* | ID | BP | WP | type | Lock code | +* +-----------------+-----------+----+------+-----------+ +* | 0 | 2_3 | 7 | 1 | 0x8C | +* | 1/6/11 | 2_3_4 | 7 | 2 | 0x9C | +* | 4/5/10/12/13/14 | 2_3_4_5 | 7 | 3 | 0xBC | +* | 2/3/7/8/9 | 2_3_4_5_6 | 7 | 4 | 0xFC | +* +-----------------+-----------+----+------+-----------+ +* +*/ +int himax_mcu_WP_BP_status(struct himax_ts_data *ts) +{ + uint8_t addr[4]; + uint8_t data[4]; + int ret = 0; + uint8_t lock_code = 0; + + ret = himax_mcu_flash_id_check(ts); + + if (ret == -1) { + E("%s: Cannot recognize flash id type \n", __func__); + return -1; + } + + switch (ts->ic_incell.pfw_op->flash_lock_type[0]) { + case 1: + lock_code = 0x8C; + break; + case 2: + lock_code = 0x9C; + break; + case 3: + lock_code = 0xBC; + break; + case 4: + lock_code = 0xFC; + break; + default: + E("%s: Unknown lock type with value : %x \n", __func__, + ts->ic_incell.pfw_op->flash_lock_type[0]); + break; + } + + /*Check Addr 0x8000_002C value, if 0x9C BP is lock*/ + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x00 + 0x10; + data[3] = 0x00; + data[2] = 0x02; + data[1] = 0x07; + data[0] = 0x80; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x20; + data[3] = 0x42; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x00; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x24; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x05; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x2C; + himax_mcu_register_read(ts, addr, 1, data, 0); + + I("%s: WP BP lock check, Addr 0x8000_002C, read value is %02X\n", __func__, data[0]); + + if (data[0] == lock_code) { + I("%s: WP BP lock status is lock with value : %x.\n", __func__, data[0]); + } else { + I("%s: WP BP lock status is unlock with value : %x.\n", __func__, data[0]); + } + return data[0]; +} + +/*------------------------------------------------------------------------- +* +* Create: 2021/08 +* +* Using: Check Flash ID and sort ID to Lock type. +* +* param: None +* +* Dependency function: himax_mcu_WP_BP_disable, himax_mcu_WP_BP_enable +* +* +-----------------+-----------+----+------+-----------+ +* | ID | BP | WP | type | Lock code | +* +-----------------+-----------+----+------+-----------+ +* | 0 | 2_3 | 7 | 1 | 0x8C | +* | 1/6/11 | 2_3_4 | 7 | 2 | 0x9C | +* | 4/5/10/12/13/14 | 2_3_4_5 | 7 | 3 | 0xBC | +* | 2/3/7/8/9 | 2_3_4_5_6 | 7 | 4 | 0xFC | +* +-----------------+-----------+----+------+-----------+ +* +*/ +int himax_mcu_flash_id_check(struct himax_ts_data *ts) +{ + uint8_t addr[4]; + uint8_t data[4]; + uint8_t i; + uint8_t Flash_list_tmp[][3] = Flash_list; + size_t len = sizeof(Flash_list_tmp) / (sizeof(uint8_t) * 3); + uint8_t flash_idx; + + flash_idx = 0xff; + + /*Check Addr 0x8000_002C value, if 0x9C BP is lock*/ + addr[3] = 0x80; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x00 + 0x20; + data[3] = 0x42; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x02; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x24; + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0x9F; + himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0); + + addr[0] = 0x00 + 0x2C; + himax_mcu_register_read(ts, addr, DATA_LEN_4, data, 0); + + I("%s: FlashList len : %zd, Flash type ID data = %X,%X,%X\n", __func__, len, data[0], + data[1], data[2]); + + for (i = 0; i < len; i++) { + if ((Flash_list_tmp[i][0] == data[0]) && (Flash_list_tmp[i][1] == data[1]) && + (Flash_list_tmp[i][2] == data[2])) { + flash_idx = i; + break; + } + } + + I("%s: Flash id mapping to index : %d.\n", __func__, flash_idx); + + if (flash_idx == 0) { + ts->ic_incell.pfw_op->flash_lock_type[0] = 1; + } else if ((flash_idx == 1) || (flash_idx == 6) || (flash_idx == 11)) { + ts->ic_incell.pfw_op->flash_lock_type[0] = 2; + } else if ((flash_idx == 4) || (flash_idx == 5) || (flash_idx == 10) || (flash_idx == 12) || + (flash_idx == 13) || (flash_idx == 14)) { + ts->ic_incell.pfw_op->flash_lock_type[0] = 3; + } else if ((flash_idx == 2) || (flash_idx == 3) || (flash_idx == 7) || (flash_idx == 8) || + (flash_idx == 9)) { + ts->ic_incell.pfw_op->flash_lock_type[0] = 4; + } else { + ts->ic_incell.pfw_op->flash_lock_type[0] = 0xff; + } + + if (ts->ic_incell.pfw_op->flash_lock_type[0] == 0xff) { + E("%s: Flash_lock_type Unknown cause FlashList compare fail \n", __func__); + return -1; + } + + I("%s: Flash id check and sort finished.\n", __func__); + return 0; +} + +/* init end*/ +/* CORE_INIT */ diff --git a/drivers/input/touchscreen/hxchipset/himax_inspection.c b/drivers/input/touchscreen/hxchipset/himax_inspection.c new file mode 100644 index 000000000000..8f54a403e347 --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/himax_inspection.c @@ -0,0 +1,2151 @@ +/* Himax Android Driver Sample Code for inspection functions + * + * Copyright (C) 2021 Himax Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "himax.h" +#include "himax_inspection.h" + +#define FAIL_IN_INDEX "%s: %s FAIL in index %d\n" + +static const char *g_hx_head_str[] = { + "TP_Info", + "Project_Info", + "TestItem", + "TestCriteria", + NULL +}; + +/*Need to map THP_INSPECTION_ENUM*/ +static const char *g_himax_inspection_mode[] = { + "HIMAX_OPEN", + "HIMAX_MICRO_OPEN", + "HIMAX_SHORT", + "HIMAX_RAWDATA", + "HIMAX_BPN_RAWDATA", + "HIMAX_SC", + "HIMAX_WEIGHT_NOISE", + "HIMAX_ABS_NOISE", + "HIMAX_SORTING", + "HIMAX_GAPTEST_RAW", + /*"HIMAX_GAPTEST_RAW_X",*/ + /*"HIMAX_GAPTEST_RAW_Y",*/ + + "HIMAX_ACT_IDLE_RAWDATA", + "HIMAX_ACT_IDLE_BPN_RAWDATA", + "HIMAX_ACT_IDLE_NOISE", + + "HIMAX_LPWUG_RAWDATA", + "HIMAX_LPWUG_BPN_RAWDATA", + "HIMAX_LPWUG_WEIGHT_NOISE", + "HIMAX_LPWUG_ABS_NOISE", + "HIMAX_LPWUG_IDLE_RAWDATA", + "HIMAX_LPWUG_IDLE_BPN_RAWDATA", + "HIMAX_LPWUG_IDLE_NOISE", + + "HIMAX_BACK_NORMAL", NULL +}; + +/* for criteria */ +static const char *g_hx_inspt_crtra_name[] = { + "CRITERIA_RAW_MIN", + "CRITERIA_RAW_MAX", + "CRITERIA_RAW_BPN_MIN", + "CRITERIA_RAW_BPN_MAX", + "CRITERIA_SC_MIN", + "CRITERIA_SC_MAX", + "CRITERIA_SC_GOLDEN", + "CRITERIA_SHORT_MIN", + "CRITERIA_SHORT_MAX", + "CRITERIA_OPEN_MIN", + "CRITERIA_OPEN_MAX", + "CRITERIA_MICRO_OPEN_MIN", + "CRITERIA_MICRO_OPEN_MAX", + "CRITERIA_NOISE_WT_MIN", + "CRITERIA_NOISE_WT_MAX", + "CRITERIA_NOISE_ABS_MIN", + "CRITERIA_NOISE_ABS_MAX", + "CRITERIA_SORT_MIN", + "CRITERIA_SORT_MAX", + + "CRITERIA_GAP_RAW_HOR_MIN", + "CRITERIA_GAP_RAW_HOR_MAX", + "CRITERIA_GAP_RAW_VER_MIN", + "CRITERIA_GAP_RAW_VER_MAX", + + "ACT_IDLE_NOISE_MIN", + "ACT_IDLE_NOISE_MAX", + "ACT_IDLE_RAWDATA_MIN", + "ACT_IDLE_RAWDATA_MAX", + "ACT_IDLE_RAW_BPN_MIN", + "ACT_IDLE_RAW_BPN_MAX", + + "LPWUG_NOISE_WT_MIN", + "LPWUG_NOISE_WT_MAX", + "LPWUG_NOISE_ABS_MIN", + "LPWUG_NOISE_ABS_MAX", + "LPWUG_RAWDATA_MIN", + "LPWUG_RAWDATA_MAX", + "LPWUG_RAW_BPN_MIN", + "LPWUG_RAW_BPN_MAX", + + "LPWUG_IDLE_NOISE_MIN", + "LPWUG_IDLE_NOISE_MAX", + "LPWUG_IDLE_RAWDATA_MIN", + "LPWUG_IDLE_RAWDATA_MAX", + "LPWUG_IDLE_RAW_BPN_MIN", + "LPWUG_IDLE_RAW_BPN_MAX", + NULL +}; + +static void himax_press_powerkey(struct himax_ts_data *ts) +{ + I(" %s POWER KEY event %x press\n", __func__, KEY_POWER); + input_report_key(ts->input_dev, KEY_POWER, 1); + input_sync(ts->input_dev); + + msleep(100); + + I(" %s POWER KEY event %x release\n", __func__, KEY_POWER); + input_report_key(ts->input_dev, KEY_POWER, 0); + input_sync(ts->input_dev); +} + +static uint8_t NOISEMAX; +static uint8_t g_recal_thx; + +static int arraydata_max1, arraydata_max2, arraydata_max3; +static int arraydata_min1, arraydata_min2, arraydata_min3; + +static void himax_get_arraydata_edge(struct himax_ts_data *ts, uint32_t *RAW) +{ + int temp, i, j; + int len = ts->ic_data->HX_RX_NUM * ts->ic_data->HX_TX_NUM; + uint32_t *ArrayData; + + ArrayData = kmalloc(sizeof(int) * len, GFP_KERNEL); + if (ArrayData == NULL) { + E("%s:ArrayData malloc error\n", __func__); + return; + } + + for (i = 0; i < len; i++) + ArrayData[i] = RAW[i]; + for (j = len - 1; j > 0; j--) { /*min to max*/ + for (i = 0; i < j; i++) { + if (ArrayData[i] > ArrayData[i + 1]) { + temp = ArrayData[i]; + ArrayData[i] = ArrayData[i + 1]; + ArrayData[i + 1] = temp; + } + } + } + + arraydata_min1 = ArrayData[0]; + arraydata_min2 = ArrayData[1]; + arraydata_min3 = ArrayData[2]; + arraydata_max1 = ArrayData[len - 3]; + arraydata_max2 = ArrayData[len - 2]; + arraydata_max3 = ArrayData[len - 1]; + + kfree(ArrayData); +} + +static int hx_test_data_get(struct himax_ts_data *ts, uint32_t RAW[], char *start_log, char *result, + int now_item) +{ + uint32_t i; + + ssize_t len = 0; + char *testdata = NULL; + uint32_t SZ_SIZE = ts->one_kind_raw_size; + + I("%s: Entering, Now type=%s!\n", __func__, g_himax_inspection_mode[now_item]); + + testdata = kzalloc(sizeof(char) * SZ_SIZE, GFP_KERNEL); + if (testdata == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + + len += snprintf((testdata + len), SZ_SIZE - len, "%s", start_log); + for (i = 0; i < ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM; i++) { + if (i > 1 && ((i + 1) % ts->ic_data->HX_RX_NUM) == 0) + len += snprintf((testdata + len), SZ_SIZE - len, "%5d,\n", RAW[i]); + else + len += snprintf((testdata + len), SZ_SIZE - len, "%5d,", RAW[i]); + } + len += snprintf((testdata + len), SZ_SIZE - len, "\n%s", result); + + memcpy(&ts->rslt_data[0], testdata, len); + ts->rslt_data_len = len; + I("%s: g_rslt_data_len=%d!\n", __func__, ts->rslt_data_len); + + /* dbg */ + /* for(i = 0; i < SZ_SIZE; i++) + * { + * I("0x%04X, ", ts->rslt_data[i + (now_item * SZ_SIZE)]); + * if(i > 0 && (i % 16 == 15)) + * PI("\n"); + * } + */ + + kfree(testdata); + I("%s: End!\n", __func__); + return NO_ERR; +} + +static int himax_switch_mode_inspection(struct himax_ts_data *ts, int mode) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4] = { 0 }; + + I("%s: Entering\n", __func__); + + /*Stop Handshaking*/ + himax_parse_assign_cmd(sram_adr_rawdata_addr, tmp_addr, sizeof(tmp_addr)); + himax_mcu_register_write(ts, tmp_addr, 4, tmp_data, 0); + + /*Swtich Mode*/ + switch (mode) { + case HX_SORTING: + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = PWD_SORTING_START; + tmp_data[0] = PWD_SORTING_START; + break; + case HX_OPEN: + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = PWD_OPEN_START; + tmp_data[0] = PWD_OPEN_START; + break; + case HX_MICRO_OPEN: + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = PWD_MICRO_OPEN_START; + tmp_data[0] = PWD_MICRO_OPEN_START; + break; + case HX_SHORT: + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = PWD_SHORT_START; + tmp_data[0] = PWD_SHORT_START; + break; + + case HX_GAPTEST_RAW: + case HX_RAWDATA: + case HX_BPN_RAWDATA: + case HX_SC: + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = PWD_RAWDATA_START; + tmp_data[0] = PWD_RAWDATA_START; + break; + + case HX_WT_NOISE: + case HX_ABS_NOISE: + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = PWD_NOISE_START; + tmp_data[0] = PWD_NOISE_START; + break; + + case HX_ACT_IDLE_RAWDATA: + case HX_ACT_IDLE_BPN_RAWDATA: + case HX_ACT_IDLE_NOISE: + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = PWD_ACT_IDLE_START; + tmp_data[0] = PWD_ACT_IDLE_START; + break; + + case HX_LP_RAWDATA: + case HX_LP_BPN_RAWDATA: + case HX_LP_ABS_NOISE: + case HX_LP_WT_NOISE: + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = PWD_LP_START; + tmp_data[0] = PWD_LP_START; + break; + case HX_LP_IDLE_RAWDATA: + case HX_LP_IDLE_BPN_RAWDATA: + case HX_LP_IDLE_NOISE: + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = PWD_LP_IDLE_START; + tmp_data[0] = PWD_LP_IDLE_START; + break; + + default: + I("%s,Nothing to be done!\n", __func__); + break; + } + + if (ts->core_fp.fp_assign_sorting_mode != NULL) + ts->core_fp.fp_assign_sorting_mode(ts, tmp_data); + I("%s: End of setting!\n", __func__); + + return 0; +} + +static uint32_t himax_get_rawdata(struct himax_ts_data *ts, uint32_t RAW[], uint32_t datalen) +{ + uint8_t *tmp_rawdata; + bool get_raw_rlst; + uint8_t retry = 0; + uint32_t i = 0; + uint32_t j = 0; + uint32_t index = 0; + uint32_t Min_DATA = 0xFFFFFFFF; + uint32_t Max_DATA = 0x00000000; + + /* We use two bytes to combine a value of rawdata.*/ + tmp_rawdata = kzalloc(sizeof(uint8_t) * (datalen * 2), GFP_KERNEL); + if (tmp_rawdata == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return HX_INSP_MEMALLCTFAIL; + } + + while (retry < 200) { + get_raw_rlst = ts->core_fp.fp_get_DSRAM_data(ts, tmp_rawdata, false); + if (get_raw_rlst) + break; + retry++; + } + + if (retry >= 200) + goto DIRECT_END; + + /* Copy Data*/ + for (i = 0; i < ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM; i++) + RAW[i] = tmp_rawdata[(i * 2) + 1] * 256 + tmp_rawdata[(i * 2)]; + /* + for (j = 0; j < ts->ic_data->HX_RX_NUM; j++) { + if (j == 0) + PI(" RX%2d", j + 1); + else + PI(" RX%2d", j + 1); + } + PI("\n"); +*/ + for (i = 0; i < ts->ic_data->HX_TX_NUM; i++) { + if (ts->debug_log_level & BIT(3)) + //I("TX%2d", i + 1); + for (j = 0; j < ts->ic_data->HX_RX_NUM; j++) { + if (ts->debug_log_level & BIT(3)) + //I("%5d ", RAW[index]); + if (RAW[index] > Max_DATA) + Max_DATA = RAW[index]; + + if (RAW[index] < Min_DATA) + Min_DATA = RAW[index]; + + index++; + } + if (ts->debug_log_level & BIT(3)) + I("\n"); + } + I("Max = %5d, Min = %5d\n", Max_DATA, Min_DATA); +DIRECT_END: + kfree(tmp_rawdata); + + if (get_raw_rlst) + return HX_INSP_OK; + else + return HX_INSP_EGETRAW; +} + +static void himax_switch_data_type(struct himax_ts_data *ts, uint8_t checktype) +{ + uint8_t datatype = 0x00; + + switch (checktype) { + case HX_SORTING: + datatype = DATA_SORTING; + break; + case HX_OPEN: + datatype = DATA_OPEN; + break; + case HX_MICRO_OPEN: + datatype = DATA_MICRO_OPEN; + break; + case HX_SHORT: + datatype = DATA_SHORT; + break; + case HX_RAWDATA: + case HX_BPN_RAWDATA: + case HX_SC: + case HX_GAPTEST_RAW: + datatype = DATA_RAWDATA; + break; + + case HX_WT_NOISE: + case HX_ABS_NOISE: + datatype = DATA_NOISE; + break; + case HX_BACK_NORMAL: + datatype = DATA_BACK_NORMAL; + break; + case HX_ACT_IDLE_RAWDATA: + case HX_ACT_IDLE_BPN_RAWDATA: + datatype = DATA_ACT_IDLE_RAWDATA; + break; + case HX_ACT_IDLE_NOISE: + datatype = DATA_ACT_IDLE_NOISE; + break; + + case HX_LP_RAWDATA: + case HX_LP_BPN_RAWDATA: + datatype = DATA_LP_RAWDATA; + break; + case HX_LP_WT_NOISE: + case HX_LP_ABS_NOISE: + datatype = DATA_LP_NOISE; + break; + case HX_LP_IDLE_RAWDATA: + case HX_LP_IDLE_BPN_RAWDATA: + datatype = DATA_LP_IDLE_RAWDATA; + break; + case HX_LP_IDLE_NOISE: + datatype = DATA_LP_IDLE_NOISE; + break; + + default: + E("Wrong type=%d\n", checktype); + break; + } + ts->core_fp.fp_diag_register_set(ts, datatype, 0x00, false); +} + +static void himax_bank_search_set(struct himax_ts_data *ts, uint16_t Nframe, uint8_t checktype) +{ + uint8_t tmp_addr[4] = { 0 }; + uint8_t tmp_data[4] = { 0 }; + + /*skip frame 0x100070F4*/ + himax_parse_assign_cmd(addr_skip_frame, tmp_addr, sizeof(tmp_addr)); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + + switch (checktype) { + case HX_ACT_IDLE_RAWDATA: + case HX_ACT_IDLE_BPN_RAWDATA: + case HX_ACT_IDLE_NOISE: + tmp_data[0] = BS_ACT_IDLE; + break; + case HX_LP_RAWDATA: + case HX_LP_BPN_RAWDATA: + case HX_LP_ABS_NOISE: + case HX_LP_WT_NOISE: + tmp_data[0] = BS_LPWUG; + break; + case HX_LP_IDLE_RAWDATA: + case HX_LP_IDLE_BPN_RAWDATA: + case HX_LP_IDLE_NOISE: + tmp_data[0] = BS_LP_dile; + break; + case HX_RAWDATA: + case HX_BPN_RAWDATA: + case HX_SC: + tmp_data[0] = BS_RAWDATA; + break; + case HX_WT_NOISE: + case HX_ABS_NOISE: + tmp_data[0] = BS_NOISE; + break; + default: + tmp_data[0] = BS_OPENSHORT; + break; + } + himax_mcu_register_write(ts, tmp_addr, 4, tmp_data, 0); +} + +static void himax_neg_noise_sup(struct himax_ts_data *ts, uint8_t *data) +{ + uint8_t tmp_addr[4] = { 0 }; + uint8_t tmp_data[4] = { 0 }; + + /*0x10007FD8 Check support negative value or not */ + himax_parse_assign_cmd(addr_neg_noise_sup, tmp_addr, sizeof(tmp_addr)); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + + if ((tmp_data[3] & 0x04) == 0x04) { + himax_parse_assign_cmd(data_neg_noise, tmp_data, sizeof(tmp_data)); + data[2] = tmp_data[2]; + data[3] = tmp_data[3]; + } else + I("%s Not support negative noise\n", __func__); +} + +static void himax_set_N_frame(struct himax_ts_data *ts, uint16_t Nframe, uint8_t checktype) +{ + uint8_t tmp_addr[4] = { 0 }; + uint8_t tmp_data[4] = { 0 }; + + himax_bank_search_set(ts, Nframe, checktype); + + /*IIR MAX - 0x10007294*/ + himax_parse_assign_cmd(addr_set_frame_addr, tmp_addr, sizeof(tmp_addr)); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = (uint8_t)((Nframe & 0xFF00) >> 8); + tmp_data[0] = (uint8_t)(Nframe & 0x00FF); + himax_mcu_register_write(ts, tmp_addr, 4, tmp_data, 0); + + if (checktype == HX_WT_NOISE || checktype == HX_ABS_NOISE || checktype == HX_LP_WT_NOISE || + checktype == HX_LP_ABS_NOISE) + himax_neg_noise_sup(ts, tmp_data); + + himax_mcu_register_write(ts, tmp_addr, 4, tmp_data, 0); +} + +/*Normal Threshold*/ +static void himax_get_noise_base(struct himax_ts_data *ts, uint8_t checktype) +{ + uint8_t tmp_addr[4] = { 0 }; + uint8_t tmp_data[4] = { 0 }; + + switch (checktype) { + case HX_WT_NOISE: + himax_parse_assign_cmd(addr_normal_noise_thx, tmp_addr, sizeof(tmp_addr)); + break; + case HX_LP_WT_NOISE: + himax_parse_assign_cmd(addr_lpwug_noise_thx, tmp_addr, sizeof(tmp_addr)); + break; + default: + I("%s Not support type\n", __func__); + } + + /*normal : 0x1000708F, LPWUG:0x10007093*/ + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + NOISEMAX = tmp_data[3]; + + himax_parse_assign_cmd(addr_recal_thx, tmp_addr, sizeof(tmp_addr)); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + g_recal_thx = tmp_data[2]; /*0x10007092*/ + I("%s: NOISEMAX=%d, g_recal_thx = %d\n", __func__, NOISEMAX, g_recal_thx); +} + +/*Palm Number*/ +static uint16_t himax_get_palm_num(struct himax_ts_data *ts) +{ + uint8_t tmp_addr[4] = { 0 }; + uint8_t tmp_data[4] = { 0 }; + uint16_t palm_num; + + himax_parse_assign_cmd(addr_palm_num, tmp_addr, sizeof(tmp_addr)); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + palm_num = tmp_data[3]; /*0x100070AB*/ + I("%s: palm_num = %d ", __func__, palm_num); + + return palm_num; +} + +static int himax_get_noise_weight_test(struct himax_ts_data *ts, uint8_t checktype) +{ + uint8_t tmp_addr[4] = { 0 }; + uint8_t tmp_data[4] = { 0 }; + uint16_t weight = 0; + uint16_t value = 0; + + himax_parse_assign_cmd(addr_weight_sup, tmp_addr, sizeof(tmp_addr)); + + /*0x100072C8 weighting value*/ + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + if (tmp_data[3] != tmp_addr[1] || tmp_data[2] != tmp_addr[0]) + return FW_NOT_READY; + + value = (tmp_data[1] << 8) | tmp_data[0]; + I("%s: value = %d, %d, %d ", __func__, value, tmp_data[2], tmp_data[3]); + + switch (checktype) { + case HX_WT_NOISE: + himax_parse_assign_cmd(addr_normal_weight_a, tmp_addr, sizeof(tmp_addr)); + break; + case HX_LP_WT_NOISE: + himax_parse_assign_cmd(addr_lpwug_weight_a, tmp_addr, sizeof(tmp_addr)); + break; + default: + I("%s Not support type\n", __func__); + } + + /*Normal:0x1000709C, LPWUG:0x100070A0 weighting threshold*/ + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + weight = tmp_data[0]; + + himax_parse_assign_cmd(addr_weight_b, tmp_addr, sizeof(tmp_addr)); + + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + weight = tmp_data[1] * weight; /*0x10007095 weighting threshold*/ + I("%s: weight = %d ", __func__, weight); + + if (value > weight) + return ERR_TEST_FAIL; + else + return 0; +} + +static uint32_t himax_check_mode(struct himax_ts_data *ts, uint8_t checktype) +{ + uint8_t tmp_data[4] = { 0 }; + uint8_t wait_pwd[2] = { 0 }; + + switch (checktype) { + case HX_SORTING: + wait_pwd[0] = PWD_SORTING_END; + wait_pwd[1] = PWD_SORTING_END; + break; + case HX_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HX_MICRO_OPEN: + wait_pwd[0] = PWD_MICRO_OPEN_END; + wait_pwd[1] = PWD_MICRO_OPEN_END; + break; + case HX_SHORT: + wait_pwd[0] = PWD_SHORT_END; + wait_pwd[1] = PWD_SHORT_END; + break; + case HX_RAWDATA: + case HX_BPN_RAWDATA: + case HX_SC: + case HX_GAPTEST_RAW: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + + case HX_WT_NOISE: + case HX_ABS_NOISE: + wait_pwd[0] = PWD_NOISE_END; + wait_pwd[1] = PWD_NOISE_END; + break; + + case HX_ACT_IDLE_RAWDATA: + case HX_ACT_IDLE_BPN_RAWDATA: + case HX_ACT_IDLE_NOISE: + wait_pwd[0] = PWD_ACT_IDLE_END; + wait_pwd[1] = PWD_ACT_IDLE_END; + break; + + case HX_LP_RAWDATA: + case HX_LP_BPN_RAWDATA: + case HX_LP_ABS_NOISE: + case HX_LP_WT_NOISE: + wait_pwd[0] = PWD_LP_END; + wait_pwd[1] = PWD_LP_END; + break; + case HX_LP_IDLE_RAWDATA: + case HX_LP_IDLE_BPN_RAWDATA: + case HX_LP_IDLE_NOISE: + wait_pwd[0] = PWD_LP_IDLE_END; + wait_pwd[1] = PWD_LP_IDLE_END; + break; + + default: + E("Wrong type=%d\n", checktype); + break; + } + + if (ts->core_fp.fp_check_sorting_mode != NULL) + ts->core_fp.fp_check_sorting_mode(ts, tmp_data); + + if ((wait_pwd[0] == tmp_data[0]) && (wait_pwd[1] == tmp_data[1])) { + I("Change to mode=%s\n", g_himax_inspection_mode[checktype]); + return 0; + } else { + return 1; + } +} + +#define TEMP_LOG "%s:%s,tmp_data[0]=%x,tmp_data[1]=%x,tmp_data[2]=%x,tmp_data[3]=%x\n" + +static uint32_t himax_wait_sorting_mode(struct himax_ts_data *ts, uint8_t checktype) +{ + uint8_t tmp_addr[4] = { 0 }; + uint8_t tmp_data[4] = { 0 }; + uint8_t wait_pwd[2] = { 0 }; + int count = 0; + + switch (checktype) { + case HX_SORTING: + wait_pwd[0] = PWD_SORTING_END; + wait_pwd[1] = PWD_SORTING_END; + break; + case HX_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HX_MICRO_OPEN: + wait_pwd[0] = PWD_MICRO_OPEN_END; + wait_pwd[1] = PWD_MICRO_OPEN_END; + break; + case HX_SHORT: + wait_pwd[0] = PWD_SHORT_END; + wait_pwd[1] = PWD_SHORT_END; + break; + case HX_RAWDATA: + case HX_BPN_RAWDATA: + case HX_SC: + case HX_GAPTEST_RAW: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HX_WT_NOISE: + case HX_ABS_NOISE: + wait_pwd[0] = PWD_NOISE_END; + wait_pwd[1] = PWD_NOISE_END; + break; + case HX_ACT_IDLE_RAWDATA: + case HX_ACT_IDLE_BPN_RAWDATA: + case HX_ACT_IDLE_NOISE: + wait_pwd[0] = PWD_ACT_IDLE_END; + wait_pwd[1] = PWD_ACT_IDLE_END; + break; + + case HX_LP_RAWDATA: + case HX_LP_BPN_RAWDATA: + case HX_LP_ABS_NOISE: + case HX_LP_WT_NOISE: + wait_pwd[0] = PWD_LP_END; + wait_pwd[1] = PWD_LP_END; + break; + case HX_LP_IDLE_RAWDATA: + case HX_LP_IDLE_BPN_RAWDATA: + case HX_LP_IDLE_NOISE: + wait_pwd[0] = PWD_LP_IDLE_END; + wait_pwd[1] = PWD_LP_IDLE_END; + break; + + default: + I("No Change Mode and now type=%d\n", checktype); + break; + } + + do { + if (ts->core_fp.fp_check_sorting_mode != NULL) + ts->core_fp.fp_check_sorting_mode(ts, tmp_data); + if ((wait_pwd[0] == tmp_data[0]) && (wait_pwd[1] == tmp_data[1])) + return HX_INSP_OK; + + himax_parse_assign_cmd(addr_cs_central_state, tmp_addr, sizeof(tmp_addr)); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + //I(TEMP_LOG, __func__, "0x900000A8", + // tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + himax_parse_assign_cmd(addr_flag_reset_event, tmp_addr, sizeof(tmp_addr)); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + //I(TEMP_LOG, __func__, "0x900000E4", + // tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + himax_parse_assign_cmd(addr_fw_dbg_msg_addr, tmp_addr, sizeof(tmp_addr)); + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false); + //I(TEMP_LOG, __func__, "0x10007F40", + // tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + I("Now retry %d times!\n", count++); + msleep(50); + } while (count < 50); + + return HX_INSP_ESWITCHMODE; +} + +/* HX_GAP START gap test function */ +/* extern int himax_write_to_ic_flash_flow(uint32_t start_addr,*/ +/* uint32_t *write_data, uint32_t write_len);*/ + +static int himax_gap_test_vertical_setting(struct himax_ts_data *ts) +{ + ts->gap_vertical_part[0] = 0; + ts->gap_vertical_part[1] = 4; + ts->gap_vertical_part[2] = 8; + + return NO_ERR; +} + +static void himax_cal_gap_data_vertical(struct himax_ts_data *ts, int start, int end_idx, + int direct, uint32_t *org_raw, uint32_t *result_raw) +{ + int i = 0; + int rx_num = ts->ic_data->HX_RX_NUM; + + I("%s:start=%d,end_idx=%d\n", __func__, start, end_idx); + + for (i = start; i < (start + rx_num * end_idx); i++) { + if (direct == 0) { /* up - down */ + if (i < start + rx_num) + result_raw[i] = 0; + else + result_raw[i] = org_raw[i - rx_num] - org_raw[i]; + + } else { /* down - up */ + if (i > (start + rx_num * (end_idx - 1) - 1)) + result_raw[i] = 0; + else + result_raw[i] = org_raw[i + rx_num] - org_raw[i]; + } + } +} + +static int himax_gap_test_vertical_raw(struct himax_ts_data *ts, int test_type, uint32_t *org_raw) +{ + int i_partial = 0; + int tmp_start = 0; + int tmp_end_idx = 0; + uint32_t *result_raw = NULL; + int i = 0; + int ret_val = NO_ERR; + + int tx_num = ts->ic_data->HX_TX_NUM; + int rx_num = ts->ic_data->HX_RX_NUM; + + ts->gap_vertical_part = kcalloc(ts->gap_vertical_partial, sizeof(int), GFP_KERNEL); + if (ts->gap_vertical_part == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + + result_raw = kcalloc(tx_num * rx_num, sizeof(uint32_t), GFP_KERNEL); + if (result_raw == NULL) { + E("%s: Memory allocation falied!\n", __func__); + ret_val = MEM_ALLOC_FAIL; + goto alloc_result_raw_failed; + } + + himax_gap_test_vertical_setting(ts); + + I("Print vertical ORG RAW\n"); + for (i = 0; i < tx_num * rx_num; i++) { + I("%04d,", org_raw[i]); + if (i > 0 && i % rx_num == (rx_num - 1)) + I("\n"); + } + + for (i_partial = 0; i_partial < ts->gap_vertical_partial; i_partial++) { + tmp_start = ts->gap_vertical_part[i_partial] * rx_num; + if (i_partial + 1 == ts->gap_vertical_partial) + tmp_end_idx = tx_num - ts->gap_vertical_part[i_partial]; + else + tmp_end_idx = ts->gap_vertical_part[i_partial + 1] - + ts->gap_vertical_part[i_partial]; + + if (i_partial % 2 == 0) + himax_cal_gap_data_vertical(ts, tmp_start, tmp_end_idx, 0, org_raw, + result_raw); + else + himax_cal_gap_data_vertical(ts, tmp_start, tmp_end_idx, 1, org_raw, + result_raw); + } + + I("Print Vertical New RAW\n"); + for (i = 0; i < tx_num * rx_num; i++) { + I("%04d,", result_raw[i]); + if (i > 0 && i % rx_num == (rx_num - 1)) + I("\n"); + } + + for (i = 0; i < tx_num * rx_num; i++) { + if (result_raw[i] < ts->inspection_criteria[IDX_GAP_VER_RAWMIN][i] && + result_raw[i] > ts->inspection_criteria[IDX_GAP_VER_RAWMAX][i]) { + ret_val = NO_ERR - i; + break; + } + } + + /* himax_write_to_ic_flash_flow(0x1A000,result_raw,tx_num*rx_num); */ + kfree(result_raw); +alloc_result_raw_failed: + kfree(ts->gap_vertical_part); + ts->gap_vertical_part = NULL; + + return ret_val; +} + +static int himax_gap_test_horizontal_setting(struct himax_ts_data *ts) +{ + ts->gap_horizontal_part[0] = 0; + ts->gap_horizontal_part[1] = 8; + ts->gap_horizontal_part[2] = 24; + + return NO_ERR; +} + +static void himax_cal_gap_data_horizontal(struct himax_ts_data *ts, int start, int end_idx, + int direct, uint32_t *org_raw, uint32_t *result_raw) +{ + int i = 0; + int j = 0; + int rx_num = ts->ic_data->HX_RX_NUM; + int tx_num = ts->ic_data->HX_TX_NUM; + + I("start=%d,end_idx=%d\n", start, end_idx); + + for (j = 0; j < tx_num; j++) { + for (i = (start + (j * rx_num)); i < (start + (j * rx_num) + end_idx); i++) { + /* left - right */ + if (direct == 0) { + if (i == (start + (j * rx_num))) + result_raw[i] = 0; + else + result_raw[i] = org_raw[i - 1] - org_raw[i]; + + } else { /* right - left */ + if (i == ((start + (j * rx_num) + end_idx) - 1)) + result_raw[i] = 0; + else + result_raw[i] = org_raw[i + 1] - org_raw[i]; + } + } + } +} + +static int himax_gap_test_honrizontal_raw(struct himax_ts_data *ts, int test_type, uint32_t *raw) +{ + int rx_num = ts->ic_data->HX_RX_NUM; + int tx_num = ts->ic_data->HX_TX_NUM; + int tmp_start = 0; + int tmp_end_idx = 0; + int i_partial = 0; + uint32_t *result_raw; + int i = 0; + int ret_val = NO_ERR; + + ts->gap_horizontal_part = kcalloc(ts->gap_horizontal_partial, sizeof(int), GFP_KERNEL); + if (ts->gap_horizontal_part == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + + result_raw = kcalloc(tx_num * rx_num, sizeof(uint32_t), GFP_KERNEL); + if (result_raw == NULL) { + E("%s: Memory allocation falied!\n", __func__); + ret_val = MEM_ALLOC_FAIL; + goto alloc_result_raw_failed; + } + + himax_gap_test_horizontal_setting(ts); + + I("Print Horizontal ORG RAW\n"); + for (i = 0; i < tx_num * rx_num; i++) { + I("%04d,", raw[i]); + if (i > 0 && i % rx_num == (rx_num - 1)) + I("\n"); + } + + for (i_partial = 0; i_partial < ts->gap_horizontal_partial; i_partial++) { + tmp_start = ts->gap_horizontal_part[i_partial]; + if (i_partial + 1 == ts->gap_horizontal_partial) + tmp_end_idx = rx_num - ts->gap_horizontal_part[i_partial]; + else + tmp_end_idx = ts->gap_horizontal_part[i_partial + 1] - + ts->gap_horizontal_part[i_partial]; + + if (i_partial % 2 == 0) + himax_cal_gap_data_horizontal(ts, tmp_start, tmp_end_idx, 0, raw, + result_raw); + else + himax_cal_gap_data_horizontal(ts, tmp_start, tmp_end_idx, 1, raw, + result_raw); + } + I("Print Horizontal New RAW\n"); + for (i = 0; i < tx_num * rx_num; i++) { + I("%04d,", result_raw[i]); + if (i > 0 && i % rx_num == (rx_num - 1)) + I("\n"); + } + + for (i = 0; i < tx_num * rx_num; i++) { + if (result_raw[i] < ts->inspection_criteria[IDX_GAP_HOR_RAWMIN][i] && + result_raw[i] > ts->inspection_criteria[IDX_GAP_HOR_RAWMAX][i]) { + ret_val = NO_ERR - i; + break; + } + } + + /* himax_write_to_ic_flash_flow(0x1A800,result_raw,tx_num*rx_num); */ + kfree(result_raw); +alloc_result_raw_failed: + kfree(ts->gap_horizontal_part); + ts->gap_horizontal_part = NULL; + + return ret_val; +} + +static uint32_t himax_data_campare(struct himax_ts_data *ts, uint8_t checktype, uint32_t *RAW, + int ret_val) +{ + int i = 0; + int idx_max = 0; + int idx_min = 0; + int block_num = ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM; + uint16_t palm_num = 0; + uint16_t noise_count = 0; + + switch (checktype) { + case HX_SORTING: + idx_min = IDX_SORTMIN; + break; + case HX_OPEN: + idx_max = IDX_OPENMAX; + idx_min = IDX_OPENMIN; + break; + + case HX_MICRO_OPEN: + idx_max = IDX_M_OPENMAX; + idx_min = IDX_M_OPENMIN; + break; + + case HX_SHORT: + idx_max = IDX_SHORTMAX; + idx_min = IDX_SHORTMIN; + break; + + case HX_RAWDATA: + idx_max = IDX_RAWMAX; + idx_min = IDX_RAWMIN; + break; + + case HX_BPN_RAWDATA: + idx_max = IDX_BPN_RAWMAX; + idx_min = IDX_BPN_RAWMIN; + break; + case HX_SC: + idx_max = IDX_SCMAX; + idx_min = IDX_SCMIN; + break; + case HX_WT_NOISE: + idx_max = IDX_WT_NOISEMAX; + idx_min = IDX_WT_NOISEMIN; + break; + case HX_ABS_NOISE: + idx_max = IDX_ABS_NOISEMAX; + idx_min = IDX_ABS_NOISEMIN; + break; + case HX_GAPTEST_RAW: + break; + + case HX_ACT_IDLE_RAWDATA: + idx_max = IDX_ACT_IDLE_RAWDATA_MAX; + idx_min = IDX_ACT_IDLE_RAWDATA_MIN; + break; + + case HX_ACT_IDLE_BPN_RAWDATA: + idx_max = IDX_ACT_IDLE_RAW_BPN_MAX; + idx_min = IDX_ACT_IDLE_RAW_BPN_MIN; + break; + + case HX_ACT_IDLE_NOISE: + idx_max = IDX_ACT_IDLE_NOISE_MAX; + idx_min = IDX_ACT_IDLE_NOISE_MIN; + break; + + case HX_LP_RAWDATA: + idx_max = IDX_LP_RAWDATA_MAX; + idx_min = IDX_LP_RAWDATA_MIN; + break; + + case HX_LP_BPN_RAWDATA: + idx_max = IDX_LP_RAW_BPN_MAX; + idx_min = IDX_LP_RAW_BPN_MIN; + break; + + case HX_LP_WT_NOISE: + idx_max = IDX_LP_WT_NOISEMAX; + idx_min = IDX_LP_WT_NOISEMIN; + break; + + case HX_LP_ABS_NOISE: + idx_max = IDX_LP_NOISE_ABS_MAX; + idx_min = IDX_LP_NOISE_ABS_MIN; + break; + + case HX_LP_IDLE_RAWDATA: + idx_max = IDX_LP_IDLE_RAWDATA_MAX; + idx_min = IDX_LP_IDLE_RAWDATA_MIN; + break; + + case HX_LP_IDLE_BPN_RAWDATA: + idx_max = IDX_LP_IDLE_RAW_BPN_MAX; + idx_min = IDX_LP_IDLE_RAW_BPN_MIN; + break; + + case HX_LP_IDLE_NOISE: + idx_max = IDX_LP_IDLE_NOISE_MAX; + idx_min = IDX_LP_IDLE_NOISE_MIN; + break; + + default: + E("Wrong type=%d\n", checktype); + break; + } + + /*data process*/ + switch (checktype) { + case HX_SORTING: + for (i = 0; i < block_num; i++) + ts->inspection_criteria[idx_max][i] = 999999; + break; + case HX_BPN_RAWDATA: + case HX_ACT_IDLE_BPN_RAWDATA: + case HX_LP_BPN_RAWDATA: + case HX_LP_IDLE_BPN_RAWDATA: + for (i = 0; i < block_num; i++) + RAW[i] = (int)RAW[i] * 100 / ts->dc_max; + break; + case HX_SC: + for (i = 0; i < block_num; i++) { + RAW[i] = ((int)RAW[i] - ts->inspection_criteria[IDX_SC_GOLDEN][i]) * 100 / + ts->inspection_criteria[IDX_SC_GOLDEN][i]; + } + break; + } + + /*data campare*/ + switch (checktype) { + case HX_GAPTEST_RAW: + if (himax_gap_test_vertical_raw(ts, HX_GAPTEST_RAW, RAW) != NO_ERR) { + E("%s: HX_GAPTEST_RAW FAIL\n", __func__); + ret_val |= 1 << (checktype + ERR_SFT); + break; + } + if (himax_gap_test_honrizontal_raw(ts, HX_GAPTEST_RAW, RAW) != NO_ERR) { + E("%s: HX_GAPTEST_RAW FAIL\n", __func__); + ret_val |= 1 << (checktype + ERR_SFT); + break; + } + break; + + case HX_WT_NOISE: + case HX_LP_WT_NOISE: + noise_count = 0; + himax_get_noise_base(ts, checktype); + palm_num = himax_get_palm_num(ts); + for (i = 0; i < (ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > NOISEMAX) + noise_count++; + } + I("noise_count=%d\n", noise_count); + if (noise_count > palm_num) { + E("%s: noise test FAIL\n", __func__); + ret_val |= 1 << (checktype + ERR_SFT); + break; + } + snprintf(ts->start_log, 256 * sizeof(char), "\n Threshold = %d\n", NOISEMAX); + /*Check weightingt*/ + if (himax_get_noise_weight_test(ts, checktype) < 0) { + I("%s: %s FAIL %X\n", __func__, g_himax_inspection_mode[checktype], + ret_val); + ret_val |= 1 << (checktype + ERR_SFT); + break; + } + + /*Check negative side noise*/ + for (i = 0; i < block_num; i++) { + if ((int)RAW[i] > (ts->inspection_criteria[idx_max][i] * NOISEMAX / 100) || + (int)RAW[i] < + (ts->inspection_criteria[idx_min][i] * g_recal_thx / 100)) { + E(FAIL_IN_INDEX, __func__, g_himax_inspection_mode[checktype], i); + ret_val |= 1 << (checktype + ERR_SFT); + break; + } + } + break; + + case HX_LP_IDLE_RAWDATA: + case HX_LP_IDLE_BPN_RAWDATA: + case HX_LP_IDLE_NOISE: + case HX_ACT_IDLE_RAWDATA: + case HX_ACT_IDLE_BPN_RAWDATA: + case HX_ACT_IDLE_NOISE: + block_num = ts->ic_data->ic_adc_num; + fallthrough; + case HX_SORTING: + case HX_OPEN: + case HX_MICRO_OPEN: + case HX_SHORT: + case HX_RAWDATA: + case HX_BPN_RAWDATA: + case HX_SC: + case HX_ABS_NOISE: + case HX_LP_RAWDATA: + case HX_LP_BPN_RAWDATA: + case HX_LP_ABS_NOISE: + for (i = 0; i < block_num; i++) { + if ((int)RAW[i] > ts->inspection_criteria[idx_max][i] || + (int)RAW[i] < ts->inspection_criteria[idx_min][i]) { + E(FAIL_IN_INDEX, __func__, g_himax_inspection_mode[checktype], i); + ret_val |= 1 << (checktype + ERR_SFT); + break; + } + } + break; + + default: + E("Wrong type=%d\n", checktype); + break; + } + + I("%s: %s %s\n", __func__, g_himax_inspection_mode[checktype], + (ret_val == HX_INSP_OK) ? "PASS" : "FAIL"); + + return ret_val; +} + +static int himax_get_max_dc(struct himax_ts_data *ts) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + int dc_max = 0; + + himax_parse_assign_cmd(addr_max_dc, tmp_addr, sizeof(tmp_addr)); + + himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, 0); + I("%s: tmp_data[0]=%x,tmp_data[1]=%x\n", __func__, tmp_data[0], tmp_data[1]); + + dc_max = tmp_data[1] << 8 | tmp_data[0]; + I("%s: dc max = %d\n", __func__, dc_max); + return dc_max; +} + +/* HX_GAP END*/ +static uint32_t mpTestFunc(struct himax_ts_data *ts, uint8_t checktype, uint32_t datalen) +{ + uint32_t len = 0; + uint32_t *RAW = NULL; + int n_frame = 0; + uint32_t ret_val = HX_INSP_OK; + + /*uint16_t* pInspectGridData = &gInspectGridData[0];*/ + /*uint16_t* pInspectNoiseData = &gInspectNoiseData[0];*/ + + I("Now Check type = %d\n", checktype); + + RAW = kcalloc(datalen, sizeof(uint32_t), GFP_KERNEL); + if (RAW == NULL) { + E("%s, Failed to allocate memory\n", __func__); + return HX_INSP_MEMALLCTFAIL; + } + + if (himax_check_mode(ts, checktype)) { + /*himax_check_mode(ts, checktype);*/ + + I("Need Change Mode ,target=%s\n", g_himax_inspection_mode[checktype]); + + ts->core_fp.fp_sense_off(ts, true); + + if (ts->core_fp.fp_reload_disable != NULL) + ts->core_fp.fp_reload_disable(ts, 1); + + himax_switch_mode_inspection(ts, checktype); + + switch (checktype) { + case HX_WT_NOISE: + case HX_ABS_NOISE: + n_frame = NOISEFRAME; + break; + case HX_ACT_IDLE_RAWDATA: + case HX_ACT_IDLE_NOISE: + case HX_ACT_IDLE_BPN_RAWDATA: + n_frame = NORMAL_IDLE_RAWDATA_NOISEFRAME; + break; + case HX_LP_RAWDATA: + case HX_LP_BPN_RAWDATA: + n_frame = LP_RAWDATAFRAME; + break; + case HX_LP_WT_NOISE: + case HX_LP_ABS_NOISE: + n_frame = LP_NOISEFRAME; + break; + case HX_LP_IDLE_RAWDATA: + case HX_LP_IDLE_BPN_RAWDATA: + n_frame = LP_IDLE_RAWDATAFRAME; + break; + case HX_LP_IDLE_NOISE: + n_frame = LP_IDLE_NOISEFRAME; + break; + default: + n_frame = OTHERSFRAME; + } + himax_set_N_frame(ts, n_frame, checktype); + + ts->core_fp.fp_sense_on(ts, 0x01); + } + + ret_val |= himax_wait_sorting_mode(ts, checktype); + if (ret_val) { + E("%s: himax_wait_sorting_mode FAIL\n", __func__); + ret_val |= (1 << (checktype + ERR_SFT)); + goto fail_wait_sorting_mode; + } + himax_switch_data_type(ts, checktype); + + ret_val |= himax_get_rawdata(ts, RAW, datalen); + if (ret_val) { + E("%s: himax_get_rawdata FAIL\n", __func__); + ret_val |= (1 << (checktype + ERR_SFT)); + goto fail_get_rawdata; + } + + /*get Max DC from FW*/ + ts->dc_max = himax_get_max_dc(ts); + + /* back to normal */ + himax_switch_data_type(ts, HX_BACK_NORMAL); + + I("%s: Init OK, start to test!\n", __func__); + + len += snprintf(ts->start_log + len, 256 * sizeof(char), "\n%s%s\n", + g_himax_inspection_mode[checktype], ": data as follow!\n"); + + ret_val |= himax_data_campare(ts, checktype, RAW, ret_val); + + himax_get_arraydata_edge(ts, RAW); + + len += snprintf(ts->start_log + len, 256 * sizeof(char) - len, "\n arraydata_min1 = %d,", + arraydata_min1); + len += snprintf(ts->start_log + len, 256 * sizeof(char) - len, " arraydata_min2 = %d,", + arraydata_min2); + len += snprintf(ts->start_log + len, 256 * sizeof(char) - len, " arraydata_min3 = %d,", + arraydata_min3); + len += snprintf(ts->start_log + len, 256 * sizeof(char) - len, "\n arraydata_max1 = %d,", + arraydata_max1); + len += snprintf(ts->start_log + len, 256 * sizeof(char) - len, " arraydata_max2 = %d,", + arraydata_max2); + snprintf(ts->start_log + len, 256 * sizeof(char) - len, " arraydata_max3 = %d\n", + arraydata_max3); + + if (!ret_val) { /*PASS*/ + snprintf(ts->rslt_log, 256 * sizeof(char), "\n%s%s\n", + g_himax_inspection_mode[checktype], ":Test Pass!"); + I("pass write log\n"); + } else { /*FAIL*/ + snprintf(ts->rslt_log, 256 * sizeof(char), "\n%s%s\n", + g_himax_inspection_mode[checktype], ":Test Fail!"); + I("fail write log\n"); + } + + hx_test_data_get(ts, RAW, ts->start_log, ts->rslt_log, checktype); +fail_get_rawdata: +fail_wait_sorting_mode: + kfree(RAW); + return ret_val; +} + +/* claculate 10's power function */ +static int himax_power_cal(int pow, int number) +{ + int i = 0; + int result = 1; + + for (i = 0; i < pow; i++) + result *= 10; + result = result * number; + + return result; +} + +/* String to int */ +static int hiamx_parse_str2int(char *str) +{ + int i = 0; + int temp_cal = 0; + int result = 0; + unsigned int str_len = strlen(str); + int negtive_flag = 0; + + for (i = 0; i < str_len; i++) { + if (str[i] != '-' && str[i] > '9' && str[i] < '0') { + E("%s: Parsing fail!\n", __func__); + result = -9487; + negtive_flag = 0; + break; + } + if (str[i] == '-') { + negtive_flag = 1; + continue; + } + temp_cal = str[i] - '0'; + result += himax_power_cal(str_len - i - 1, temp_cal); + /* str's the lowest char is the number's the highest number + * So we should reverse this number before using the power + * function + * -1: starting number is from 0 ex:10^0 = 1,10^1=10 + */ + } + + if (negtive_flag == 1) + result = 0 - result; + + return result; +} + +/* get idx of criteria whe parsing file */ +static int hx_find_crtra_id(struct himax_ts_data *ts, char *input) +{ + int i = 0; + int result = 0; + + for (i = 0; i < ts->hx_criteria_size; i++) { + if (strcmp(g_hx_inspt_crtra_name[i], input) == 0) { + result = i; + I("find the str=%s,idx=%d\n", g_hx_inspt_crtra_name[i], i); + break; + } + } + if (i > (ts->hx_criteria_size - 1)) { + E("%s: find Fail!\n", __func__); + return LENGTH_FAIL; + } + + return result; +} + +static int hx_print_crtra_after_parsing(struct himax_ts_data *ts) +{ + int i = 0, j = 0; + int all_mut_len = ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM; + + for (i = 0; i < ts->hx_criteria_size; i++) { + I("Now is %s\n", g_hx_inspt_crtra_name[i]); + if (ts->inspt_crtra_flag[i] == 1) { + for (j = 0; j < all_mut_len; j++) { + I("%d, ", ts->inspection_criteria[i][j]); + if (j % 16 == 15) + PI("\n"); + } + } else { + I("No this Item in this criteria file!\n"); + } + PI("\n"); + } + + return 0; +} + +static int hx_crtra_get(struct himax_ts_data *ts, char *result, int himax_count_type, + int comprae_data) +{ + int temp = 0; + + temp = hiamx_parse_str2int(result); + + if (temp != -9487) + ts->inspection_criteria[himax_count_type][comprae_data] = temp; + else { + I("%s: Parsing Fail in %d, rslt = %d\n", __func__, comprae_data, temp); + return HX_INSP_EFILE; + } + + if (ts->debug_log_level & BIT(4)) { + /* dbg:print all of criteria from parsing file */ + hx_print_crtra_after_parsing(ts); + } + + return HX_INSP_OK; +} + +static int himax_parse_criteria_str(struct himax_ts_data *ts, char *str_data, int hx_str_len) +{ + int err = HX_INSP_OK; + char result[100] = { 0 }; + char str_rslt[100] = { 0 }; + int str_rslt_nm = 0; + int result_nm = 0; + char str_len = 0; + char *str_addr = NULL; + int str_flag = 1; + int i, j, k = 0; + int crtra_id = 0; + int mul_num = ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM; + int flag = 1; + int temp; + + I("%s,Entering\n", __func__); + + for (str_rslt_nm = 0; str_rslt_nm < hx_str_len; str_rslt_nm++) + str_rslt[str_rslt_nm] = *(str_data + str_rslt_nm); + + crtra_id = hx_find_crtra_id(ts, str_rslt); + if (crtra_id == -1) { + E("Please check criteria file again!\n"); + return HX_INSP_EFILE; + } + ts->inspt_crtra_flag[crtra_id] = 1; + + str_data = str_data + hx_str_len + 1; + + for (i = 0; i < mul_num; i++) { + if (i <= mul_num - 2) { + while (flag) { + if (*(str_data + flag) == ',') { + str_addr = str_data + flag; + flag = 1; + break; + } + flag++; + } + if (str_addr == NULL) + continue; + str_flag = 1; + str_len = str_addr - str_data; + for (j = 1; j <= str_len; j++) { + if ((*(str_data + j) == '\r' || *(str_data + j) == '\n')) { + memset(result, 0, 100); + for (k = 0; k < j; k++) + result[k] = *(str_data + k); + str_flag = 0; + break; + } + } + if (str_flag) { + memset(result, 0, 100); + for (k = 0; k < str_len; k++) + result[k] = *(str_data + k); + } + err = hx_crtra_get(ts, result, crtra_id, i); + if (err != HX_INSP_OK) { + E("%s:Get crrteria Fail!!\n", __func__); + return HX_INSP_EFILE; + } + str_data = str_addr + 1; + } else { + temp = 1; + while (*(str_data + temp) <= '9' && *(str_data + temp) >= '0') + temp++; + str_len = temp; + memset(result, 0, 100); + for (result_nm = 0; result_nm < str_len; result_nm++) + result[result_nm] = *(str_data + result_nm); + + err = hx_crtra_get(ts, result, crtra_id, mul_num - 1); + if (err != HX_INSP_OK) { + E("%s:Get crrteria Fail!\n", __func__); + return HX_INSP_EFILE; + } + } + } + + I("%s,END\n", __func__); + return err; + /* parsing Criteria end */ +} + +static int himax_test_item_parse(struct himax_ts_data *ts, char *str_data, int str_size) +{ + int size = str_size; + char *str_ptr = str_data; + char *end_ptr = NULL; + int i = 0; + int ret = HX_INSP_EFILE; + + I("%s,str_data: %p, str_size: %d\n", __func__, str_data, str_size); + + do { + str_ptr = strnstr(str_ptr, "HIMAX", size); + end_ptr = strnstr(str_ptr, "\x0d\x0a", size); + if (str_ptr != NULL && end_ptr != NULL) { + while (g_himax_inspection_mode[i]) { + if (strncmp(str_ptr, g_himax_inspection_mode[i], + end_ptr - str_ptr) == 0) { + I("%s,Find item : %s\n", __func__, + g_himax_inspection_mode[i]); + ts->test_item_flag[i] = 1; + ret = HX_INSP_OK; + break; + } + i++; + } + size = str_size - (end_ptr - str_data); + str_ptr = end_ptr++; + i = 0; + } else { + I("%s,Can't find %s or %s\n", __func__, "HIMAX", "\x0d\x0a"); + break; + } + } while (size > strlen("HIMAX")); + + return ret; +} + +static int himax_parse_criteria(struct himax_ts_data *ts, const struct firmware *file_entry) +{ + int ret = 0; + int i = 0; + int start_str_len = 0; + char *start_ptr = NULL; + + while (g_hx_inspt_crtra_name[i] != NULL) { + start_ptr = strnstr(file_entry->data, g_hx_inspt_crtra_name[i], file_entry->size); + + if (start_ptr != NULL) { + //I("g_hx_inspt_crtra_name[%d] = %s\n", + // i, g_hx_inspt_crtra_name[i]); + start_str_len = strlen(g_hx_inspt_crtra_name[i]); + + ret |= himax_parse_criteria_str(ts, start_ptr, start_str_len); + } + i++; + } + + return ret; +} + +static int himax_parse_test_dri_file(struct himax_ts_data *ts, const struct firmware *file_entry) +{ + int start_str_len = 0; + int str_size = 0; + char *start_ptr = NULL; + char *end_ptr = NULL; + int i = 0; + int j = 0; + char str[2][60]; /*[0]->Start string, [1]->End string*/ + char *str_tail[2] = { "_Begin]\x0d\x0a", "_End]\x0d\x0a" }; + int ret = HX_INSP_OK; + + while (g_hx_head_str[i]) { + /*compose header string of .dri file*/ + for (j = 0; j < 2; j++) { + strlcpy(str[j], "[", sizeof(str[j])); + strlcat(str[j], g_hx_head_str[i], sizeof(str[j])); + strlcat(str[j], str_tail[j], sizeof(str[j])); + /*I("%s string[%d] : %s\n", __func__, j, str[j]);*/ + } + + /*find each group of .dri file*/ + start_str_len = strlen(str[0]); + start_ptr = strnstr(file_entry->data, str[0], file_entry->size); + end_ptr = strnstr(file_entry->data, str[1], file_entry->size); + + if (start_ptr == NULL || end_ptr == NULL) { + E("%s,Can't find string %s\n", __func__, g_hx_head_str[i]); + } else { + /*parse each sub group string*/ + /*if (strncmp(g_hx_head_str[i], "Project_Info",*/ + /*strlen(g_hx_head_str[i])) == 0) {*/ + /* get project informaion - Not Use*/ + /*}*/ + str_size = end_ptr - start_ptr - start_str_len; + /*I("%s,String Length = %d\n", __func__, str_size);*/ + + if (strncmp(g_hx_head_str[i], "TestItem", strlen(g_hx_head_str[i])) == 0) { + /*get Test Item*/ + I("%s,Start to parse %s\n", __func__, g_hx_head_str[i]); + ret |= himax_test_item_parse(ts, start_ptr + start_str_len, + str_size); + } + /*if (strncmp(g_hx_head_str[i], "TestCriteria_Weight",*/ + /*strlen(g_hx_head_str[i])) == 0) {*/ + /*get Test Criteria Weight - Not Use*/ + /*}*/ + if (strncmp(g_hx_head_str[i], "TestCriteria", strlen(g_hx_head_str[i])) == + 0) { + /*get Test Criteria*/ + I("%s,Start to parse %s\n", __func__, g_hx_head_str[i]); + ret |= himax_parse_criteria(ts, file_entry); + } + } + i++; + } + + return ret; +} + +static void himax_test_item_chk(struct himax_ts_data *ts, int csv_test) +{ + int i = 0; + + if (csv_test) + for (i = 0; i < ts->hx_criteria_item - 1; i++) + ts->test_item_flag[i] = 1; + + ts->test_item_flag[HX_OPEN] &= + (ts->inspt_crtra_flag[IDX_OPENMIN] == 1 && ts->inspt_crtra_flag[IDX_OPENMAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_MICRO_OPEN] &= (ts->inspt_crtra_flag[IDX_M_OPENMIN] == 1 && + ts->inspt_crtra_flag[IDX_M_OPENMAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_SHORT] &= (ts->inspt_crtra_flag[IDX_SHORTMIN] == 1 && + ts->inspt_crtra_flag[IDX_SHORTMAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_RAWDATA] &= + (ts->inspt_crtra_flag[IDX_RAWMIN] == 1 && ts->inspt_crtra_flag[IDX_RAWMAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_BPN_RAWDATA] &= (ts->inspt_crtra_flag[IDX_BPN_RAWMIN] == 1 && + ts->inspt_crtra_flag[IDX_BPN_RAWMAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_SC] &= + (ts->inspt_crtra_flag[IDX_SCMIN] == 1 && ts->inspt_crtra_flag[IDX_SCMAX] == 1 && + ts->inspt_crtra_flag[IDX_SC_GOLDEN] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_WT_NOISE] &= (ts->inspt_crtra_flag[IDX_WT_NOISEMIN] == 1 && + ts->inspt_crtra_flag[IDX_WT_NOISEMAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_ABS_NOISE] &= (ts->inspt_crtra_flag[IDX_ABS_NOISEMIN] == 1 && + ts->inspt_crtra_flag[IDX_ABS_NOISEMAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_SORTING] &= + (ts->inspt_crtra_flag[IDX_SORTMIN] == 1 && ts->inspt_crtra_flag[IDX_SORTMAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_GAPTEST_RAW] &= (ts->inspt_crtra_flag[IDX_GAP_HOR_RAWMAX] == 1 && + ts->inspt_crtra_flag[IDX_GAP_HOR_RAWMIN] == 1 && + ts->inspt_crtra_flag[IDX_GAP_VER_RAWMAX] == 1 && + ts->inspt_crtra_flag[IDX_GAP_VER_RAWMIN] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_ACT_IDLE_RAWDATA] &= + (ts->inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MIN] == 1 && + ts->inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_ACT_IDLE_BPN_RAWDATA] &= + (ts->inspt_crtra_flag[IDX_ACT_IDLE_RAW_BPN_MIN] == 1 && + ts->inspt_crtra_flag[IDX_ACT_IDLE_RAW_BPN_MAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_ACT_IDLE_NOISE] &= + (ts->inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MIN] == 1 && + ts->inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_LP_RAWDATA] &= (ts->inspt_crtra_flag[IDX_LP_RAWDATA_MIN] == 1 && + ts->inspt_crtra_flag[IDX_LP_RAWDATA_MAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_LP_BPN_RAWDATA] &= (ts->inspt_crtra_flag[IDX_LP_RAW_BPN_MIN] == 1 && + ts->inspt_crtra_flag[IDX_LP_RAW_BPN_MAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_LP_WT_NOISE] &= (ts->inspt_crtra_flag[IDX_LP_WT_NOISEMAX] == 1 && + ts->inspt_crtra_flag[IDX_LP_WT_NOISEMIN] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_LP_ABS_NOISE] &= (ts->inspt_crtra_flag[IDX_LP_NOISE_ABS_MAX] == 1 && + ts->inspt_crtra_flag[IDX_LP_NOISE_ABS_MIN] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_LP_IDLE_RAWDATA] &= + (ts->inspt_crtra_flag[IDX_LP_IDLE_RAWDATA_MAX] == 1 && + ts->inspt_crtra_flag[IDX_LP_IDLE_RAWDATA_MIN] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_LP_IDLE_BPN_RAWDATA] &= + (ts->inspt_crtra_flag[IDX_LP_IDLE_RAW_BPN_MIN] == 1 && + ts->inspt_crtra_flag[IDX_LP_IDLE_RAW_BPN_MAX] == 1) ? + 1 : + 0; + + ts->test_item_flag[HX_LP_IDLE_NOISE] &= (ts->inspt_crtra_flag[IDX_LP_IDLE_NOISE_MAX] == 1 && + ts->inspt_crtra_flag[IDX_LP_IDLE_NOISE_MIN] == 1) ? + 1 : + 0; + + ts->do_lpwg_test = + ts->test_item_flag[HX_LP_RAWDATA] | ts->test_item_flag[HX_LP_BPN_RAWDATA] | + ts->test_item_flag[HX_LP_WT_NOISE] | ts->test_item_flag[HX_LP_ABS_NOISE] | + ts->test_item_flag[HX_LP_IDLE_RAWDATA] | + ts->test_item_flag[HX_LP_IDLE_BPN_RAWDATA] | ts->test_item_flag[HX_LP_IDLE_NOISE]; + + for (i = 0; i < ts->hx_criteria_item - 1; i++) + I("g_test_item_flag[%d] = %d\n", i, ts->test_item_flag[i]); +} + +static int hx_get_size_str_arr(struct himax_ts_data *ts, const char **input) +{ + int i = 0; + int result = 0; + + while (input[i] != NULL) + i++; + + result = i; + if (ts->debug_log_level & BIT(4)) + I("There is %d in [0]=%s\n", result, input[0]); + + return result; +} + +static void hx_print_ic_id(struct himax_ts_data *ts) +{ + uint8_t i; + uint32_t len = 0; + char *prt_data = NULL; + + prt_data = kzalloc(sizeof(char) * HX_SZ_ICID, GFP_KERNEL); + if (prt_data == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return; + } + + len += snprintf(prt_data + len, HX_SZ_ICID - len, "IC ID : "); + for (i = 0; i < 13; i++) { + len += snprintf(prt_data + len, HX_SZ_ICID - len, "%02X", + ts->ic_data->vendor_ic_id[i]); + } + len += snprintf(prt_data + len, HX_SZ_ICID - len, "\n"); + + memcpy(&ts->rslt_data[0], prt_data, len); + ts->rslt_data_len = len; + I("%s: g_rslt_data_len=%d!\n", __func__, ts->rslt_data_len); + + kfree(prt_data); +} + +static int himax_self_test_data_init(struct himax_ts_data *ts) +{ + const struct firmware *file_entry = NULL; + char *file_name_1 = "hx_criteria.dri"; + //char *file_name_2 = "hx_criteria.csv"; + int ret = HX_INSP_OK; + int err = 0; + int i = 0; + + /* + * 5: one value will not over than 99999, so get this size of string + * 2: get twice size + */ + ts->one_kind_raw_size = 5 * ts->ic_data->HX_RX_NUM * ts->ic_data->HX_TX_NUM * 2; + + /* get test item and its items of criteria*/ + ts->hx_criteria_item = hx_get_size_str_arr(ts, g_himax_inspection_mode); + ts->hx_criteria_size = hx_get_size_str_arr(ts, g_hx_inspt_crtra_name); + I("There is %d ts->hx_criteria_item and %d HX_CRITERIA_SIZE\n", ts->hx_criteria_item, + ts->hx_criteria_size); + + /* init criteria data*/ + ts->test_item_flag = kcalloc(ts->hx_criteria_item, sizeof(int), GFP_KERNEL); + if (ts->test_item_flag == NULL) { + E("%s,%d: Memory allocation falied!\n", __func__, __LINE__); + ret = HX_INSP_MEMALLCTFAIL; + goto err_malloc_test_item_flag; + } + + ts->inspt_crtra_flag = kcalloc(ts->hx_criteria_size, sizeof(int), GFP_KERNEL); + if (ts->inspt_crtra_flag == NULL) { + E("%s,%d: Memory allocation falied!\n", __func__, __LINE__); + ret = HX_INSP_MEMALLCTFAIL; + goto err_malloc_inspt_crtra_flag; + } + + ts->inspection_criteria = kcalloc(ts->hx_criteria_size, sizeof(int *), GFP_KERNEL); + if (ts->inspection_criteria == NULL) { + E("%s,%d: Memory allocation falied!\n", __func__, __LINE__); + ret = HX_INSP_MEMALLCTFAIL; + goto err_malloc_inspection_criteria; + } + + for (i = 0; i < ts->hx_criteria_size; i++) { + ts->inspection_criteria[i] = kcalloc( + (ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM), sizeof(int), GFP_KERNEL); + if (ts->inspection_criteria[i] == NULL) { + E("%s,%d: Memory allocation %d falied!\n", __func__, __LINE__, i); + ret = HX_INSP_MEMALLCTFAIL; + goto err_malloc_inspection_criteria2; + } + } + + ts->rslt_data_len = 0; + if (ts->rslt_data == NULL) { + ts->rslt_data = kcalloc(ts->one_kind_raw_size, sizeof(char), GFP_KERNEL); + if (ts->rslt_data == NULL) { + E("%s,%d: Memory allocation falied!\n", __func__, __LINE__); + ret = HX_INSP_MEMALLCTFAIL; + goto err_malloc_rslt_data; + } + } + I("%s: initialize g_rslt_data, length = %d\n", __func__, ts->one_kind_raw_size); + memset(ts->rslt_data, 0x00, ts->one_kind_raw_size * sizeof(char)); + + /* default path is /system/etc/firmware */ + /* request criteria file*/ + err = request_firmware(&file_entry, file_name_1, ts->dev); + if (err < 0) { + E("%s,Fail to get %s\n", __func__, file_name_1); + err = request_firmware(&file_entry, ts->pdata->criteria_file_name, ts->dev); + if (err < 0) { + E("%s,Fail to get %s\n", __func__, ts->pdata->criteria_file_name); + I("No criteria file file"); + ret = HX_INSP_EFILE; + goto err_open_criteria_file; + } else { + I("%s,Success to get %s\n", __func__, ts->pdata->criteria_file_name); + /* parsing criteria from file .csv*/ + ret = himax_parse_criteria(ts, file_entry); + release_firmware(file_entry); + if (ret > 0) + goto err_open_criteria_file; + himax_test_item_chk(ts, true); + } + } else { + /* parsing test file .dri*/ + I("%s,Success to get %s\n", __func__, file_name_1); + ret = himax_parse_test_dri_file(ts, file_entry); + release_firmware(file_entry); + if (ret > 0) + goto err_open_criteria_file; + himax_test_item_chk(ts, false); + } + + if (ts->debug_log_level & BIT(4)) { + /* print get criteria string */ + for (i = 0; i < ts->hx_criteria_size; i++) { + if (ts->inspt_crtra_flag[i] != 0) + I("%s: [%d]There is String=%s\n", __func__, i, + g_hx_inspt_crtra_name[i]); + } + } + + snprintf(ts->file_path, (int)(strlen(HX_RSLT_OUT_PATH) + strlen(HX_RSLT_OUT_FILE) + 1), + "%s%s", HX_RSLT_OUT_PATH, HX_RSLT_OUT_FILE); + + ts->file_w_flag = true; + return ret; + +err_open_criteria_file: + kfree(ts->rslt_data); + ts->rslt_data = NULL; +err_malloc_rslt_data: + +err_malloc_inspection_criteria2: + for (i = 0; i < ts->hx_criteria_size; i++) { + if (ts->inspection_criteria[i] != NULL) { + kfree(ts->inspection_criteria[i]); + ts->inspection_criteria[i] = NULL; + } + } + kfree(ts->inspection_criteria); + ts->inspection_criteria = NULL; +err_malloc_inspection_criteria: + kfree(ts->inspt_crtra_flag); + ts->inspt_crtra_flag = NULL; +err_malloc_inspt_crtra_flag: + kfree(ts->test_item_flag); + ts->test_item_flag = NULL; +err_malloc_test_item_flag: + return ret; +} + +static void himax_self_test_data_deinit(struct himax_ts_data *ts) +{ + int i = 0; + + /*dbg*/ + /* for (i = 0; i < ts->hx_criteria_item; i++) + * I("%s:[%d]%d\n", __func__, i, ts->inspection_criteria[i]); + */ + + I("%s: release allocated memory\n", __func__); + + for (i = 0; i < ts->hx_criteria_size; i++) { + if (ts->inspection_criteria[i] != NULL) { + kfree(ts->inspection_criteria[i]); + ts->inspection_criteria[i] = NULL; + } + } + kfree(ts->inspection_criteria); + ts->inspection_criteria = NULL; + + kfree(ts->inspt_crtra_flag); + ts->inspt_crtra_flag = NULL; + + kfree(ts->test_item_flag); + ts->test_item_flag = NULL; + I("%s: release finished\n", __func__); +} + +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +static int himax_chip_self_test(struct himax_ts_data *ts, struct seq_file *s, void *v) +{ + uint32_t ret = HX_INSP_OK; + uint32_t test_size = ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM + + ts->ic_data->HX_TX_NUM + ts->ic_data->HX_RX_NUM; + int i = 0; + uint8_t tmp_addr[DATA_LEN_4] = { 0x94, 0x72, 0x00, 0x10 }; + uint8_t tmp_data[DATA_LEN_4] = { 0x01, 0x00, 0x00, 0x00 }; + struct file *raw_file = NULL; +#if 0 + mm_segment_t fs; +#endif + loff_t pos = 0; + uint32_t rslt = HX_INSP_OK; + + I("%s:IN\n", __func__); + + ts->suspend_resume_done = 0; + + ret = himax_self_test_data_init(ts); + if (ret > 0) { + E("%s: initialize self test failed\n", __func__); + goto END; + } + + if (raw_file == NULL && ts->file_w_flag) { + raw_file = filp_open(ts->file_path, O_TRUNC | O_CREAT | O_RDWR, 0660); + + if (IS_ERR(raw_file)) { + E("%s open file failed = %ld\n", __func__, PTR_ERR(raw_file)); + ts->file_w_flag = false; + } + } + +#if 0 + fs = get_fs(); + set_fs(KERNEL_DS); +#endif + + hx_print_ic_id(ts); + if (ts->file_w_flag) { + kernel_write(raw_file, ts->rslt_data, ts->rslt_data_len, &pos); + pos += ts->rslt_data_len; + } + + /*Do normal test items*/ + for (i = 0; i < ts->hx_criteria_item; i++) { + if (i < HX_LP_RAWDATA) { + if (ts->test_item_flag[i] == 1) { + I("%d. %s Start\n", i, g_himax_inspection_mode[i]); + rslt = mpTestFunc(ts, i, test_size); + if (ts->file_w_flag && ((rslt & HX_INSP_EGETRAW) == 0) && + ((rslt & HX_INSP_ESWITCHMODE) == 0)) { + kernel_write(raw_file, ts->rslt_data, + ts->rslt_data_len, &pos); + pos += ts->rslt_data_len; + } + ret |= rslt; + + I("%d. %s End, ret = %d\n", i, g_himax_inspection_mode[i], ret); + } + } else { + break; + } + } + + /* Press power key and do LPWUG test items*/ + if (ts->do_lpwg_test) { + himax_press_powerkey(ts); + /* Wait suspend done */ + while (ts->suspend_resume_done != 1) + usleep_range(1000, 1001); + ts->suspend_resume_done = 0; + + for (; i < ts->hx_criteria_item; i++) { + if (ts->test_item_flag[i] == 1) { + I("%d.%s Start\n", i, g_himax_inspection_mode[i]); + rslt = mpTestFunc(ts, i, test_size); + if (ts->file_w_flag && ((rslt & HX_INSP_EGETRAW) == 0) && + ((rslt & HX_INSP_ESWITCHMODE) == 0)) { + kernel_write(raw_file, ts->rslt_data, + ts->rslt_data_len, &pos); + pos += ts->rslt_data_len; + } + ret |= rslt; + + I("%d.%s End\n", i, g_himax_inspection_mode[i]); + } + } + himax_press_powerkey(ts); + /* Wait resume done */ + while (ts->suspend_resume_done != 1) + usleep_range(1000, 1001); + } + if (ts->file_w_flag) + filp_close(raw_file, NULL); +#if 0 + set_fs(fs); +#endif + + ts->core_fp.fp_sense_off(ts, true); + /*himax_set_N_frame(ts, 1, HX_INSPECTION_WT_NOISE);*/ + /* set N frame back to default value 1*/ + himax_mcu_register_write(ts, tmp_addr, 4, tmp_data, 0); + + if (ts->core_fp.fp_reload_disable != NULL) + ts->core_fp.fp_reload_disable(ts, 0); + + if (himax_check_mode(ts, HX_RAWDATA)) { + I("%s:try to Need to back to Normal!\n", __func__); + himax_switch_mode_inspection(ts, HX_RAWDATA); + ts->core_fp.fp_sense_on(ts, 0x00); + himax_wait_sorting_mode(ts, HX_RAWDATA); + } else { + I("%s: It has been in Normal!\n", __func__); + ts->core_fp.fp_sense_on(ts, 0x00); + } + + if (ret == HX_INSP_OK) + seq_puts(s, "Self_Test Pass:\n"); + else + seq_puts(s, "Self_Test Fail:\n"); + + for (i = 0; i < ts->hx_criteria_item - 1; i++) { + if (ts->test_item_flag[i] == 1) { + seq_printf(s, "%s : %s\n", g_himax_inspection_mode[i], + ((ret & (1 << (i + ERR_SFT))) == (1 << (i + ERR_SFT))) ? "Fail" : + "OK"); + } + } + + himax_self_test_data_deinit(ts); + +END: + I("running status = %X\n", ret); + + /*if (ret != 0)*/ + /*ret = 1;*/ + + I("%s:OUT\n", __func__); + return ret; +} + +void himax_inspect_data_clear(struct himax_ts_data *ts) +{ + if (ts->rslt_data) { + kfree(ts->rslt_data); + ts->rslt_data = NULL; + } +} + +void himax_inspection_init(struct himax_ts_data *ts) +{ + I("%s: enter, %d\n", __func__, __LINE__); + + ts->core_fp.fp_chip_self_test = himax_chip_self_test; + + ts->gap_vertical_partial = 3; + ts->gap_horizontal_partial = 3; +} diff --git a/drivers/input/touchscreen/hxchipset/himax_inspection.h b/drivers/input/touchscreen/hxchipset/himax_inspection.h new file mode 100644 index 000000000000..e37751f5a24e --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/himax_inspection.h @@ -0,0 +1,278 @@ +/* Himax Android Driver Sample Code for inspection functions + * + * Copyright (C) 2021 Himax Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" + +/*#define HX_GAP_TEST*/ +/*#define HX_INSP_LP_TEST*/ +/*#define HX_ACT_IDLE_TEST*/ + +#define HX_RSLT_OUT_PATH "/sdcard/" +#define HX_RSLT_OUT_FILE "hx_test_result.txt" +#define PI(x...) pr_cont(x) +#define HX_SZ_ICID 60 + +#define BS_RAWDATA 8 +#define BS_NOISE 8 +#define BS_OPENSHORT 1 +#define BS_LPWUG 1 +#define BS_LP_dile 1 +#define BS_ACT_IDLE 1 + +/* skip notch & dummy */ +#define SKIP_NOTCH_START 5 +#define SKIP_NOTCH_END 10 +/* TX+SKIP_NOTCH_START */ +#define SKIP_DUMMY_START 23 +/* TX+SKIP_NOTCH_END*/ +#define SKIP_DUMMY_END 28 + +#define NOISEFRAME 200 +#define NORMAL_IDLE_RAWDATA_NOISEFRAME 10 +#define LP_RAWDATAFRAME 1 +#define LP_NOISEFRAME 1 +#define LP_IDLE_RAWDATAFRAME 1 +#define LP_IDLE_NOISEFRAME 1 +#define OTHERSFRAME 1 +#define UNIFMAX 500 + +/*Himax MP Password*/ +#define PWD_OPEN_START 0x77 +#define PWD_OPEN_END 0x88 +#define PWD_MICRO_OPEN_START 0x6B +#define PWD_MICRO_OPEN_END 0x70 +#define PWD_SHORT_START 0x11 +#define PWD_SHORT_END 0x33 +#define PWD_RAWDATA_START 0x00 +#define PWD_RAWDATA_END 0x99 +#define PWD_NOISE_START 0x00 +#define PWD_NOISE_END 0x99 +#define PWD_SORTING_START 0xAA +#define PWD_SORTING_END 0xCC + +#define PWD_ACT_IDLE_START 0x22 +#define PWD_ACT_IDLE_END 0x44 + +#define PWD_LP_START 0x55 +#define PWD_LP_END 0x66 + +#define PWD_LP_IDLE_START 0x50 +#define PWD_LP_IDLE_END 0x60 + +#define PWD_TURN_ON_MPAP_OVL 0x107380 + +/*Himax DataType*/ +#define DATA_SORTING 0x0A +#define DATA_OPEN 0x0B +#define DATA_MICRO_OPEN 0x0A +#define DATA_SHORT 0x0A +#define DATA_RAWDATA 0x0A +#define DATA_NOISE 0x0F +#define DATA_BACK_NORMAL 0x00 +#define DATA_LP_RAWDATA 0x0C +#define DATA_LP_NOISE 0x0F +#define DATA_ACT_IDLE_RAWDATA 0x0A +#define DATA_ACT_IDLE_NOISE 0x0F +#define DATA_LP_IDLE_RAWDATA 0x0A +#define DATA_LP_IDLE_NOISE 0x0F + +/*Himax Data Ready Password*/ +#define Data_PWD0 0xA5 +#define Data_PWD1 0x5A + +/* ASCII format */ +#define ASCII_LF (0x0A) +#define ASCII_CR (0x0D) +#define ASCII_COMMA (0x2C) +#define ASCII_ZERO (0x30) +#define CHAR_EL '\0' +#define CHAR_NL '\n' +#define ACSII_SPACE (0x20) +/* INSOECTION Setting */ + +void himax_inspection_init(struct himax_ts_data *ts); + +/*Inspection register*/ +#define addr_normal_noise_thx 0x1000708C +#define addr_lpwug_noise_thx 0x10007090 +#define addr_noise_scale 0x10007094 +#define addr_recal_thx 0x10007090 +#define addr_palm_num 0x100070A8 +#define addr_weight_sup 0x100072C8 +#define addr_normal_weight_a 0x1000709C +#define addr_lpwug_weight_a 0x100070A0 +#define addr_weight_b 0x10007094 +#define addr_max_dc 0x10007FC8 +#define addr_skip_frame 0x100070F4 +#define addr_neg_noise_sup 0x10007FD8 +#define data_neg_noise 0x7F0C0000 +#define addr_ctrl_mpap_ovl 0x100073EC + +/*Need to map *g_himax_inspection_mode[]*/ +enum THP_INSPECTION_ENUM { + HX_OPEN, + HX_MICRO_OPEN, + HX_SHORT, + HX_RAWDATA, + HX_BPN_RAWDATA, + HX_SC, + HX_WT_NOISE, + HX_ABS_NOISE, + HX_SORTING, + + HX_GAPTEST_RAW, + /*HX_GAPTEST_RAW_X,*/ + /*HX_GAPTEST_RAW_Y,*/ + + HX_ACT_IDLE_RAWDATA, + HX_ACT_IDLE_BPN_RAWDATA, + HX_ACT_IDLE_NOISE, + /*LPWUG test must put after Normal test*/ + HX_LP_RAWDATA, + HX_LP_BPN_RAWDATA, + HX_LP_WT_NOISE, + HX_LP_ABS_NOISE, + HX_LP_IDLE_RAWDATA, + HX_LP_IDLE_BPN_RAWDATA, + HX_LP_IDLE_NOISE, + + HX_BACK_NORMAL, /*Must put in the end*/ +}; + +enum HX_CRITERIA_ENUM { + IDX_RAWMIN = 0, + IDX_RAWMAX, + IDX_BPN_RAWMIN, + IDX_BPN_RAWMAX, + IDX_SCMIN, + IDX_SCMAX, + IDX_SC_GOLDEN, + IDX_SHORTMIN, + IDX_SHORTMAX, + IDX_OPENMIN, + IDX_OPENMAX, + IDX_M_OPENMIN, + IDX_M_OPENMAX, + IDX_WT_NOISEMIN, + IDX_WT_NOISEMAX, + IDX_ABS_NOISEMIN, + IDX_ABS_NOISEMAX, + IDX_SORTMIN, + IDX_SORTMAX, + + IDX_GAP_HOR_RAWMAX, + IDX_GAP_HOR_RAWMIN, + IDX_GAP_VER_RAWMAX, + IDX_GAP_VER_RAWMIN, + + IDX_ACT_IDLE_NOISE_MIN, + IDX_ACT_IDLE_NOISE_MAX, + IDX_ACT_IDLE_RAWDATA_MIN, + IDX_ACT_IDLE_RAWDATA_MAX, + IDX_ACT_IDLE_RAW_BPN_MIN, + IDX_ACT_IDLE_RAW_BPN_MAX, + + IDX_LP_WT_NOISEMIN, + IDX_LP_WT_NOISEMAX, + IDX_LP_NOISE_ABS_MIN, + IDX_LP_NOISE_ABS_MAX, + IDX_LP_RAWDATA_MIN, + IDX_LP_RAWDATA_MAX, + IDX_LP_RAW_BPN_MIN, + IDX_LP_RAW_BPN_MAX, + + IDX_LP_IDLE_NOISE_MIN, + IDX_LP_IDLE_NOISE_MAX, + IDX_LP_IDLE_RAWDATA_MIN, + IDX_LP_IDLE_RAWDATA_MAX, + IDX_LP_IDLE_RAW_BPN_MIN, + IDX_LP_IDLE_RAW_BPN_MAX, +}; + +#define ERR_SFT 4 +/* Error code of Inspection */ +enum HX_INSP_ERR_ENUM { + /* OK */ + HX_INSP_OK = 0, + + /* Criteria file error*/ + HX_INSP_EFILE = 1, + + /* Get raw data errors */ + HX_INSP_EGETRAW = 1 << 1, + + /* Memory allocate errors */ + HX_INSP_MEMALLCTFAIL = 1 << 2, + + /* Switch mode error*/ + HX_INSP_ESWITCHMODE = 1 << 3, + + /* Sensor open error */ + HX_EOPEN = 1 << (HX_OPEN + ERR_SFT), + + /* Sensor micro open error */ + HX_EMOPEN = 1 << (HX_MICRO_OPEN + ERR_SFT), + + /* Sensor short error */ + HX_ESHORT = 1 << (HX_SHORT + ERR_SFT), + + /* Raw data error */ + HX_ERAW = 1 << (HX_RAWDATA + ERR_SFT), + + /* Raw data BPN error */ + HX_EBPNRAW = 1 << (HX_BPN_RAWDATA + ERR_SFT), + + /* Get SC errors */ + HX_ESC = 1 << (HX_SC + ERR_SFT), + + /* Noise error */ + HX_WT_ENOISE = 1 << (HX_WT_NOISE + ERR_SFT), + + /* Noise error */ + HX_ABS_ENOISE = 1 << (HX_ABS_NOISE + ERR_SFT), + + /* Sorting error*/ + HX_ESORT = 1 << (HX_SORTING + ERR_SFT), + + /* Raw Data GAP */ + HX_EGAP_RAW = 1 << (HX_GAPTEST_RAW + ERR_SFT), + + /* ACT_IDLE RAW ERROR */ + HX_EACT_IDLE_RAW = 1 << (HX_ACT_IDLE_RAWDATA + ERR_SFT), + + /* ACT_IDLE NOISE ERROR */ + HX_EACT_IDLE_NOISE = 1 << (HX_ACT_IDLE_NOISE + ERR_SFT), + + /* LPWUG RAW ERROR */ + HX_ELP_RAW = 1 << (HX_LP_RAWDATA + ERR_SFT), + + /* LPWUG NOISE ERROR */ + HX_ELP_WT_NOISE = 1 << (HX_LP_WT_NOISE + ERR_SFT), + + /* LPWUG NOISE ERROR */ + HX_ELP_ABS_NOISE = 1 << (HX_LP_ABS_NOISE + ERR_SFT), + + /* LPWUG IDLE RAW ERROR */ + HX_ELP_IDLE_RAW = 1 << (HX_LP_IDLE_RAWDATA + ERR_SFT), + + /* LPWUG IDLE NOISE ERROR */ + HX_ELP_IDLE_NOISE = 1 << (HX_LP_IDLE_NOISE + ERR_SFT), + HX_EACT_IDLE_BPNRAW = 1 << (HX_ACT_IDLE_BPN_RAWDATA + ERR_SFT), + HX_ELP_BPNRAW = 1 << (HX_LP_BPN_RAWDATA + ERR_SFT), + HX_ELP_IDLE_BPNRAW = 1 << (HX_LP_IDLE_BPN_RAWDATA + ERR_SFT), +}; + +extern void himax_inspect_data_clear(struct himax_ts_data *ts); diff --git a/drivers/input/touchscreen/hxchipset/himax_platform.c b/drivers/input/touchscreen/hxchipset/himax_platform.c new file mode 100644 index 000000000000..12b2539a280d --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/himax_platform.c @@ -0,0 +1,1200 @@ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2021 Himax Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "himax.h" +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" + +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HIMAX_IC_HX83192) +DECLARE_HIMAX_CHIP(hx83192_chip, hx83192_chip_detect); +#endif + +/* FIXME */ +static int mmi_refcnt; + +extern void notify_tp_procfile(const int exsited_flag); + +static ssize_t himax_i2c_trc_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t bytes); +static BIN_ATTR(i2c_trace, 0444, himax_i2c_trc_read, NULL, sizeof_field(struct trc_lines, lines)); + +/******************************************************************************* + * Provides the data for the SysFS 'i2c_trace' binary file. This is the data + * sent and received via i2c. If a transfer fails, we cannot say here at + * which byte the failure occurred so this is not a replacement for a logic + * analyser. + * + * Returns number of bytes stored. + ******************************************************************************/ +static ssize_t himax_i2c_trc_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t bytes) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct himax_ts_data *ts = dev_get_drvdata(dev); + + struct trc_line *line; + int offset = 0; + + /* mutex_lock(&ts->trc_lines.mutex);*/ + + if (ts->trc_lines.fill_status >= TRC_NUM_LINES) { + /* Some debug lines have been lost so say how many */ + offset = scnprintf(buf, bytes, "Discarded %d lines\n", + ts->trc_lines.fill_status - TRC_NUM_LINES); + + /* Now we've said how many lines have been lost, we only need + * to know the buffer is now completely full. */ + ts->trc_lines.fill_status = TRC_NUM_LINES; + } + + while ((ts->trc_lines.fill_status > 0) && (offset != bytes)) { + line = &ts->trc_lines.lines[ts->trc_lines.read_idx]; + /* line_start = line->text + line->read_offset; */ + + /* Copy as much of the line as possible into the buffer + * (excluding terminating null. + */ + while ((offset != bytes) && (line->text[line->read_offset] != '\0')) { + buf[offset++] = line->text[line->read_offset++]; + } + + if (offset != bytes) { + /* There's still space in the buffer so move + * to the next line in the ring buffer */ + ts->trc_lines.read_idx++; + ts->trc_lines.read_idx %= TRC_NUM_LINES; + ts->trc_lines.fill_status--; + } + } + + /* mutex_unlock(&ts->trc_lines.mutex);*/ + return offset; +} + +/******************************************************************************* + * Converts binary data into ASCII hex. This is a helper function for + * himax_dbg_add. + * + * buf - the buffer to hold the ASCII hex. + * avail - the number of characters available in buf + * data - pointer to the binary data + * len - length of the binary data + * consumed - pointer (may be NULL) to a variable to hold the number of bytes of + * binary data that were consumed. + * + * Returns the number of ASCII characters written (not including terminating \0) + ******************************************************************************/ +static int himax_dbg_write_hex(char *buf, size_t avail, const u8 *data, size_t len, int *consumed) +{ + int offset = 0; + int min_length = 0; + char buffer[5]; + + if (consumed) + *consumed = 0; + + while ((len > 0) && (avail >= min_length)) { + min_length = scnprintf(buffer, avail, "%02x%s", *data++, --len > 0 ? " " : ""); + + /* Make sure there is enough space to store a full ASCII hex + * representation of the byte */ + if (avail >= min_length) { + memcpy(buf + offset, buffer, min_length); + offset += min_length; + avail -= min_length; + if (consumed) + (*consumed)++; + } + } + + return offset; +} + +/******************************************************************************* + * Add a log message to the debug log. + * + * msg - A text message to store. + * ptr - pointer (may be NULL) to the binary data to convert to hex. + * data_len - length of the binary data + ******************************************************************************/ +static void himax_dbg_add(struct himax_ts_data *ts, const char *msg, const u8 *ptr, int data_len) +{ + struct trc_line *line; + int length; + int consumed; + int offset; + int idx; + /* struct timespec ts_nsec; + getnstimeofday(&ts_nsec);*/ + + /* if (!ts->trc_lines.enabled) + return;*/ + + /* mutex_lock(&ts->trc_lines.mutex);*/ + + do { + offset = 0; + idx = ts->trc_lines.write_idx; + line = &ts->trc_lines.lines[idx]; + + line->read_offset = 0; + /* + offset = scnprintf(line->text, + TRC_TEXT_LENGTH, + "[%5llu.%06llu] ", + ts_nsec.tv_nsec / 1000000000, + (ts_nsec.tv_nsec % 1000000000) / 1000); + */ + length = scnprintf(line->text + offset, TRC_TEXT_LENGTH - offset, "%s", msg); + + offset += length; + if (offset == TRC_TEXT_LENGTH) + /* We didn't copy all of the message */ + msg += length; + else + /* Message was completely copied but prepare for having + * too much binary data for one line */ + msg = "|->"; + + if (ptr) { + length = himax_dbg_write_hex(line->text + offset, TRC_TEXT_LENGTH - offset, + ptr, data_len, &consumed); + + offset += length; + ptr += consumed; + data_len -= consumed; + } + + /* Add the end-of-line string. + * We don't need to check for space because we've used + * TRC_TEXT_LENGTH so far which leaves space for the end-of-line + * string and terminating NUL. + */ + scnprintf(&line->text[offset], TRC_EOL_LENGTH, TRC_EOL); + + ts->trc_lines.write_idx = (++idx) % TRC_NUM_LINES; + + /* If we've filled the ring buffer and have wrapped round, + * move the read index forward so that it always points to the + * oldest unread entry that still exists. */ + if (++ts->trc_lines.fill_status > TRC_NUM_LINES) { + ts->trc_lines.read_idx++; + ts->trc_lines.read_idx %= TRC_NUM_LINES; + } + } while (data_len > 0); + + /* mutex_unlock(&ts->trc_lines.mutex);*/ +} + +/******************************************************************************* + * Creates the SysFS file entries. + ******************************************************************************/ +int himax_sysfs_init(struct himax_ts_data *ts) +{ + struct i2c_client *client = ts->client; + int error; + /* + error = sysfs_create_group(&client->dev.kobj, &himax_attr_group); + if (error) { + dev_err(&client->dev, + "Failed to create sysfs sysfs group (%d)\n", + error); + return error; + } + */ + error = sysfs_create_bin_file(&client->dev.kobj, &bin_attr_i2c_trace); + + /* This is only for debugging purposes so failing to create the + * file for whatever reason is not a critical error. */ + if (error) + dev_warn(&client->dev, "Failed to create %s (%d)\n", bin_attr_i2c_trace.attr.name, + error); + + return error; +} + +void himax_sysfs_deinit(struct himax_ts_data *ts) +{ + struct i2c_client *client = ts->client; + + sysfs_remove_bin_file(&client->dev.kobj, &bin_attr_i2c_trace); +} + +int himax_dev_set(struct himax_ts_data *ts) +{ + int ret = 0; + + ts->input_dev = input_allocate_device(); + + if (ts->input_dev == NULL) { + ret = -ENOMEM; + E("%s: Failed to allocate input device-input_dev\n", __func__); + return ret; + } + + if (ts->location) { + ts->input_dev->name = ts->location; + /* NOTE: To identify each display. */ + ts->input_dev->phys = + devm_kasprintf(ts->dev, GFP_KERNEL, "himax-%s/input0", dev_name(ts->dev)); + } else { + ts->input_dev->name = "himax-touchscreen"; + } + + return ret; +} +int himax_input_register_device(struct input_dev *input_dev) +{ + return input_register_device(input_dev); +} + +int himax_parse_dt(struct himax_ts_data *ts, struct himax_i2c_platform_data *pdata) +{ + int rc, coords_size = 0; + uint32_t coords[4] = { 0 }; + struct property *prop; + struct device_node *dt = ts->client->dev.of_node; + u32 data = 0; + int ret = 0; + const char *name; + struct pinctrl *p; + struct pinctrl_state *default_state; + + of_property_read_string(dt, "himax,location", &ts->location); + + prop = of_find_property(dt, "himax,panel-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid panel coords size %d\n", __func__, coords_size); + } + + ret = of_property_read_u32_array(dt, "himax,panel-coords", coords, coords_size); + if (ret == 0) { + pdata->abs_x_min = coords[0]; + pdata->abs_x_max = (coords[1] - 1); + pdata->abs_y_min = coords[2]; + pdata->abs_y_max = (coords[3] - 1); + I(" DT-%s:panel-coords = %d, %d, %d, %d\n", __func__, pdata->abs_x_min, + pdata->abs_x_max, pdata->abs_y_min, pdata->abs_y_max); + } + + prop = of_find_property(dt, "himax,display-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid display coords size %d\n", __func__, coords_size); + } + + rc = of_property_read_u32_array(dt, "himax,display-coords", coords, coords_size); + + if (rc && (rc != -EINVAL)) { + D(" %s:Fail to read display-coords %d\n", __func__, rc); + return rc; + } + + pdata->screenWidth = coords[1]; + pdata->screenHeight = coords[3]; + I(" DT-%s:display-coords = (%d, %d)\n", __func__, pdata->screenWidth, pdata->screenHeight); + pdata->gpio_irq = of_get_named_gpio(dt, "himax,irq-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_irq)) + I(" DT:gpio_irq value is not valid\n"); + + pdata->fail_det = of_get_named_gpio(dt, "himax,fail-det", 0); + + if (!gpio_is_valid(pdata->fail_det)) + I(" DT:fail_det value is not valid\n"); + + pdata->gpio_reset = of_get_named_gpio(dt, "himax,rst-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_reset)) + I(" DT:gpio_rst value is not valid\n"); + + pdata->gpio_pon = of_get_named_gpio(dt, "himax,pon-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_pon)) + I(" DT:gpio_pon value is not valid\n"); + + pdata->lcm_rst = of_get_named_gpio(dt, "himax,lcm-rst", 0); + + if (!gpio_is_valid(pdata->lcm_rst)) + I(" DT:tp-rst value is not valid\n"); + + I(" DT:pdata->gpio_pon=%d, pdata->lcm_rst=%d\n", pdata->gpio_pon, pdata->lcm_rst); + + pdata->gpio_3v3_en = of_get_named_gpio(dt, "himax,3v3-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_3v3_en)) + I(" DT:gpio_3v3_en value is not valid\n"); + + I(" DT:gpio_irq=%d, gpio_rst=%d, gpio_3v3_en=%d\n", pdata->gpio_irq, pdata->gpio_reset, + pdata->gpio_3v3_en); + I(" DT:fail_det=%d", pdata->fail_det); + + if (of_property_read_u32(dt, "report_type", &data) == 0) { + pdata->protocol_type = data; + I(" DT:protocol_type=%d\n", pdata->protocol_type); + } + + if (of_property_read_string(dt, "himax,tp-fw-bin", &name) < 0) { + I("DT:fail to get himax tp fw bin\n"); + } else { + pdata->fw_name = name; + I("DT:himax tp fw bin:%s\n", pdata->fw_name); + } + + if (of_property_read_string(dt, "himax,hx-criteria", &name) < 0) { + I("DT:fail to get himax,hx-criteria file\n"); + } else { + pdata->criteria_file_name = name; + I("DT:himax,hx-criteria:%s\n", pdata->criteria_file_name); + } + + p = devm_pinctrl_get(&ts->client->dev); + default_state = pinctrl_lookup_state(p, "default"); + pinctrl_select_state(p, default_state); + I("%s: active pinctrl!\n", __func__); + + return 0; +} +EXPORT_SYMBOL(himax_parse_dt); + +int himax_bus_read(struct himax_ts_data *ts, uint8_t command, uint8_t *data, uint32_t length, + uint8_t toRetry) +{ + int retry; + char buf[50]; + int i; + int ret = 0; + struct i2c_client *client = ts->client; + struct i2c_msg msg[] = { { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &command, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = ts->rw_buf, + } }; + mutex_lock(&ts->rw_lock); + + for (retry = 0; retry < toRetry; retry++) { + ret = i2c_transfer(client->adapter, msg, 2); + if (ret == 2) { + memcpy(data, ts->rw_buf, length); + break; + } + msleep(20); + } + + for (i = 0; i < ret; i++) { + himax_dbg_add(ts, msg[i].flags & I2C_M_RD ? "[R]" : "[W]", (u8 *)msg[i].buf, + msg[i].len); + } + scnprintf(buf, sizeof(buf), "Messages sent: %d/%d", ret, 2); + buf[sizeof(buf) - 1] = '\0'; + himax_dbg_add(ts, buf, 0, 0); + + if (retry == toRetry) { + E("%s: i2c_read_block retry over %d\n", __func__, toRetry); + mutex_unlock(&ts->rw_lock); + return -EIO; + } + + mutex_unlock(&ts->rw_lock); + return 0; +} +EXPORT_SYMBOL(himax_bus_read); + +int himax_bus_write(struct himax_ts_data *ts, uint8_t command, uint8_t *data, uint32_t length, + uint8_t toRetry) +{ + int retry; + char buf[50]; + int i; + int ret = 0; + struct i2c_client *client = ts->client; + struct i2c_msg msg[] = { { + .addr = client->addr, + .flags = 0, + .len = length + 1, + .buf = ts->rw_buf, + } }; + + mutex_lock(&ts->rw_lock); + ts->rw_buf[0] = command; + if (data != NULL) + memcpy(ts->rw_buf + 1, data, length); + + for (retry = 0; retry < toRetry; retry++) { + ret = i2c_transfer(client->adapter, msg, 1); + if (ret == 1) + break; + msleep(20); + } + + for (i = 0; i < ret; i++) { + himax_dbg_add(ts, msg[i].flags & I2C_M_RD ? "[R]" : "[W]", (u8 *)msg[i].buf, + msg[i].len); + } + + scnprintf(buf, sizeof(buf), "Messages sent: %d/%d", ret, 1); + buf[sizeof(buf) - 1] = '\0'; + himax_dbg_add(ts, buf, 0, 0); + + if (retry == toRetry) { + E("%s: i2c_write_block retry over %d\n", __func__, toRetry); + mutex_unlock(&ts->rw_lock); + return -EIO; + } + + mutex_unlock(&ts->rw_lock); + return 0; +} +EXPORT_SYMBOL(himax_bus_write); + +void himax_int_enable(struct himax_ts_data *ts, int enable) +{ + unsigned long irqflags = 0; + int irqnum = ts->client->irq; + + spin_lock_irqsave(&ts->irq_lock, irqflags); + I("%s: Entering!\n", __func__); + if (enable == 1 && atomic_read(&ts->irq_state) == 0) { + atomic_set(&ts->irq_state, 1); + enable_irq(irqnum); + ts->irq_enabled = 1; + } else if (enable == 0 && atomic_read(&ts->irq_state) == 1) { + atomic_set(&ts->irq_state, 0); + disable_irq_nosync(irqnum); + ts->irq_enabled = 0; + } + + I("enable = %d\n", enable); + spin_unlock_irqrestore(&ts->irq_lock, irqflags); +} +EXPORT_SYMBOL(himax_int_enable); + +#if defined(HX_RST_PIN_FUNC) +void himax_gpio_set(int pinnum, uint8_t value) +{ + gpio_direction_output(pinnum, value); +} +EXPORT_SYMBOL(himax_gpio_set); +#endif + +uint8_t himax_int_gpio_read(int pinnum) +{ + return gpio_get_value(pinnum); +} + +int himax_gpio_power_config(struct himax_ts_data *ts, struct himax_i2c_platform_data *pdata) +{ + int error = 0; + struct i2c_client *client = ts->client; +#if defined(HX_RST_PIN_FUNC) + + if (pdata->gpio_reset >= 0) { + error = gpio_request(pdata->gpio_reset, "himax-reset"); + + if (error < 0) { + E("%s: request reset pin failed\n", __func__); + goto err_gpio_reset_req; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", pdata->gpio_reset); + goto err_gpio_reset_dir; + } + } + +#endif + + if (pdata->lcm_rst >= 0) { + error = gpio_request(pdata->lcm_rst, "lcm-reset"); + + if (error < 0) { + E("%s: request lcm-reset pin failed\n", __func__); + goto err_lcm_rst_req; + } + + error = gpio_direction_output(pdata->lcm_rst, 0); + if (error) { + E("unable to set direction for lcm_rst [%d]\n", pdata->lcm_rst); + goto err_lcm_rst_dir; + } + } + + if (gpio_is_valid(pdata->gpio_pon)) { + error = gpio_request(pdata->gpio_pon, "hmx_pon_gpio"); + + if (error) { + E("unable to request scl gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_req; + } + + error = gpio_direction_output(pdata->gpio_pon, 0); + + I("gpio_pon LOW [%d]\n", pdata->gpio_pon); + + if (error) { + E("unable to set direction for pon gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_dir; + } + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "himax_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_req; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_set_input; + } + + client->irq = gpio_to_irq(pdata->gpio_irq); + ts->hx_irq = client->irq; + } else { + E("irq gpio not provided\n"); + goto err_gpio_irq_req; + } + + if (gpio_is_valid(pdata->fail_det)) { + /* configure touchscreen fail_det gpio */ + error = gpio_request(pdata->fail_det, "himax_fail_det"); + + if (error) + E("unable to request gpio [%d]\n", pdata->fail_det); + + error = gpio_direction_input(pdata->fail_det); + + if (error) + E("unable to set direction for gpio [%d]\n", pdata->fail_det); + + ts->hx_fail_det = gpio_to_irq(pdata->fail_det); + + } else { + I("fail_det not provided\n"); + } + + msleep(10); + + if (pdata->lcm_rst >= 0) { + error = gpio_direction_output(pdata->lcm_rst, 1); + + if (error) { + E("lcm_rst unable to set direction for gpio [%d]\n", pdata->lcm_rst); + goto err_lcm_reset_set_high; + } + } + msleep(10); + +#if defined(HX_RST_PIN_FUNC) + + if (pdata->gpio_reset >= 0) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", pdata->gpio_reset); + goto err_gpio_reset_set_high; + } + } +#endif + + msleep(100); + + if (gpio_is_valid(pdata->gpio_pon)) { + error = gpio_direction_output(pdata->gpio_pon, 1); + + I("gpio_pon HIGH [%d]\n", pdata->gpio_pon); + + if (error) { + E("gpio_pon unable to set direction for gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_set_high; + } + } + + return error; + +err_gpio_pon_set_high: +#if defined(HX_RST_PIN_FUNC) +err_gpio_reset_set_high: +#endif +err_lcm_reset_set_high: +err_gpio_irq_set_input: + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); +err_gpio_irq_req: + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); +err_gpio_pon_dir: + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +err_gpio_pon_req: +err_lcm_rst_dir: + if (gpio_is_valid(pdata->lcm_rst)) + gpio_free(pdata->lcm_rst); +err_lcm_rst_req: + +#if defined(HX_RST_PIN_FUNC) +err_gpio_reset_dir: + if (pdata->gpio_reset >= 0) + gpio_free(pdata->gpio_reset); +err_gpio_reset_req: +#endif + return error; +} + +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata) +{ + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +#if defined(HX_RST_PIN_FUNC) + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); +#endif + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); + + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +} + +static void himax_ts_isr_func(struct himax_ts_data *ts) +{ + himax_ts_work(ts); +} + +static irqreturn_t himax_ts_thread(int irq, void *ptr) +{ + himax_ts_isr_func((struct himax_ts_data *)ptr); + + return IRQ_HANDLED; +} + +static void himax_ts_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work); + + himax_ts_work(ts); +} + +static irqreturn_t himax_fail_det_thread(int irq, void *ptr) +{ + struct himax_ts_data *ts = (struct himax_ts_data *)ptr; + + himax_fail_det_work(ts); + + return IRQ_HANDLED; +} + +static int himax_int_register_trigger(struct himax_ts_data *ts) +{ + int ret = 0; + struct i2c_client *client = ts->client; + + if (ts->ic_data->HX_INT_IS_EDGE) { + I("%s edge triiger falling\n ", __func__); + ret = request_threaded_irq(client->irq, NULL, himax_ts_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, ts); + } else { + I("%s level trigger low\n ", __func__); + ret = request_threaded_irq(client->irq, NULL, himax_ts_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, ts); + } + + return ret; +} + +static int himax_fail_det_int_register_trigger(struct himax_ts_data *ts) +{ + int ret = 0; + + char *hx_fail_det_name = "hx_fail_det"; + + I("%s level trigger high\n ", __func__); + ret = request_threaded_irq(ts->hx_fail_det, NULL, himax_fail_det_thread, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, hx_fail_det_name, + (void *)ts); + + return ret; +} + +int himax_int_en_set(struct himax_ts_data *ts) +{ + int ret = NO_ERR; + + ret = himax_int_register_trigger(ts); + return ret; +} + +int himax_fail_det_register_interrupt(struct himax_ts_data *ts) +{ + int ret = 0; + + if (ts->hx_fail_det) { /*INT mode*/ + ret = himax_fail_det_int_register_trigger(ts); + + if (ret == 0) + I("%s: irq enabled at gpio: %d\n", __func__, ts->hx_fail_det); + else + E("%s: request_irq failed\n", __func__); + + } else { + I("%s: hx_fail_det is empty.\n", __func__); + } + + return ret; +} + +int himax_ts_register_interrupt(struct himax_ts_data *ts) +{ + struct i2c_client *client = ts->client; + int ret = 0; + + ts->irq_enabled = 0; + + /* Work functon */ + if (client->irq && ts->hx_irq) { /*INT mode*/ + ts->use_irq = 1; + ret = himax_int_register_trigger(ts); + + if (ret == 0) { + ts->irq_enabled = 1; + atomic_set(&ts->irq_state, 1); + I("%s: irq enabled at gpio: %d\n", __func__, client->irq); + } else { + ts->use_irq = 0; + E("%s: request_irq failed\n", __func__); + } + } else { + I("%s: client->irq is empty, use polling mode.\n", __func__); + } + + /*if use polling mode need to disable HX_ESD_RECOVERY function*/ + if (!ts->use_irq) { + ts->himax_wq = create_singlethread_workqueue("himax_touch"); + INIT_WORK(&ts->work, himax_ts_work_func); + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = himax_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + I("%s: polling mode enabled\n", __func__); + } + + return ret; +} + +int himax_ts_unregister_interrupt(struct himax_ts_data *ts) +{ + int ret = 0; + + I("%s: entered.\n", __func__); + + /* Work functon */ + if (ts->hx_irq && ts->use_irq) { /*INT mode*/ + + free_irq(ts->hx_irq, ts); + I("%s: irq disabled at qpio: %d\n", __func__, ts->hx_irq); + } + + /*if use polling mode need to disable HX_ESD_RECOVERY function*/ + if (!ts->use_irq) { + hrtimer_cancel(&ts->timer); + cancel_work_sync(&ts->work); + if (ts->himax_wq != NULL) + destroy_workqueue(ts->himax_wq); + I("%s: polling mode destroyed", __func__); + } + + return ret; +} + +static int himax_common_suspend(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + struct pinctrl *p; + struct pinctrl_state *suspend_state; + + I("%s: enter\n", __func__); +#if defined(HX_CONFIG_DRM) && !defined(HX_CONFIG_FB) + if (!ts->initialized) + return -ECANCELED; +#endif + himax_chip_common_suspend(ts); + if (gpio_is_valid(ts->pdata->gpio_reset)) + gpio_direction_output(ts->pdata->gpio_reset, 0); + I("%s: set tp_rst 0\n", __func__); + p = devm_pinctrl_get(&ts->client->dev); + suspend_state = pinctrl_lookup_state(p, "sleep"); + pinctrl_select_state(p, suspend_state); + I("%s: suspend mode pinctrl!\n", __func__); + return 0; +} + +static int himax_common_resume(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + struct pinctrl *p; + struct pinctrl_state *default_state; + + I("%s: enter\n", __func__); +#if defined(HX_CONFIG_DRM) && !defined(HX_CONFIG_FB) + /* + * wait until device resume for TDDI + * TDDI: Touch and display Driver IC + */ + if (!ts->initialized) + if (himax_chip_common_init(ts)) + return -ECANCELED; +#endif + himax_chip_common_resume(ts); + p = devm_pinctrl_get(&ts->client->dev); + default_state = pinctrl_lookup_state(p, "default"); + pinctrl_select_state(p, default_state); + I("%s: active pinctrl!\n", __func__); + return 0; +} + +#if defined(HX_CONFIG_FB) +int himax_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct himax_ts_data *ts = container_of(self, struct himax_ts_data, fb_notif); + + I(" %s\n", __func__); + + if (evdata && evdata->data && event == FB_EVENT_BLANK && ts->client) { + blank = evdata->data; + + switch (*blank) { + case FB_BLANK_UNBLANK: + himax_common_resume(&ts->client->dev); + break; + case FB_BLANK_POWERDOWN: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_NORMAL: + himax_common_suspend(&ts->client->dev); + break; + } + } + + return 0; +} +#elif defined(HX_CONFIG_DRM) +int himax_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank; + struct himax_ts_data *ts = container_of(self, struct himax_ts_data, fb_notif); + + if (!evdata || (evdata->id != 0)) + return 0; + + D("DRM %s\n", __func__); + + if (evdata->data && event == MSM_DRM_EARLY_EVENT_BLANK && ts->client) { + blank = evdata->data; + switch (*blank) { + case MSM_DRM_BLANK_POWERDOWN: + if (!ts->initialized) + return -ECANCELED; + himax_common_suspend(&ts->client->dev); + break; + } + } + + if (evdata->data && event == MSM_DRM_EVENT_BLANK && ts->client) { + blank = evdata->data; + switch (*blank) { + case MSM_DRM_BLANK_UNBLANK: + himax_common_resume(&ts->client->dev); + ts->touch_num = 50; + break; + } + } + + return 0; +} +#endif + +static int himax_2nd_probe(struct himax_ts_data *ts) +{ + struct i2c_client *client = ts->client; + int i, ret = 0; + + if (ts->probe_flag) { + pr_err("[HiMax_Touch][ERROR] have initialed already!!\n\n"); + return 0; + } + + for (i = 0; i < 3; i++) { + if (i2c_smbus_read_byte_data(client, 0x40) < 0) { + E("can't read ic data!\n"); + msleep(100); + } else { + break; + } + } + + ret = himax_chip_common_init(ts); + if (ret < 0) { + E("%s: ret=%d\n", __func__, ret); + return ret; + } + + ts->probe_flag = true; + + return ret; +} + +/*static void himax_probe_work(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, probe_work); + + himax_2nd_probe(ts); +} + +static void himax_connected_work(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, + connected_work); + + himax_2nd_probe(ts); + if (ts->probe_flag) + himax_common_resume(&ts->client->dev); + ts->touch_num = 50; +} + +static void himax_disconnected_work(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, + disconnected_work); + + if (ts->probe_flag) + himax_common_suspend(&ts->client->dev); +} + +int himax_hotplug_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct himax_ts_data *ts = container_of(nb, struct himax_ts_data, + cable_hotplug_nb); + + I("%s:Enter\n", __func__); + + if (extcon_get_state(ts->edev, EXTCON_JACK_VIDEO_OUT)) { + cancel_work_sync(&ts->connected_work); + schedule_work(&ts->connected_work); + } else { + cancel_work_sync(&ts->disconnected_work); + schedule_work(&ts->disconnected_work); + } + + return 0; +} + +int tp_diag_himax(void) +{ + int flag = 1; + // int flag = 0; + + // if(tp_client) { + // if( i2c_smbus_read_byte_data(tp_client, 0x10) < 0) { + // E("[DIAG] himax i2c bus connection error\n"); + // } else { + // flag = 1; + // I("[DIAG]PASS!\n"); + // } + // return flag; + // } else { + // E("[DIAG]himax probe error\n"); + // } + return flag; +} +EXPORT_SYMBOL(tp_diag_himax); +*/ + +static int himax_chip_common_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + //struct device_node *node = client->dev.of_node; + int ret = 0; + struct himax_ts_data *ts; + + I("%s:Enter\n", __func__); + + /* Check I2C functionality */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + E("%s: i2c check functionality error\n", __func__); + return -ENODEV; + } + + ts = kzalloc(sizeof(struct himax_ts_data), GFP_KERNEL); + if (ts == NULL) { + E("%s: allocate himax_ts_data failed\n", __func__); + ret = -ENOMEM; + goto err_alloc_data_failed; + } + + i2c_set_clientdata(client, ts); + ts->client = client; + ts->dev = &client->dev; + mutex_init(&ts->rw_lock); + + /* FIXME */ + INIT_LIST_HEAD(&ts->chips); +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HIMAX_IC_HX83192) + list_add_tail(&hx83192_chip.list, &ts->chips); +#endif + + ts->rw_buf = kcalloc(BUS_RW_MAX_LEN, sizeof(uint8_t), GFP_KERNEL); + if (!ts->rw_buf) { + E("Allocate I2C RW Buffer failed\n"); + ret = -ENODEV; + goto err_alloc_rw_buf_failed; + } + + ts->initialized = false; + ts->touch_num = 50; + + himax_2nd_probe(ts); + + /*ts->edev = extcon_find_edev_by_node(node->parent->parent); + if (IS_ERR(ts->edev)) { + if (PTR_ERR(ts->edev) != -EPROBE_DEFER) + dev_err(ts->dev, "Invalid or missing extcon\n"); + return PTR_ERR(ts->edev); + } + + INIT_WORK(&ts->connected_work, himax_connected_work); + INIT_WORK(&ts->disconnected_work, himax_disconnected_work); + + ts->cable_hotplug_nb.notifier_call = himax_hotplug_notifier; + ret = extcon_register_notifier(ts->edev, EXTCON_JACK_VIDEO_OUT, + &ts->cable_hotplug_nb); + if (ret < 0) { + dev_err(ts->dev, "failed to register notifier for SDP\n"); + return ret; + } + + ts->probe_flag = false; + ret = extcon_get_state(ts->edev, EXTCON_JACK_VIDEO_OUT); + if (ret) { + I("[HiMax_Touch]%s:touch is connected\n",__func__); + INIT_WORK(&ts->probe_work, himax_probe_work); + schedule_work(&ts->probe_work); + }*/ + + // tp_client = client; + return 0; + +err_alloc_rw_buf_failed: + kfree(ts); +err_alloc_data_failed: + + return ret; +} + +static void himax_chip_common_remove(struct i2c_client *client) +{ + struct himax_ts_data *ts = i2c_get_clientdata(client); + + if (ts->hx_chip_inited) + himax_chip_common_deinit(ts); + + /* FIXME */ + // himax_hx83192_remove(); +} + +static const struct i2c_device_id himax_common_ts_id[] = { + { HIMAX_common_NAME, 0 }, + {} +}; + +static const struct dev_pm_ops himax_common_pm_ops = { +#if (!defined(HX_CONFIG_FB)) && (!defined(HX_CONFIG_DRM)) + .suspend = himax_common_suspend, + .resume = himax_common_resume, +#endif +}; + +#if defined(CONFIG_OF) +static const struct of_device_id himax_match_table[] = { + { .compatible = "himax,hxcommon" }, + {}, +}; +#else +#define himax_match_table NULL +#endif + +static struct i2c_driver himax_common_driver = { + .id_table = himax_common_ts_id, + .probe = himax_chip_common_probe, + .remove = himax_chip_common_remove, + .driver = { + .name = HIMAX_common_NAME, + .owner = THIS_MODULE, + .of_match_table = himax_match_table, +#if defined(CONFIG_PM) + .pm = &himax_common_pm_ops, +#endif + }, +}; + +static int __init himax_common_init(void) +{ + I("Himax common touch panel driver init\n"); + D("Himax check double loading\n"); + if (mmi_refcnt++ > 0) { + I("Himax driver has been loaded! ignoring....\n"); + + return 0; + } + i2c_add_driver(&himax_common_driver); + + return 0; +} + +static void __exit himax_common_exit(void) +{ + i2c_del_driver(&himax_common_driver); +} + +late_initcall(himax_common_init); +module_exit(himax_common_exit); + +MODULE_DESCRIPTION("Himax_common driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/hxchipset/himax_platform.h b/drivers/input/touchscreen/hxchipset/himax_platform.h new file mode 100644 index 000000000000..fe1f9348d682 --- /dev/null +++ b/drivers/input/touchscreen/hxchipset/himax_platform.h @@ -0,0 +1,116 @@ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2021 Himax Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef HIMAX_PLATFORM_H +#define HIMAX_PLATFORM_H + +#include +#include +#include +#include +#include +#include + +#define HIMAX_I2C_PLATFORM +#define HIMAX_I2C_RETRY_TIMES 3 +#define BUS_RW_MAX_LEN 256 + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) +#define D(x...) pr_debug("[HXTP] " x) +#define I(x...) pr_info("[HXTP] " x) +#define W(x...) pr_warn("[HXTP][WARNING] " x) +#define E(x...) pr_err("[HXTP][ERROR] " x) +#define DIF(x...) \ + do { \ + if (debug_flag) \ + pr_debug("[HXTP][DEBUG] " x) \ + } while (0) +#else + +#define D(x...) +#define I(x...) +#define W(x...) +#define E(x...) +#define DIF(x...) +#endif + +#define HIMAX_common_NAME "himax_tp" +#define HIMAX_I2C_ADDR 0x48 +#define INPUT_DEV_NAME "himax-touchscreen" + +struct himax_i2c_platform_data { + int abs_x_min; + int abs_x_max; + int abs_x_fuzz; + int abs_y_min; + int abs_y_max; + int abs_y_fuzz; + int abs_pressure_min; + int abs_pressure_max; + int abs_pressure_fuzz; + int abs_width_min; + int abs_width_max; + int screenWidth; + int screenHeight; + uint8_t fw_version; + uint8_t tw_id; + uint8_t powerOff3V3; + uint8_t cable_config[2]; + uint8_t protocol_type; + int gpio_irq; + int fail_det; + int gpio_reset; + int gpio_3v3_en; + int gpio_pon; + int lcm_rst; + int (*power)(int on); + void (*reset)(void); + struct himax_virtual_key *virtual_key; + int hx_config_size; + const char *fw_name; + const char *criteria_file_name; +}; + +extern int himax_bus_read(struct himax_ts_data *ts, uint8_t command, uint8_t *data, uint32_t length, + uint8_t toRetry); +extern int himax_bus_write(struct himax_ts_data *ts, uint8_t command, uint8_t *data, + uint32_t length, uint8_t toRetry); +extern void himax_int_enable(struct himax_ts_data *ts, int enable); +extern int himax_ts_register_interrupt(struct himax_ts_data *ts); +extern int himax_fail_det_register_interrupt(struct himax_ts_data *ts); +int himax_ts_unregister_interrupt(struct himax_ts_data *ts); +extern uint8_t himax_int_gpio_read(int pinnum); +extern int himax_gpio_power_config(struct himax_ts_data *ts, struct himax_i2c_platform_data *pdata); +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata); + +#if defined(HX_CONFIG_FB) +extern int himax_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#elif defined(HX_CONFIG_DRM) +extern int himax_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif +extern int himax_hotplug_notifier(struct notifier_block *self, unsigned long event, void *data); + +extern void himax_ts_work(struct himax_ts_data *ts); +extern void himax_fail_det_work(struct himax_ts_data *ts); +extern enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer); +extern int himax_chip_common_init(struct himax_ts_data *ts); +extern void himax_chip_common_deinit(struct himax_ts_data *ts); +extern int himax_sysfs_init(struct himax_ts_data *ts); +extern void himax_sysfs_deinit(struct himax_ts_data *ts); +int himax_int_en_set(struct himax_ts_data *ts); +int tp_diag_himax(void); +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HIMAX_IC_HX83192) +bool hx83192_chip_detect(struct himax_ts_data *ts); +#endif +#endif