diff options
author | Will McVicker <willmcvicker@google.com> | 2022-04-14 12:14:26 -0700 |
---|---|---|
committer | Will McVicker <willmcvicker@google.com> | 2022-04-14 12:14:26 -0700 |
commit | a243325c2aa6e3219cb08596000b5e2a471dda31 (patch) | |
tree | dfbd462920c4a6aa0df0bf1f4e848a4fea925a65 | |
parent | be6ba854f8a96a4c820db65715747d711e2e80b4 (diff) | |
parent | b85fd5939c20d5cbac45ab426a94aca730d7342a (diff) | |
download | sec_touch-a243325c2aa6e3219cb08596000b5e2a471dda31.tar.gz |
Merge 'android13-gs-pixel-5.10' into android-gs-pixel-mainline
* 'android13-gs-pixel-5.10' of sso://partner-android/kernel/private/google-modules/touch/sec_touch:
touch/sec: update FW grip with offload filter_grip setting.
touch: sec: support rotation reporting
touch/sec: add more logs after touch suspend.
touch/sec: enable FW grip for wake-up gesture.
touch/sec: abandon the cancel event for FoD.
touch/sec: Configure grip prescreen mode with timeout.
Kleaf: add touch sec kernel module for cloudripper
touch/sec: use mode switch for resume insted of T-IC reset.
touch/sec: support FoD (Fingerprint on Display) event.
touch/sec: support FoD (Fingerprint on Display) coordinates.
touch/sec: add bugreport bus reference.
touch/sec: Revise heatmap retrieval for v4l2.
touch/sec: set rt priority for SPI transfer
touch/sec: defer probe if FW initialization failed
touch/sec: refine input timestamp for normal report
touch/sec: defer probe if boot ack timeout
touch: sec: add mutex to protect offload report
touch/sec: Single read command for customlib.
touch/sec: Reduce heatmap enable read commands.
touch/sec: Add delay to read_from_customlib.
touch: sec: tbn function changes
touch/sec_ts: Do not get properties of non-existent supplies
touch/sec: Update SPI delay for reading data in custom library.
touch/sec: handle the specific cases to force WLC mode
touch/sec: add tsp_vsync status
touch/sec: look at display effectively active state
touch/sec: Revise SPI delay for reading encoded heatmap.
touch/sec: use hardware reset to re-initialize touch
touch/sec: Dynamic SPI delay for reading data in custom library.
touch/sec: support encoded heatmap in custom library.
input: touchscreen: sec: Read touch_offload device_id from DT.
touch/sec: use corresponding debug flag for logs
touch/sec: support major and minor conversion
touch/sec: add more information into status sysfs
touch/sec: update touch_offload include path and Makefile
touch/sec: add sysfs to support heatmap dump
touch/sec: Fix -Wtautological-unsigned-zero-compare warnings
touch/sec: update heatmap include path and Makefile
touch/sec: update include path and Makefile for the TBN driver
Signed-off-by: Will McVicker <willmcvicker@google.com>
Change-Id: Ic91a89a3e4c96a722ed84316cbf2791fd12cc62a
-rw-r--r-- | BUILD.bazel | 31 | ||||
-rw-r--r-- | Makefile | 17 | ||||
-rw-r--r-- | sec_ts.c | 1399 | ||||
-rw-r--r-- | sec_ts.h | 142 | ||||
-rw-r--r-- | sec_ts_fn.c | 321 | ||||
-rw-r--r-- | sec_ts_fw.c | 203 | ||||
-rw-r--r-- | sec_ts_only_vendor.c | 2 |
7 files changed, 1675 insertions, 440 deletions
diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 0000000..37eb754 --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,31 @@ +# NOTE: THIS FILE IS EXPERIMENTAL FOR THE BAZEL MIGRATION AND NOT USED FOR +# YOUR BUILDS CURRENTLY. +# +# It is not yet the source of truth for your build. If you're looking to modify +# the build file, modify the Android.bp file instead. Do *not* modify this file +# unless you have coordinated with the team managing the Soong to Bazel +# migration. + +load("//build/kleaf:kernel.bzl", "kernel_module") + +kernel_module( + name = "sec.cloudripper", + srcs = glob([ + "**/*.c", + "**/*.h", + "Kbuild", + ]) + [ + "//private/google-modules/touch/common:headers", + "//private/google-modules/display/samsung:headers", + ], + outs = [ + "sec_touch.ko", + ], + kernel_build = "//private/gs-google:cloudripper", + kernel_module_deps = [ + "//private/google-modules/touch/common:common.cloudripper", + ], + visibility = [ + "//private/gs-google:__pkg__", + ], +) @@ -4,8 +4,19 @@ else obj-$(CONFIG_TOUCHSCREEN_SEC_TS) += sec_ts.o sec_ts_fw.o sec_ts_fn.o sec_cmd.o sec_ts_only_vendor.o endif -KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_SEC_TS=m -EXTRA_CFLAGS = -I$(KERNEL_SRC)/../google-modules/display +KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_SEC_TS=m +EXTRA_CFLAGS += -DDYNAMIC_DEBUG_MODULE +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_TBN +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HEATMAP +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_OFFLOAD +EXTRA_CFLAGS += -I$(KERNEL_SRC)/../google-modules/display +EXTRA_CFLAGS += -I$(KERNEL_SRC)/../google-modules/touch/common +EXTRA_CFLAGS += -I$(KERNEL_SRC)/../google-modules/touch/common/include +EXTRA_SYMBOLS += $(OUT_DIR)/../google-modules/touch/common/Module.symvers modules modules_install clean: - $(MAKE) -C $(KERNEL_SRC) M=$(M) $(KBUILD_OPTIONS) EXTRA_CFLAGS="$(EXTRA_CFLAGS)" $(@) + $(MAKE) -C $(KERNEL_SRC) M=$(M) \ + $(KBUILD_OPTIONS) \ + EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \ + KBUILD_EXTRA_SYMBOLS="$(EXTRA_SYMBOLS)" \ + $(@) @@ -15,6 +15,11 @@ struct sec_ts_data *tsp_info; #include "sec_ts.h" #include <samsung/exynos_drm_connector.h> +/* init the kfifo for debug used. */ +#define SEC_TS_DEBUG_KFIFO_LEN 4 /* must be power of 2. */ +DEFINE_KFIFO(debug_fifo, struct sec_ts_coordinate, SEC_TS_DEBUG_KFIFO_LEN); +static struct sec_ts_coordinate last_coord[SEC_TS_DEBUG_KFIFO_LEN]; + /* Switch GPIO values */ #define SEC_SWITCH_GPIO_VALUE_SLPI_MASTER 1 #define SEC_SWITCH_GPIO_VALUE_AP_MASTER 0 @@ -47,29 +52,45 @@ static void unregister_panel_bridge(struct drm_bridge *bridge); int sec_ts_read_information(struct sec_ts_data *ts); #ifndef I2C_INTERFACE -int sec_ts_spi_delay(u8 reg) +u32 sec_ts_spi_delay(u8 reg, u32 data_len) { + u32 delay_us = 100; + switch (reg) { case SEC_TS_READ_TOUCH_RAWDATA: - return 500; + delay_us = 500; + break; #if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) case SEC_TS_CMD_HEATMAP_READ: - return 500; + delay_us = 500; + break; #endif + case SEC_TS_CMD_CUSTOMLIB_READ_PARAM: + delay_us = min(120 + (data_len >> 2), (u32) 500); + break; case SEC_TS_READ_ALL_EVENT: - return 550; + delay_us = 550; + break; case SEC_TS_READ_CSRAM_RTDP_DATA: - return 550; + delay_us = 550; + break; case SEC_TS_CAAT_READ_STORED_DATA: - return 550; + delay_us = 550; + break; case SEC_TS_CMD_FLASH_READ_DATA: - return 2000; + delay_us = 2000; + break; case SEC_TS_READ_FIRMWARE_INTEGRITY: - return 20*1000; + delay_us = 20*1000; + break; case SEC_TS_READ_SELFTEST_RESULT: - return 4000; - default: return 100; + delay_us = 4000; + break; } + + usleep_range(delay_us, delay_us + 1); + + return delay_us; } int sec_ts_spi_post_delay(u8 reg) @@ -101,7 +122,7 @@ int sec_ts_write(struct sec_ts_data *ts, u8 reg, u8 *data, int len) if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, - "%s: POWER_STATUS : OFF\n", __func__); + "%s: POWER_STATUS: OFF\n", __func__); goto err; } @@ -182,7 +203,7 @@ int sec_ts_write(struct sec_ts_data *ts, u8 reg, u8 *data, int len) if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, - "%s: POWER_STATUS : OFF, retry:%d\n", + "%s: POWER_STATUS: OFF, retry: %d\n", __func__, retry); mutex_unlock(&ts->io_mutex); goto err; @@ -231,6 +252,7 @@ static int sec_ts_read_internal(struct sec_ts_data *ts, u8 reg, #else struct spi_message msg; struct spi_transfer transfer[1] = { { 0 } }; + u32 spi_delay_us = 0; unsigned int i; unsigned int spi_write_len = 0, spi_read_len = 0; unsigned char write_checksum = 0x0, read_checksum = 0x0; @@ -240,7 +262,7 @@ static int sec_ts_read_internal(struct sec_ts_data *ts, u8 reg, if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, - "%s: POWER_STATUS : OFF\n", __func__); + "%s: POWER_STATUS: OFF\n", __func__); goto err; } @@ -311,7 +333,7 @@ static int sec_ts_read_internal(struct sec_ts_data *ts, u8 reg, usleep_range(1 * 1000, 1 * 1000); if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, - "%s: POWER_STATUS : OFF, retry:%d\n", + "%s: POWER_STATUS: OFF, retry: %d\n", __func__, retry); mutex_unlock(&ts->io_mutex); goto err; @@ -356,7 +378,7 @@ static int sec_ts_read_internal(struct sec_ts_data *ts, u8 reg, if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, - "%s: POWER_STATUS : OFF, retry:%d\n", + "%s: POWER_STATUS: OFF, retry: %d\n", __func__, retry); mutex_unlock(&ts->io_mutex); goto err; @@ -372,8 +394,7 @@ static int sec_ts_read_internal(struct sec_ts_data *ts, u8 reg, continue; } - usleep_range(sec_ts_spi_delay(reg), - sec_ts_spi_delay(reg) + 1); + spi_delay_us = sec_ts_spi_delay(reg, len); // read sequence start spi_message_init(&msg); @@ -411,16 +432,29 @@ static int sec_ts_read_internal(struct sec_ts_data *ts, u8 reg, ret = -EIO; - input_err(true, &ts->client->dev, - "%s: retry %d\n", - __func__, retry + 1); - ts->comm_err_count++; + /* + * For LP idle state from AOD to normal screen-on, + * T-IC needs one SPI cmds to wake-up. + * Therefore, change the log level to warning + * for this intended behavir. + */ + if (!completion_done(&ts->bus_resumed) && + reg == SEC_TS_CMD_CHG_SYSMODE) { + dev_warn(&ts->client->dev, + "%s: wake-up touch(#%d) by 0x%02X cmd delay_us(%d)\n", + __func__, retry + 1, reg, spi_delay_us); + } else { + input_err(true, &ts->client->dev, + "%s: retry %d for 0x%02X size(%d) delay_us(%d)\n", + __func__, retry + 1, reg, len, spi_delay_us); + ts->comm_err_count++; + } usleep_range(1 * 1000, 1 * 1000); if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, - "%s: POWER_STATUS : OFF, retry:%d\n", + "%s: POWER_STATUS: OFF, retry: %d\n", __func__, retry); mutex_unlock(&ts->io_mutex); goto err; @@ -451,7 +485,7 @@ static int sec_ts_read_internal(struct sec_ts_data *ts, u8 reg, usleep_range(1 * 1000, 1 * 1000); if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, - "%s: POWER_STATUS : OFF, retry:%d\n", + "%s: POWER_STATUS: OFF, retry: %d\n", __func__, retry); mutex_unlock(&ts->io_mutex); goto err; @@ -482,7 +516,7 @@ static int sec_ts_read_internal(struct sec_ts_data *ts, u8 reg, if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, - "%s: POWER_STATUS : OFF, retry:%d\n", + "%s: POWER_STATUS: OFF, retry: %d\n", __func__, retry); mutex_unlock(&ts->io_mutex); goto err; @@ -527,7 +561,7 @@ static int sec_ts_read_internal(struct sec_ts_data *ts, u8 reg, if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, - "%s: POWER_STATUS : OFF, retry:%d\n", + "%s: POWER_STATUS: OFF, retry: %d\n", __func__, retry); mutex_unlock(&ts->io_mutex); goto err; @@ -543,8 +577,7 @@ static int sec_ts_read_internal(struct sec_ts_data *ts, u8 reg, continue; } - usleep_range(sec_ts_spi_delay(reg), - sec_ts_spi_delay(reg) + 1); + sec_ts_spi_delay(reg, len); copy_size = 0; remain = spi_read_len; @@ -595,7 +628,7 @@ static int sec_ts_read_internal(struct sec_ts_data *ts, u8 reg, == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, - "%s: POWER_STATUS : OFF, retry:%d\n", + "%s: POWER_STATUS: OFF, retry: %d\n", __func__, retry); mutex_unlock(&ts->io_mutex); goto err; @@ -669,10 +702,15 @@ skip_spi_read: } else ts->io_err_count = 0; - /* do hw reset if continuously failed over SEC_TS_IO_RESET_CNT times */ - if (ts->io_err_count >= SEC_TS_IO_RESET_CNT) { + /* + * Do hw reset if continuously failed over SEC_TS_IO_RESET_CNT times + * except for FW update process. + */ + if (ts->io_err_count >= SEC_TS_IO_RESET_CNT && + (ts->bus_refmask & SEC_TS_BUS_REF_FW_UPDATE) == 0) { + ts->hw_reset_count++; ts->io_err_count = 0; - sec_ts_hw_reset(ts); + sec_ts_system_reset(ts, RESET_MODE_HW, false, true); } return ret; @@ -884,7 +922,7 @@ retry_message: usleep_range(1 * 1000, 1 * 1000); if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, - "%s: POWER_STATUS : OFF, retry:%d\n", + "%s: POWER_STATUS: OFF, retry: %d\n", __func__, retry); mutex_unlock(&ts->io_mutex); goto err; @@ -965,7 +1003,9 @@ static int sec_ts_read_from_customlib(struct sec_ts_data *ts, u8 *data, int len) ret = sec_ts_write(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, data, 2); if (ret < 0) input_err(true, &ts->client->dev, - "%s: fail to read custom library command\n", __func__); + "%s: fail to write custom library command\n", __func__); + + usleep_range(100, 100); ret = sec_ts_read(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, (u8 *)data, len); if (ret < 0) @@ -1094,7 +1134,7 @@ int sec_ts_read_calibration_report(struct sec_ts_data *ts) } input_info(true, &ts->client->dev, - "%s: count:%d, pass count:%d, fail count:%d, status:%X, param version:%X %X %X %X\n", + "%s: count: %d, pass cnt: %d, fail cnt: %d, status: %X, param ver: %X %X %X %X\n", __func__, ts->cali_report_try_cnt, ts->cali_report_pass_cnt, ts->cali_report_fail_cnt, ts->cali_report_status, ts->cali_report_param_ver[0], ts->cali_report_param_ver[1], @@ -1103,6 +1143,244 @@ int sec_ts_read_calibration_report(struct sec_ts_data *ts) return ts->cali_report_status; } +#define PTFLIB_ENCODED_ENABLED_OFFSET_LSB 0xA0 +#define PTFLIB_ENCODED_ENABLED_OFFSET_MSB 0x00 +#define PTFLIB_ENCODED_ENABLED_TRUE 0x01 +#define PTFLIB_ENCODED_ENABLED_FALSE 0x00 +static int sec_ts_ptflib_reinit(struct sec_ts_data *ts) +{ + u8 r_data[2] = {0x00, 0x00}; + u8 w_data[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + int ret = 0; + + /* Check whether encoded heatmap is inited in custom library. */ + r_data[0] = PTFLIB_ENCODED_ENABLED_OFFSET_LSB; + r_data[1] = PTFLIB_ENCODED_ENABLED_OFFSET_MSB; + ret = sec_ts_read_from_customlib(ts, r_data, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Read encoded heatmap's inited failed.\n", + __func__); + return -EIO; + } + + if (r_data[1] != 0x01) { + input_err(true, &ts->client->dev, + "%s: Encoded heatmap was not initialized.\n", + __func__); + return -EIO; + } + + /* Return if encoded heatmap is already enabled. */ + if (r_data[0] == 0x01) { + input_info(true, &ts->client->dev, + "%s: Encoded heatmap is already enabled.\n", + __func__); + return 0; + } + + /* Enable encoded heatmap. */ + w_data[0] = PTFLIB_ENCODED_ENABLED_OFFSET_LSB; + w_data[1] = PTFLIB_ENCODED_ENABLED_OFFSET_MSB; + w_data[2] = PTFLIB_ENCODED_ENABLED_TRUE; + ret = sec_ts_write(ts, SEC_TS_CMD_CUSTOMLIB_WRITE_PARAM, w_data, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: ptlib - Writing encoded heatmap's enabled register failed.\n", + __func__); + return -EIO; + } + + /* Check whether encoded heatmap is enabled in customlib. */ + r_data[0] = PTFLIB_ENCODED_ENABLED_OFFSET_LSB; + r_data[1] = PTFLIB_ENCODED_ENABLED_OFFSET_MSB; + ret = sec_ts_read_from_customlib(ts, r_data, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: ptlib - Read encoded heatmap's enabled status failed.\n", + __func__); + return -EIO; + } + + if (r_data[0] != PTFLIB_ENCODED_ENABLED_TRUE) { + input_err(true, &ts->client->dev, + "%s: ptlib - Enabling encoded heatmap failed.\n", + __func__); + return -EIO; + } + + return 0; +} + +#define PTFLIB_GRIP_ENABLED_OFFSET_LSB 0x80 +#define PTFLIB_GRIP_ENABLED_OFFSET_MSB 0x00 +static int sec_ts_ptflib_grip_prescreen_enable(struct sec_ts_data *ts, + int grip_prescreen_mode) { + u8 r_data[2] = {0x00, 0x00}; + u8 w_data[3] = {0x00, 0x00, 0x00}; + int result; + + input_info(true, &ts->client->dev, "%s: set mode %d.\n", + __func__, grip_prescreen_mode); + + if (grip_prescreen_mode < GRIP_PRESCREEN_OFF || + grip_prescreen_mode > GRIP_PRESCREEN_MODE_3) { + input_err(true, &ts->client->dev, + "%s: invalid grip_prescreen_mode value %d.\n", + __func__, grip_prescreen_mode); + return -EINVAL; + } + + w_data[0] = PTFLIB_GRIP_ENABLED_OFFSET_LSB; + w_data[1] = PTFLIB_GRIP_ENABLED_OFFSET_MSB; + w_data[2] = grip_prescreen_mode; + result = ts->sec_ts_write(ts, SEC_TS_CMD_CUSTOMLIB_WRITE_PARAM, + w_data, 3); + if (result < 0) { + input_err(true, &ts->client->dev, + "%s: Write grip_prescreen_mode register failed.\n", + __func__); + return -EIO; + } + + r_data[0] = PTFLIB_GRIP_ENABLED_OFFSET_LSB; + r_data[1] = PTFLIB_GRIP_ENABLED_OFFSET_MSB; + result = ts->sec_ts_read_customlib(ts, r_data, 2); + if (result < 0) { + input_err(true, &ts->client->dev, + "%s: Read grip_prescreen_mode register failed.\n", + __func__); + return -EIO; + } + + if (r_data[0] != grip_prescreen_mode) { + input_err(true, &ts->client->dev, + "%s: Configure grip_prescreen_mode register failed %d.\n", + __func__, r_data[0]); + return -EIO; + } + + return 0; +} + +#define PTFLIB_GRIP_PRESCREEN_TIMEOUT_OFFSET_LSB 0x9A +#define PTFLIB_GRIP_PRESCREEN_TIMEOUT_OFFSET_MSB 0x00 +static int sec_ts_ptflib_grip_prescreen_timeout(struct sec_ts_data *ts, + int grip_prescreen_timeout) { + u8 r_data[2] = {0x00, 0x00}; + u8 w_data[4] = {0x00, 0x00, 0x00, 0x00}; + u16 timeout; + int result; + + input_info(true, &ts->client->dev, "%s: set timeout %d.\n", + __func__, grip_prescreen_timeout); + + if (grip_prescreen_timeout < GRIP_PRESCREEN_TIMEOUT_MIN || + grip_prescreen_timeout > GRIP_PRESCREEN_TIMEOUT_MAX) { + input_err(true, &ts->client->dev, + "%s: invalid grip_prescreen_timeout value %d.\n", + __func__, grip_prescreen_timeout); + return -EINVAL; + } + + w_data[0] = PTFLIB_GRIP_PRESCREEN_TIMEOUT_OFFSET_LSB; + w_data[1] = PTFLIB_GRIP_PRESCREEN_TIMEOUT_OFFSET_MSB; + w_data[2] = grip_prescreen_timeout & 0xFF; + w_data[3] = (grip_prescreen_timeout >> 8) & 0xFF; + result = ts->sec_ts_write(ts, SEC_TS_CMD_CUSTOMLIB_WRITE_PARAM, + w_data, 4); + if (result < 0) { + input_err(true, &ts->client->dev, + "%s: Write grip_prescreen_timeout register failed.\n", + __func__); + return -EIO; + } + + r_data[0] = PTFLIB_GRIP_PRESCREEN_TIMEOUT_OFFSET_LSB; + r_data[1] = PTFLIB_GRIP_PRESCREEN_TIMEOUT_OFFSET_MSB; + result = ts->sec_ts_read_customlib(ts, r_data, 2); + if (result < 0) { + input_err(true, &ts->client->dev, + "%s: Read grip_prescreen_timeout register failed.\n", + __func__); + return -EIO; + } + + timeout = le16_to_cpup((uint16_t *) r_data); + if (timeout != grip_prescreen_timeout) { + input_err(true, &ts->client->dev, + "%s: Configure grip_prescreen_timeout register failed %d.\n", + __func__, timeout); + return -EIO; + } + + return 0; +} + +#define PTFLIB_GRIP_PRESCREEN_FRAMES_OFFSET_LSB 0x9C +#define PTFLIB_GRIP_PRESCREEN_FRAMES_OFFSET_MSB 0x00 +static int sec_ts_ptflib_get_grip_prescreen_frames(struct sec_ts_data *ts) { + u8 r_data[4] = {0x00, 0x00, 0x00, 0x00}; + int result; + + r_data[0] = PTFLIB_GRIP_PRESCREEN_FRAMES_OFFSET_LSB; + r_data[1] = PTFLIB_GRIP_PRESCREEN_FRAMES_OFFSET_MSB; + result = ts->sec_ts_read_customlib(ts, r_data, sizeof(r_data)); + if (result < 0) { + input_err(true, &ts->client->dev, + "%s: Read grip prescreen frames register failed.\n", + __func__); + return -EIO; + } + + return le32_to_cpup((uint32_t *) r_data); +} + +static int sec_ts_ptflib_decoder(struct sec_ts_data *ts, const u16 *in_array, + const int in_array_size, u16 *out_array, + const int out_array_max_size) +{ + const u16 ESCAPE_MASK = 0xF000; + const u16 ESCAPE_BIT = 0x8000; + + int i; + int j; + int out_array_size = 0; + u16 prev_word = 0; + u16 repetition = 0; + + for (i = 0; i < in_array_size; i++) { + u16 curr_word = in_array[i]; + if ((curr_word & ESCAPE_MASK) == ESCAPE_BIT) { + repetition = (curr_word & ~ESCAPE_MASK) - 1; + if (out_array_size + repetition > out_array_max_size) + break; + + for (j = 0; j < repetition; j++) { + *out_array++ = prev_word; + out_array_size++; + } + } else { + if (out_array_size >= out_array_max_size) + break; + + *out_array++ = curr_word; + out_array_size++; + prev_word = curr_word; + } + } + + if (i != in_array_size || out_array_size != out_array_max_size) { + input_info(true, &ts->client->dev, + "%s: %d (in=%d, out=%d, rep=%d, out_max=%d).\n", + __func__, i, in_array_size, out_array_size, + repetition, out_array_max_size); + return -1; + } + + return out_array_size; +} + static void sec_ts_reinit(struct sec_ts_data *ts) { u8 w_data[2] = {0x00, 0x00}; @@ -1240,13 +1518,13 @@ static void sec_ts_reinit(struct sec_ts_data *ts) /* Update a state machine used to toggle control of the touch IC's motion * filter. */ -static void update_motion_filter(struct sec_ts_data *ts) +static void update_motion_filter(struct sec_ts_data *ts, unsigned long touch_id) { /* Motion filter timeout, in milliseconds */ const u32 mf_timeout_ms = 500; u8 next_state; /* Count the active touches */ - u8 touches = hweight32(ts->tid_touch_state); + u8 touches = hweight32(touch_id); if (ts->use_default_mf) return; @@ -1305,11 +1583,20 @@ static void update_motion_filter(struct sec_ts_data *ts) static bool read_heatmap_raw(struct v4l2_heatmap *v4l2) { struct sec_ts_data *ts = container_of(v4l2, struct sec_ts_data, v4l2); - const struct sec_ts_plat_data *pdata = ts->plat_data; + struct sec_ts_plat_data *pdata = ts->plat_data; int result; int max_x = v4l2->format.width; int max_y = v4l2->format.height; + if (ts->v4l2_mutual_strength_updated && + ts->mutual_strength_heatmap.size_x == max_x && + ts->mutual_strength_heatmap.size_y == max_y) { + memcpy(v4l2->frame, ts->mutual_strength_heatmap.data, + max_x * max_y * 2); + ts->v4l2_mutual_strength_updated = false; + return true; + } + if (ts->tsp_dump_lock == 1) { input_info(true, &ts->client->dev, "%s: drop this because raw data reading by others\n", @@ -1328,30 +1615,38 @@ static bool read_heatmap_raw(struct v4l2_heatmap *v4l2) u8 enable; struct heatmap_report report = {0}; - result = sec_ts_read(ts, - SEC_TS_CMD_HEATMAP_ENABLE, &enable, 1); - if (result < 0) { - input_err(true, &ts->client->dev, - "%s: read reg %#x failed, returned %i\n", - __func__, SEC_TS_CMD_HEATMAP_ENABLE, result); - return false; - } - - if (!enable) { - enable = 1; - result = sec_ts_write(ts, + if (!pdata->is_heatmap_enabled) { + result = sec_ts_read(ts, SEC_TS_CMD_HEATMAP_ENABLE, &enable, 1); - if (result < 0) + if (result < 0) { input_err(true, &ts->client->dev, - "%s: enable local heatmap failed, returned %i\n", - __func__, result); - /* - * After local heatmap enabled, it takes `1/SCAN_RATE` - * time to make data ready. But, we don't want to wait - * here to cause overhead. Just drop this and wait for - * next reading. - */ - return false; + "%s: read reg %#x failed, returned %i\n", + __func__, + SEC_TS_CMD_HEATMAP_ENABLE, result); + return false; + } + + if (!enable) { + enable = 1; + result = sec_ts_write(ts, + SEC_TS_CMD_HEATMAP_ENABLE, &enable, 1); + if (result < 0) + input_err(true, &ts->client->dev, + "%s: enable local heatmap failed, returned %i\n", + __func__, result); + else + pdata->is_heatmap_enabled = true; + /* + * After local heatmap enabled, it takes + * `1/SCAN_RATE` time to make data ready. But, + * we don't want to wait here to cause + * overhead. Just drop this and wait for next + * reading. + */ + return false; + } else { + ts->plat_data->is_heatmap_enabled = true; + } } result = sec_ts_read(ts, SEC_TS_CMD_HEATMAP_READ, @@ -1561,6 +1856,100 @@ static void sec_ts_handle_lib_status_event(struct sec_ts_data *ts, } #endif +#ifdef SEC_TS_DEBUG_KFIFO_LEN +inline void sec_ts_kfifo_push_coord(struct sec_ts_data *ts, u8 slot) +{ + if (slot < MAX_SUPPORT_TOUCH_COUNT) { + /* + * Use kfifo as circular buffer by skipping one element + * when fifo is full. + */ + if (kfifo_is_full(&debug_fifo)) + kfifo_skip(&debug_fifo); + kfifo_in(&debug_fifo, &ts->coord[slot], 1); + } +} + +inline void sec_ts_kfifo_pop_all_coords(struct sec_ts_data *ts) +{ + /* + * Keep coords without pop-out to support different timing + * print-out by each caller. + */ + kfifo_out_peek(&debug_fifo, last_coord, kfifo_size(&debug_fifo)); +} + +inline void sec_ts_debug_dump(struct sec_ts_data *ts) +{ + int i; + s64 delta; + s64 sec_longest_duration; + u32 ms_longest_duration; + s64 sec_delta_down; + u32 ms_delta_down; + s64 sec_delta_duration; + u32 ms_delta_duration; + s32 px_delta_x, px_delta_y; + ktime_t current_time = ktime_get(); + + sec_longest_duration = div_u64_rem(ts->longest_duration, + MSEC_PER_SEC, &ms_longest_duration); + + sec_ts_kfifo_pop_all_coords(ts); + for (i = 0 ; i < ARRAY_SIZE(last_coord) ; i++) { + if (last_coord[i].action == SEC_TS_COORDINATE_ACTION_NONE) { + input_dbg(true, &ts->client->dev, + "dump: #%d: N/A!\n", last_coord[i].id); + continue; + } + sec_delta_down = -1; + ms_delta_down = 0; + /* calculate the delta of finger down from current time. */ + delta = ktime_ms_delta(current_time, last_coord[i].ktime_pressed); + if (delta > 0) + sec_delta_down = div_u64_rem(delta, MSEC_PER_SEC, &ms_delta_down); + + /* calculate the delta of finger duration between finger up and down. */ + sec_delta_duration = -1; + ms_delta_duration = 0; + px_delta_x = 0; + px_delta_y = 0; + if (last_coord[i].action == SEC_TS_COORDINATE_ACTION_RELEASE) { + delta = ktime_ms_delta(last_coord[i].ktime_released, + last_coord[i].ktime_pressed); + if (delta > 0) { + sec_delta_duration = div_u64_rem(delta, MSEC_PER_SEC, + &ms_delta_duration); + px_delta_x = last_coord[i].x - last_coord[i].x_pressed; + px_delta_y = last_coord[i].y - last_coord[i].y_pressed; + } + } + input_info(true, &ts->client->dev, + "dump: #%d: %lld.%u(%lld.%u) D(%d, %d).\n", + last_coord[i].id, + sec_delta_down, ms_delta_down, + sec_delta_duration, ms_delta_duration, + px_delta_x, px_delta_y); + input_dbg(true, &ts->client->dev, + "dump-dbg: #%d: (%d, %d) (%d, %d).\n", + last_coord[i].id, + last_coord[i].x_pressed, last_coord[i].y_pressed, + last_coord[i].x, last_coord[i].y); + } + input_info(true, &ts->client->dev, + "dump: i/o %u, comm %u, reset %u, longest %lld.%u.\n", + ts->io_err_count, ts->comm_err_count, ts->hw_reset_count, + sec_longest_duration, ms_longest_duration); + input_info(true, &ts->client->dev, + "dump: cnt %u, active %u, wet %u, palm %u.\n", + ts->pressed_count, ts->touch_count, ts->wet_count, ts->palm_count); +} +#else +#define sec_ts_kfifo_push_coord(ts, slot) do {} while (0) +#define sec_ts_kfifo_pop_all_coords(ts) do {} while (0) +#define sec_ts_debug_dump(ts) do {} while (0) +#endif /* #ifdef SEC_TS_DEBUG_KFIFO_LEN */ + static void sec_ts_handle_coord_event(struct sec_ts_data *ts, struct sec_ts_event_coordinate *p_event_coord) { @@ -1585,12 +1974,14 @@ static void sec_ts_handle_coord_event(struct sec_ts_data *ts, SEC_TS_PRESSURE_MAX; ts->coord[t_id].ttype = p_event_coord->ttype_3_2 << 2 | p_event_coord->ttype_1_0 << 0; - ts->coord[t_id].major = p_event_coord->major; - ts->coord[t_id].minor = p_event_coord->minor; + ts->coord[t_id].major = p_event_coord->major * + ts->plat_data->mm2px; + ts->coord[t_id].minor = p_event_coord->minor * + ts->plat_data->mm2px; if (!ts->coord[t_id].palm && (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_PALM)) - ts->coord[t_id].palm_count++; + ts->palm_count++; ts->coord[t_id].palm = (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_PALM); @@ -1609,6 +2000,7 @@ static void sec_ts_handle_coord_event(struct sec_ts_data *ts, ts->offload.coords[t_id].major = ts->coord[t_id].major; ts->offload.coords[t_id].minor = ts->coord[t_id].minor; ts->offload.coords[t_id].pressure = ts->coord[t_id].z; + ts->offload.coords[t_id].rotation = 0; #endif if ((ts->coord[t_id].ttype == @@ -1622,17 +2014,14 @@ static void sec_ts_handle_coord_event(struct sec_ts_data *ts, (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_GLOVE)) { - if (ts->coord[t_id].action == - SEC_TS_COORDINATE_ACTION_RELEASE) { + if (ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_RELEASE) { + s64 ms_delta; - ktime_get_real_ts64(&ts->time_released[t_id]); - - if (ts->time_longest < - (ts->time_released[t_id].tv_sec - - ts->time_pressed[t_id].tv_sec)) - ts->time_longest = - (ts->time_released[t_id].tv_sec - - ts->time_pressed[t_id].tv_sec); + ts->coord[t_id].ktime_released = ktime_get(); + ms_delta = ktime_ms_delta(ts->coord[t_id].ktime_released, + ts->coord[t_id].ktime_pressed); + if (ts->longest_duration < ms_delta) + ts->longest_duration = ms_delta; if (ts->touch_count > 0) ts->touch_count--; @@ -1668,8 +2057,8 @@ static void sec_ts_handle_coord_event(struct sec_ts_data *ts, #endif } else if (ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_PRESS) { - ktime_get_real_ts64(&ts->time_pressed[t_id]); - + ts->coord[t_id].ktime_pressed = ktime_get(); + ts->pressed_count++; ts->touch_count++; if ((ts->touch_count > 4) && (ts->check_multi == 0)) { @@ -1678,6 +2067,8 @@ static void sec_ts_handle_coord_event(struct sec_ts_data *ts, } ts->all_finger_count++; + ts->coord[t_id].x_pressed = ts->coord[t_id].x; + ts->coord[t_id].y_pressed = ts->coord[t_id].y; ts->max_z_value = max_t(unsigned int, ts->coord[t_id].z, ts->max_z_value); @@ -1846,9 +2237,10 @@ static void sec_ts_handle_coord_event(struct sec_ts_data *ts, __func__, t_id); if (t_id < MAX_SUPPORT_TOUCH_COUNT + MAX_SUPPORT_HOVER_COUNT) { + if (ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_PRESS) { input_dbg(false, &ts->client->dev, - "%s[P] tID:%d x:%d y:%d z:%d major:%d minor:%d tc:%d type:%X\n", + "%s[P] tID: %d x: %d y: %d z: %d major: %d minor: %d tc: %u type: %X\n", ts->dex_name, t_id, ts->coord[t_id].x, ts->coord[t_id].y, ts->coord[t_id].z, @@ -1859,8 +2251,9 @@ static void sec_ts_handle_coord_event(struct sec_ts_data *ts, } else if (ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_RELEASE) { + sec_ts_kfifo_push_coord(ts, t_id); input_dbg(false, &ts->client->dev, - "%s[R] tID:%d mc:%d tc:%d lx:%d ly:%d v:%02X%02X cal:%02X(%02X) id(%d,%d) p:%d\n", + "%s[R] tID: %d mc: %d tc: %u lx: %d ly: %d v: %02X%02X cal: %02X(%02X) id(%d,%d)\n", ts->dex_name, t_id, ts->coord[t_id].mcount, ts->touch_count, @@ -1868,11 +2261,9 @@ static void sec_ts_handle_coord_event(struct sec_ts_data *ts, ts->plat_data->img_version_of_ic[2], ts->plat_data->img_version_of_ic[3], ts->cal_status, ts->nv, ts->tspid_val, - ts->tspicid_val, - ts->coord[t_id].palm_count); + ts->tspicid_val); ts->coord[t_id].mcount = 0; - ts->coord[t_id].palm_count = 0; } } } @@ -1958,10 +2349,153 @@ static void sec_ts_populate_coordinate_channel(struct sec_ts_data *ts, dc->coords[j].major = ts->offload.coords[j].major; dc->coords[j].minor = ts->offload.coords[j].minor; dc->coords[j].pressure = ts->offload.coords[j].pressure; + dc->coords[j].rotation = ts->offload.coords[j].rotation; dc->coords[j].status = ts->offload.coords[j].status; } } +static void sec_ts_update_v4l2_mutual_strength(struct sec_ts_data *ts, + uint32_t size_x, + uint32_t size_y, + int16_t *heatmap) +{ + if (!ts->mutual_strength_heatmap.data) { + ts->mutual_strength_heatmap.data = devm_kmalloc( + &ts->client->dev, size_x * size_y * 2, GFP_KERNEL); + if (!ts->mutual_strength_heatmap.data) { + input_err(true, &ts->client->dev, + "%s: kmalloc for mutual_strength_heatmap (%d) failed.\n", + __func__, size_x * size_y * 2); + } else { + ts->mutual_strength_heatmap.size_x = size_x; + ts->mutual_strength_heatmap.size_y = size_y; + input_info(true, &ts->client->dev, + "%s: kmalloc for mutual_strength_heatmap (%d).\n", + __func__, size_x * size_y * 2); + } + } + + if (ts->mutual_strength_heatmap.data) { + if (ts->mutual_strength_heatmap.size_x == size_x && + ts->mutual_strength_heatmap.size_y == size_y) { + memcpy(ts->mutual_strength_heatmap.data, + heatmap, size_x * size_y * 2); + ts->mutual_strength_heatmap.timestamp = + ts->timestamp; + ts->v4l2_mutual_strength_updated = true; + } else { + input_info(true, &ts->client->dev, + "%s: unmatched heatmap size (%d,%d) (%d,%d).\n", + __func__, size_x, size_y, + ts->mutual_strength_heatmap.size_x, + ts->mutual_strength_heatmap.size_y); + } + } +} + +#define PTFLIB_ENCODED_COUNTER_OFFSET 0x00A8 +#define PTFLIB_ENCODED_COUNTER_READ_SIZE 6 +#define PTFLIB_ENCODED_DATA_READ_SIZE 338 +static int sec_ts_populate_encoded_channel(struct sec_ts_data *ts, + struct touch_offload_frame *frame, + int channel) +{ + u32 heatmap_array_len = 0; + u32 decoded_size = 0; + u32 encoded_counter = 0; + u16 read_src_offset = 0; + int read_src_size = 0; + u8 *r_data; + u16 encoded_data_size = 0; + u16 first_word = 0; + int i; + int x; + int y; + int ret = 0; + ktime_t timestamp_read_start; + ktime_t timestamp_read_end; + struct TouchOffloadData2d *mutual_strength = + (struct TouchOffloadData2d *)frame->channel_data[channel]; + + mutual_strength->tx_size = ts->tx_count; + mutual_strength->rx_size = ts->rx_count; + mutual_strength->header.channel_type = frame->channel_type[channel]; + mutual_strength->header.channel_size = + TOUCH_OFFLOAD_FRAME_SIZE_2D(mutual_strength->rx_size, + mutual_strength->tx_size); + heatmap_array_len = ts->tx_count * ts->rx_count; + + if (!ts->encoded_buff) { + ts->encoded_buff = kmalloc( + heatmap_array_len * 2 + PTFLIB_ENCODED_COUNTER_READ_SIZE, + GFP_KERNEL); + if (!ts->encoded_buff) { + input_err(true, &ts->client->dev, + "%s: kmalloc for encoded_buff failed.\n", + __func__); + return -ENOMEM; + } + } + + /* Read encoded heatmap from customlib. */ + read_src_offset = PTFLIB_ENCODED_COUNTER_OFFSET; + read_src_size = PTFLIB_ENCODED_COUNTER_READ_SIZE + + PTFLIB_ENCODED_DATA_READ_SIZE; + r_data = (u8 *) ts->encoded_buff; + r_data[0] = read_src_offset & 0xFF; + r_data[1] = (read_src_offset >> 8) & 0xFF; + timestamp_read_start = ktime_get(); + ret = sec_ts_read_from_customlib(ts, r_data, read_src_size); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Read customlib failed, offset(0x%04X)) size(%d)\n", + __func__, read_src_offset, read_src_size); + return -EIO; + } + timestamp_read_end = ktime_get(); + + encoded_counter = le32_to_cpup((uint32_t *) r_data); + encoded_data_size = le16_to_cpup((uint16_t *) &r_data[4]); + first_word = le16_to_cpup((uint16_t *) &r_data[6]); + + if (encoded_counter == 0 || encoded_data_size == 0 || + first_word == 0x8FFF || + encoded_data_size > PTFLIB_ENCODED_DATA_READ_SIZE) { + decoded_size = 0; + } else { + decoded_size = sec_ts_ptflib_decoder(ts, (u16 *) (r_data + 6), + encoded_data_size / 2, + (u16 *) ts->heatmap_buff, + heatmap_array_len); + } + + ts->plat_data->encoded_frame_counter++; + if (decoded_size != heatmap_array_len) { + ts->plat_data->encoded_skip_counter++; + input_info(true, &ts->client->dev, + "%s: %d (%d,0x%04X,0x%04X,%d) ts(%lld,%lld)\n", + __func__, encoded_counter, + encoded_data_size & 0x0FFF, encoded_data_size, + first_word, decoded_size, + ktime_us_delta(timestamp_read_start, ts->timestamp), + ktime_us_delta(timestamp_read_end, + timestamp_read_start)); + return -EIO; + } + + i = 0; + for (y = mutual_strength->rx_size - 1; y >= 0; y--) + for (x = mutual_strength->tx_size - 1; x >= 0; x--) + ((uint16_t *) mutual_strength->data)[i++] = + ts->heatmap_buff[x * mutual_strength->rx_size + y]; + + sec_ts_update_v4l2_mutual_strength(ts, mutual_strength->tx_size, + mutual_strength->rx_size, + (int16_t *) mutual_strength->data); + + return 0; +} + static void sec_ts_populate_mutual_channel(struct sec_ts_data *ts, struct touch_offload_frame *frame, int channel) @@ -2063,6 +2597,12 @@ static void sec_ts_populate_mutual_channel(struct sec_ts_data *ts, be16_to_cpu(heatmap_value); } } + + if (target_data_type == TYPE_SIGNAL_DATA) { + sec_ts_update_v4l2_mutual_strength(ts, + mutual_strength->tx_size, mutual_strength->rx_size, + (int16_t *) mutual_strength->data); + } } static void sec_ts_populate_self_channel(struct sec_ts_data *ts, @@ -2171,11 +2711,30 @@ static void sec_ts_populate_self_channel(struct sec_ts_data *ts, } } +static void sec_ts_populate_driver_status_channel(struct sec_ts_data *ts, + struct touch_offload_frame *frame, + int channel) +{ + struct TouchOffloadDriverStatus *ds = + (struct TouchOffloadDriverStatus *)frame->channel_data[channel]; + memset(ds, 0, frame->channel_data_size[channel]); + ds->header.channel_type = (u32)CONTEXT_CHANNEL_TYPE_DRIVER_STATUS; + ds->header.channel_size = sizeof(struct TouchOffloadDriverStatus); + + ds->contents.screen_state = 1; + ds->screen_state = (ts->power_status == SEC_TS_STATE_POWER_ON) ? 1 : 0; + + ds->display_refresh_rate = ts->display_refresh_rate; + ds->contents.display_refresh_rate = 1; +} + static void sec_ts_populate_frame(struct sec_ts_data *ts, struct touch_offload_frame *frame) { static u64 index; int i; + int retval = -1; + const struct sec_ts_plat_data *pdata = ts->plat_data; frame->header.index = index++; frame->header.timestamp = ts->timestamp; @@ -2187,38 +2746,90 @@ static void sec_ts_populate_frame(struct sec_ts_data *ts, /* Populate all channels */ for (i = 0; i < frame->num_channels; i++) { - if (frame->channel_type[i] == TOUCH_DATA_TYPE_COORD) + u8 channel_type = frame->channel_type[i]; + + if (channel_type == TOUCH_DATA_TYPE_COORD) { sec_ts_populate_coordinate_channel(ts, frame, i); - else if ((frame->channel_type[i] & TOUCH_SCAN_TYPE_MUTUAL) != 0) - sec_ts_populate_mutual_channel(ts, frame, i); - else if ((frame->channel_type[i] & TOUCH_SCAN_TYPE_SELF) != 0) + } else if ((channel_type & TOUCH_SCAN_TYPE_MUTUAL) != 0) { + if ((pdata->encoded_enable == ENCODED_ENABLE_ON) && + ((channel_type & ~TOUCH_SCAN_TYPE_MUTUAL) == + TOUCH_DATA_TYPE_STRENGTH)) + retval = sec_ts_populate_encoded_channel( + ts, frame, i); + if (retval < 0) + sec_ts_populate_mutual_channel(ts, frame, i); + } else if ((channel_type & TOUCH_SCAN_TYPE_SELF) != 0) { sec_ts_populate_self_channel(ts, frame, i); + } else if ((frame->channel_type[i] == + CONTEXT_CHANNEL_TYPE_DRIVER_STATUS) != 0) + sec_ts_populate_driver_status_channel(ts, frame, i); + else if ((frame->channel_type[i] == + CONTEXT_CHANNEL_TYPE_STYLUS_STATUS) != 0) { + /* Stylus context is not required by this driver */ + input_err(true, &ts->client->dev, + "%s: Driver does not support stylus status", + __func__); + } + } +} + +void sec_ts_enable_ptflib(struct sec_ts_data *ts, bool enable) +{ + struct sec_ts_plat_data *pdata = ts->plat_data; + + input_info(true, &ts->client->dev, + "%s: enable %d.\n", __func__, enable); + + if (enable) { + sec_ts_ptflib_reinit(ts); + if (pdata->grip_prescreen_mode == GRIP_PRESCREEN_MODE_2) { + sec_ts_ptflib_grip_prescreen_timeout(ts, + pdata->grip_prescreen_timeout); + } + sec_ts_ptflib_grip_prescreen_enable(ts, + pdata->grip_prescreen_mode); + } else { + sec_ts_ptflib_grip_prescreen_enable(ts, GRIP_PRESCREEN_OFF); } } -int sec_ts_enable_grip(struct sec_ts_data *ts, bool enable) +int sec_ts_enable_fw_grip(struct sec_ts_data *ts, bool enable) { - u8 value = enable ? 1 : 0; + struct sec_ts_plat_data *pdata = ts->plat_data; + u8 value; int ret; int final_result = 0; + input_info(true, &ts->client->dev, + "%s: enable %d.\n", __func__, enable); + /* Set grip */ + value = enable ? 0x1F : 0; ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_GRIP_DETEC, &value, 1); if (ret < 0) { input_err(true, &ts->client->dev, "%s: SEC_TS_CMD_SET_GRIP_DETEC failed with ret=%d\n", __func__, ret); final_result = ret; - } - - /* Set deadzone */ - value = enable ? 1 : 0; - ret = ts->sec_ts_write(ts, SEC_TS_CMD_EDGE_DEADZONE, &value, 1); - if (ret < 0) { - input_err(true, &ts->client->dev, - "%s: SEC_TS_CMD_EDGE_DEADZONE failed with ret=%d\n", - __func__, ret); - final_result = ret; + } else { + /* Configure grip */ + if (enable) { + u8 mm = 10; + u8 px_lo = (mm * pdata->mm2px) & 0xFF; + u8 px_hi = ((mm * pdata->mm2px) >> 8) & 0xFF; + u8 long_press_zone[10] = {0x02, 0x00, /* long press reject zone type */ + px_hi, px_lo, /* left edge */ + 0x00, 0x00, /* top edge */ + px_hi, px_lo, /* right edge */ + 0x00, 0x00}; /* bottom edge */ + ret = ts->sec_ts_write(ts, SEC_TS_CMD_LONGPRESS_DROP_AREA, + long_press_zone, sizeof(long_press_zone)); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: SEC_TS_CMD_LONGPRESS_DROP_AREA failed with ret=%d\n", + __func__, ret); + } + } } return final_result; @@ -2228,18 +2839,43 @@ static void sec_ts_offload_set_running(struct sec_ts_data *ts, bool running) { if (ts->offload.offload_running != running) { ts->offload.offload_running = running; - if (running) { - pr_info("%s: disabling FW grip.\n", __func__); - sec_ts_enable_grip(ts, false); + if (running && ts->offload.config.filter_grip) { + sec_ts_enable_fw_grip(ts, false); + sec_ts_enable_ptflib(ts, true); } else { - pr_info("%s: enabling FW grip.\n", __func__); - sec_ts_enable_grip(ts, true); + sec_ts_enable_fw_grip(ts, true); + sec_ts_enable_ptflib(ts, false); } } } #endif /* CONFIG_TOUCHSCREEN_OFFLOAD */ +static void sec_ts_handle_fod_event(struct sec_ts_data *ts, + struct sec_ts_event_status *p_event_status) +{ + struct sec_ts_fod_event *p_fod = + (struct sec_ts_fod_event *)p_event_status; + int x = p_fod->x_b11_b8 << 8 | p_fod->x_b7_b0; + int y = p_fod->y_b11_b8 << 8 | p_fod->y_b7_b0; + + if (test_bit(0, &ts->tid_touch_state)) { + input_info(true, &ts->client->dev, + "%s: slot 0 is in use!", __func__); + return; + } + + if (!x || !y) { + input_info(true, &ts->client->dev, + "%s: one of coords is ZERO(%d, %d)!", + __func__, x, y); + x = ts->plat_data->fod_x; + y = ts->plat_data->fod_y; + } + + input_info(true, &ts->client->dev, + "STATUS: FoD: %s, X,Y: %d, %d\n", p_fod->status ? "ON" : "OFF", x, y); +} static void sec_ts_read_vendor_event(struct sec_ts_data *ts, struct sec_ts_event_status *p_event_status) @@ -2272,39 +2908,55 @@ static void sec_ts_read_vendor_event(struct sec_ts_data *ts, break; case SEC_TS_EVENT_STATUS_ID_REPORT_RATE: - if (ts->debug) + ts->report_rate = status_data_1; + if (ts->debug_status) input_info(true, &ts->client->dev, "STATUS: rate %d -> %d\n", status_data_2, status_data_1); break; + case SEC_TS_EVENT_STATUS_ID_VSYNC: + ts->vsync = status_data_1; + if (ts->debug_status) + input_info(true, + &ts->client->dev, + "STATUS: vsync %d -> %d\n", + status_data_2, status_data_1); + break; + case SEC_TS_EVENT_STATUS_ID_WLC: input_info(true, &ts->client->dev, - "STATUS: wlc mode change to %x\n", + "STATUS: WLC: %#x\n", status_data_1); break; case SEC_TS_EVENT_STATUS_ID_NOISE: input_info(true, &ts->client->dev, - "STATUS: noise mode change to %x\n", + "STATUS: noise: %#x\n", status_data_1); break; case SEC_TS_EVENT_STATUS_ID_GRIP: - input_info(true, - &ts->client->dev, - "STATUS: detect grip %s!\n", - (status_data_1) ? - "enter" : "leave"); + if (ts->debug_status) + input_info(true, + &ts->client->dev, + "STATUS: grip: %d.\n", + status_data_1); break; case SEC_TS_EVENT_STATUS_ID_PALM: - input_info(true, - &ts->client->dev, - "STATUS: detect palm!\n"); + input_info(true, &ts->client->dev, + "STATUS: palm: %d.\n", + status_data_1); + if (status_data_1) + ts->palm_count++; + break; + + case SEC_TS_EVENT_STATUS_ID_FOD: + sec_ts_handle_fod_event(ts, p_event_status); break; default: @@ -2381,7 +3033,7 @@ static void sec_ts_read_event(struct sec_ts_data *ts) return; } - if (ts->debug == 0x01) + if (ts->debug_events) input_info(true, &ts->client->dev, "ONE: %02X %02X %02X %02X %02X %02X %02X %02X\n", read_event_buff[0][0], read_event_buff[0][1], @@ -2430,7 +3082,7 @@ static void sec_ts_read_event(struct sec_ts_data *ts) event_buff = read_event_buff[curr_pos]; event_id = event_buff[0] & 0x3; - if (ts->debug == 0x01) + if (ts->debug_events && curr_pos > 0) input_info(true, &ts->client->dev, "ALL: %02X %02X %02X %02X %02X %02X %02X %02X\n", event_buff[0], event_buff[1], event_buff[2], @@ -2455,7 +3107,7 @@ static void sec_ts_read_event(struct sec_ts_data *ts) switch (status_data_1) { case 0x20: /* watchdog reset !? */ - sec_ts_unlocked_release_all_finger(ts); + sec_ts_locked_release_all_finger(ts); ret = sec_ts_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); if (ret < 0) @@ -2467,16 +3119,16 @@ static void sec_ts_read_event(struct sec_ts_data *ts) break; case 0x40: input_info(true, &ts->client->dev, - "%s: sw_reset done\n", + "%s: sw_reset ack.\n", __func__); - sec_ts_unlocked_release_all_finger(ts); + sec_ts_locked_release_all_finger(ts); complete_all(&ts->boot_completed); break; case 0x10: input_info(true, &ts->client->dev, - "%s: hw_reset done\n", + "%s: hw_reset ack.\n", __func__); - sec_ts_unlocked_release_all_finger(ts); + sec_ts_locked_release_all_finger(ts); complete_all(&ts->boot_completed); break; default: @@ -2492,7 +3144,7 @@ static void sec_ts_read_event(struct sec_ts_data *ts) input_err(true, &ts->client->dev, "%s: IC Event Queue is full\n", __func__); - sec_ts_unlocked_release_all_finger(ts); + sec_ts_locked_release_all_finger(ts); } if ((p_event_status->stype == @@ -2521,21 +3173,27 @@ static void sec_ts_read_event(struct sec_ts_data *ts) } #ifdef SEC_TS_SUPPORT_CUSTOMLIB + mutex_lock(&ts->eventlock); sec_ts_handle_lib_status_event(ts, p_event_status); + mutex_unlock(&ts->eventlock); #endif break; case SEC_TS_COORDINATE_EVENT: processed_pointer_event = true; + mutex_lock(&ts->eventlock); sec_ts_handle_coord_event(ts, (struct sec_ts_event_coordinate *)event_buff); + mutex_unlock(&ts->eventlock); break; case SEC_TS_GESTURE_EVENT: p_gesture_status = (struct sec_ts_gesture_status *)event_buff; #ifdef SEC_TS_SUPPORT_CUSTOMLIB + mutex_lock(&ts->eventlock); sec_ts_handle_gesture_event(ts, p_gesture_status); + mutex_unlock(&ts->eventlock); #endif break; @@ -2554,8 +3212,10 @@ static void sec_ts_read_event(struct sec_ts_data *ts) #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) if (!ts->offload.offload_running) { #endif - + mutex_lock(&ts->eventlock); + input_set_timestamp(ts->input_dev, ts->timestamp); input_sync(ts->input_dev); + mutex_unlock(&ts->eventlock); #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) } @@ -2587,7 +3247,9 @@ static void sec_ts_read_event(struct sec_ts_data *ts) */ #if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) if (processed_pointer_event) { - heatmap_read(&ts->v4l2, ktime_to_ns(ts->timestamp)); + if (ts->heatmap_init_done && !ts->offload.offload_running) { + heatmap_read(&ts->v4l2, ktime_to_ns(ts->timestamp)); + } /* palm */ if (last_tid_palm_state == 0 && @@ -2633,6 +3295,10 @@ static void sec_ts_read_event(struct sec_ts_data *ts) "COORD: all fingers released with palm(s)/grip(s) leaved once\n"); } } + + /* Disable the firmware motion filter during single touch */ + if (!ts->offload.offload_running) + update_motion_filter(ts, ts->tid_touch_state); #endif } @@ -2641,9 +3307,6 @@ static irqreturn_t sec_ts_isr(int irq, void *handle) struct sec_ts_data *ts = (struct sec_ts_data *)handle; ts->timestamp = ktime_get(); -#if !IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) - /* input_set_timestamp(ts->input_dev, ts->timestamp); */ -#endif return IRQ_WAKE_THREAD; } @@ -2664,17 +3327,8 @@ static irqreturn_t sec_ts_irq_thread(int irq, void *ptr) cpu_latency_qos_update_request(&ts->pm_qos_req, 100); pm_wakeup_event(&ts->client->dev, MSEC_PER_SEC); - mutex_lock(&ts->eventlock); - sec_ts_read_event(ts); - mutex_unlock(&ts->eventlock); - -#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) - /* Disable the firmware motion filter during single touch */ - update_motion_filter(ts); -#endif - cpu_latency_qos_update_request(&ts->pm_qos_req, PM_QOS_DEFAULT_VALUE); sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_IRQ, false); @@ -2688,14 +3342,18 @@ static void sec_ts_offload_report(void *handle, { struct sec_ts_data *ts = (struct sec_ts_data *)handle; bool touch_down = 0; + unsigned long touch_id = 0; int i; + mutex_lock(&ts->eventlock); + input_set_timestamp(ts->input_dev, report->timestamp); for (i = 0; i < MAX_COORDS; i++) { if (report->coords[i].status == COORD_STATUS_FINGER) { input_mt_slot(ts->input_dev, i); touch_down = 1; + __set_bit(i, &touch_id); input_report_key(ts->input_dev, BTN_TOUCH, touch_down); input_mt_report_slot_state(ts->input_dev, @@ -2712,6 +3370,8 @@ static void sec_ts_offload_report(void *handle, input_report_abs(ts->input_dev, ABS_MT_PRESSURE, report->coords[i].pressure); + input_report_abs(ts->input_dev, ABS_MT_ORIENTATION, + report->coords[i].rotation); } else { input_mt_slot(ts->input_dev, i); input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); @@ -2719,12 +3379,21 @@ static void sec_ts_offload_report(void *handle, MT_TOOL_FINGER, 0); input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1); + input_report_abs(ts->input_dev, ABS_MT_ORIENTATION, 0); } } input_report_key(ts->input_dev, BTN_TOUCH, touch_down); input_sync(ts->input_dev); + + mutex_unlock(&ts->eventlock); + + if (touch_down) + heatmap_read(&ts->v4l2, ktime_to_ns(report->timestamp)); + + /* Disable the firmware motion filter during single touch */ + update_motion_filter(ts, touch_id); } #endif /* CONFIG_TOUCHSCREEN_OFFLOAD */ @@ -2749,7 +3418,7 @@ int sec_ts_glove_mode_enables(struct sec_ts_data *ts, int mode) if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, - "%s: pwr off, glove:%d, status:%x\n", __func__, + "%s: pwr off, glove: %d, status: %x\n", __func__, mode, ts->touch_functions); goto glove_enable_err; } @@ -2763,7 +3432,7 @@ int sec_ts_glove_mode_enables(struct sec_ts_data *ts, int mode) } input_info(true, &ts->client->dev, - "%s: glove:%d, status:%x\n", __func__, + "%s: glove: %d, status: %x\n", __func__, mode, ts->touch_functions); return 0; @@ -2815,7 +3484,7 @@ int sec_ts_set_cover_type(struct sec_ts_data *ts, bool enable) if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, - "%s: pwr off, close:%d, status:%x\n", __func__, + "%s: pwr off, close: %d, status: %x\n", __func__, enable, ts->touch_functions); goto cover_enable_err; } @@ -2840,7 +3509,7 @@ int sec_ts_set_cover_type(struct sec_ts_data *ts, bool enable) } input_info(true, &ts->client->dev, - "%s: close:%d, status:%x\n", __func__, + "%s: close: %d, status: %x\n", __func__, enable, ts->touch_functions); return 0; @@ -2857,7 +3526,7 @@ void sec_ts_set_grip_type(struct sec_ts_data *ts, u8 set_type) u8 mode = G_NONE; input_info(true, &ts->client->dev, - "%s: re-init grip(%d), edh:%d, edg:%d, lan:%d\n", __func__, + "%s: re-init grip(%d), edh: %d, edg: %d, lan: %d\n", __func__, set_type, ts->grip_edgehandler_direction, ts->grip_edge_range, ts->grip_landscape_mode); @@ -3017,6 +3686,7 @@ static int sec_ts_parse_dt(struct spi_device *client) struct sec_ts_plat_data *pdata = dev->platform_data; struct device_node *np = dev->of_node; u32 coords[2]; + u8 offload_id[4]; int ret = 0; int count = 0; u32 ic_match_value; @@ -3069,10 +3739,10 @@ static int sec_ts_parse_dt(struct spi_device *client) "%s: Failed to get tsp-icid gpio\n", __func__); } - pdata->tsp_vsync = of_get_named_gpio(np, "sec,tsp_vsync_gpio", 0); - if (gpio_is_valid(pdata->tsp_vsync)) + pdata->vsync_gpio = of_get_named_gpio(np, "sec,tsp_vsync_gpio", 0); + if (gpio_is_valid(pdata->vsync_gpio)) input_info(true, &client->dev, "%s: vsync %s\n", __func__, - gpio_get_value(pdata->tsp_vsync) ? + gpio_get_value(pdata->vsync_gpio) ? "disable" : "enable"); pdata->irq_gpio = of_get_named_gpio(np, "sec,irq_gpio", 0); @@ -3122,12 +3792,24 @@ static int sec_ts_parse_dt(struct spi_device *client) pdata->max_x = coords[0] - 1; pdata->max_y = coords[1] - 1; + if (of_property_read_u32_array(np, "sec,fod_coords", coords, 2)) { + input_info(true, &client->dev, + "%s: sec,fod_coords not found!\n", __func__); + coords[0] = 0; + coords[1] = 0; + } + pdata->fod_x = coords[0]; + pdata->fod_y = coords[1]; + #ifdef PAT_CONTROL if (of_property_read_u32(np, "sec,pat_function", &pdata->pat_function) < 0) { pdata->pat_function = 0; input_err(true, dev, "%s: Failed to get pat_function property\n", __func__); + } else { + input_info(true, dev, + "%s: pat_function: %#x\n", __func__, pdata->pat_function); } if (of_property_read_u32(np, "sec,afe_base", &pdata->afe_base) < 0) { @@ -3167,7 +3849,7 @@ static int sec_ts_parse_dt(struct spi_device *client) return -EINVAL; } } else { - input_err(true, dev, "%s: Failed to get switch_gpio\n", + input_info(true, dev, "%s: unavailable switch_gpio!\n", __func__); } @@ -3262,6 +3944,16 @@ static int sec_ts_parse_dt(struct spi_device *client) &pdata->mis_cal_check) < 0) pdata->mis_cal_check = 0; + if (of_property_read_u32(np, "sec,encoded_enable", + &pdata->encoded_enable) < 0) + pdata->encoded_enable = 0; + + pdata->grip_prescreen_mode = GRIP_PRESCREEN_MODE_2; + pdata->grip_prescreen_timeout = 120; + pdata->is_heatmap_enabled = false; + pdata->encoded_frame_counter = 0; + pdata->encoded_skip_counter = 0; + if (of_property_read_u32(np, "sec,heatmap_mode", &pdata->heatmap_mode) < 0) pdata->heatmap_mode = 0; @@ -3274,20 +3966,28 @@ static int sec_ts_parse_dt(struct spi_device *client) pdata->support_mt_pressure = true; -#ifdef PAT_CONTROL + pdata->offload_id = 0; + if (of_property_read_u8_array(np, "sec,touch_offload_id", + offload_id, 4) == -EINVAL) + input_err(true, &client->dev, + "%s: Failed to read sec,touch_offload_id\n"); + else { + pdata->offload_id = *(u32 *)offload_id; + input_info(true, &client->dev, + "%s: Offload device ID = \"%c%c%c%c\" / 0x%08X\n", + __func__, offload_id[0], offload_id[1], offload_id[2], + offload_id[3], pdata->offload_id); + } + + if (of_property_read_u8(np, "sec,mm2px", &pdata->mm2px) < 0) + pdata->mm2px = 1; input_info(true, &client->dev, - "%s: buffer limit: %d, lcd_id:%06X, bringup:%d, FW:%s(%d), id:%d,%d, pat_function:%d mis_cal:%d dex:%d, gesture:%d\n", - __func__, pdata->io_burstmax, lcdtype, pdata->bringup, - pdata->firmware_name, count, pdata->tsp_id, pdata->tsp_icid, - pdata->pat_function, pdata->mis_cal_check, pdata->support_dex, - pdata->support_sidegesture); -#else + "%s: mm2px %d\n", __func__, pdata->mm2px); + input_info(true, &client->dev, - "%s: buffer limit: %d, lcd_id:%06X, bringup:%d, FW:%s(%d), id:%d,%d, dex:%d, gesture:%d\n", - __func__, pdata->io_burstmax, lcdtype, pdata->bringup, - pdata->firmware_name, count, pdata->tsp_id, pdata->tsp_icid, - pdata->support_dex, pdata->support_sidegesture); -#endif + "%s: io_burstmax: %d, bringup: %d, FW: %s, mis_cal_check: %d\n", + __func__, pdata->io_burstmax, pdata->bringup, + pdata->firmware_name, pdata->mis_cal_check); return ret; } @@ -3320,7 +4020,7 @@ int sec_ts_read_information(struct sec_ts_data *ts) } input_info(true, &ts->client->dev, - "%s: nTX:%d, nRX:%d, rY:%d, rX:%d\n", + "%s: nTX: %d, nRX: %d, rY: %d, rX: %d\n", __func__, data[8], data[9], (data[2] << 8) | data[3], (data[0] << 8) | data[1]); @@ -3344,7 +4044,7 @@ int sec_ts_read_information(struct sec_ts_data *ts) } input_info(true, &ts->client->dev, - "%s: STATUS : %X\n", + "%s: BOOT_STATUS: %X\n", __func__, data[0]); memset(data, 0x0, 4); @@ -3357,7 +4057,7 @@ int sec_ts_read_information(struct sec_ts_data *ts) } input_info(true, &ts->client->dev, - "%s: TOUCH STATUS : %02X, %02X, %02X, %02X\n", + "%s: TS_STATUS: %02X, %02X, %02X, %02X\n", __func__, data[0], data[1], data[2], data[3]); ret = sec_ts_read(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, (u8 *)&(ts->touch_functions), 2); @@ -3369,7 +4069,7 @@ int sec_ts_read_information(struct sec_ts_data *ts) } input_info(true, &ts->client->dev, - "%s: Functions : %02X\n", + "%s: Functions: %02X\n", __func__, ts->touch_functions); out: @@ -3482,8 +4182,12 @@ static void sec_ts_set_input_prop(struct sec_ts_data *ts, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, ts->plat_data->max_y, 0, 0); - input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); - input_set_abs_params(dev, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); + input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0, + 255 * ts->plat_data->mm2px, + 0, 0); + input_set_abs_params(dev, ABS_MT_TOUCH_MINOR, 0, + 255 * ts->plat_data->mm2px, + 0, 0); input_set_abs_params(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER, MT_TOOL_FINGER, 0, 0); #ifdef ABS_MT_CUSTOM @@ -3493,6 +4197,11 @@ static void sec_ts_set_input_prop(struct sec_ts_data *ts, input_set_abs_params(dev, ABS_MT_PRESSURE, 0, SEC_TS_PRESSURE_MAX, 0, 0); + /* Units are (-8192, 8192), representing the range between rotation + * 90 degrees to left and 90 degrees to the right. + */ + input_set_abs_params(dev, ABS_MT_ORIENTATION, -8192, 8192, 0, 0); + if (propbit == INPUT_PROP_POINTER) input_mt_init_slots(dev, MAX_SUPPORT_TOUCH_COUNT, INPUT_MT_POINTER); @@ -3519,7 +4228,7 @@ static int sec_ts_fw_init(struct sec_ts_data *ts) __func__, ret); else input_info(true, &ts->client->dev, - "%s: TOUCH DEVICE ID : %02X, %02X, %02X, %02X, %02X\n", + "%s: DEVICE ID: %02X, %02X, %02X, %02X, %02X\n", __func__, deviceID[0], deviceID[1], deviceID[2], deviceID[3], deviceID[4]); @@ -3549,7 +4258,7 @@ static int sec_ts_fw_init(struct sec_ts_data *ts) __func__, ret); } input_info(true, &ts->client->dev, - "%s: TOUCH STATUS : %02X || %02X, %02X, %02X, %02X\n", + "%s: TOUCH STATUS: %02X || %02X, %02X, %02X, %02X\n", __func__, data[0], data[1], data[2], data[3], data[4]); if (data[0] == SEC_TS_STATUS_BOOT_MODE) @@ -3658,6 +4367,43 @@ static void sec_ts_device_init(struct sec_ts_data *ts) #endif } +static int sec_ts_heatmap_init(struct sec_ts_data *ts) +{ + int ret = 0; + + if (ts->heatmap_init_done) { + input_info(true, &ts->client->dev, "%s: already init done!\n", + __func__); + return ret; + } + + input_info(true, &ts->client->dev, "%s\n", __func__); +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + /* + * Heatmap_probe must be called before irq routine is registered, + * because heatmap_read is called from the irq context. + * If the ISR runs before heatmap_probe is finished, it will invoke + * heatmap_read and cause NPE, since read_frame would not yet be set. + */ + ts->v4l2.parent_dev = &ts->client->dev; + ts->v4l2.input_dev = ts->input_dev; + ts->v4l2.read_frame = read_heatmap_raw; + ts->v4l2.width = ts->tx_count; + ts->v4l2.height = ts->rx_count; + /* 120 Hz operation */ + ts->v4l2.timeperframe.numerator = 1; + ts->v4l2.timeperframe.denominator = 120; + ret = heatmap_probe(&ts->v4l2); + if (ret == 0) { + ts->heatmap_init_done = true; + } else { + input_err(true, &ts->client->dev, + "%s: fail! ret %d\n", __func__, ret); + } +#endif + return ret; +} + #ifdef USE_CHARGER_WORK static struct notifier_block sec_ts_psy_nb; #endif @@ -3682,6 +4428,14 @@ static int sec_ts_probe(struct spi_device *client) return -EIO; } #else + if (client->controller->rt == false) { + client->rt = true; + ret = spi_setup(client); + if (ret < 0) { + input_err(true, &client->dev, "%s: setup SPI rt failed(%d)\n", + __func__, ret); + } + } input_info(true, &client->dev, "%s: SPI interface(%d Hz)\n", __func__, client->max_speed_hz); #endif @@ -3799,13 +4553,14 @@ static int sec_ts_probe(struct spi_device *client) #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) - ts->tbn = tbn_init(&ts->client->dev); - if (!ts->tbn) { - input_err(true, &ts->client->dev, - "%s: TBN initialization error\n", __func__); + if (register_tbn(&ts->tbn_register_mask)) { ret = -ENODEV; + input_err(true, &ts->client->dev, + "%s: Failed to register tbn context.\n", __func__); goto err_init_tbn; } + input_info(true, &ts->client->dev, "%s: tbn_register_mask = %#x.\n", + __func__, ts->tbn_register_mask); #endif if (gpio_is_valid(ts->plat_data->tsp_id)) @@ -3880,6 +4635,12 @@ static int sec_ts_probe(struct spi_device *client) sec_ts_delay(70); ts->power_status = SEC_TS_STATE_POWER_ON; ts->external_factory = false; + ts->heatmap_init_done = false; + ts->mutual_strength_heatmap.timestamp = 0; + ts->mutual_strength_heatmap.size_x = 0; + ts->mutual_strength_heatmap.size_y = 0; + ts->mutual_strength_heatmap.data = NULL; + ts->v4l2_mutual_strength_updated = false; ret = sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); if (ret < 0) { @@ -3891,18 +4652,32 @@ static int sec_ts_probe(struct spi_device *client) input_err(true, &ts->client->dev, "%s: could not read boot status. Assuming no device connected.\n", __func__); + ret = -EPROBE_DEFER; goto err_init; } - input_info(true, &ts->client->dev, - "%s: Attempting to reflash the firmware. Boot status = 0x%02X\n", - __func__, boot_status); - if (boot_status != SEC_TS_STATUS_BOOT_MODE) + switch (boot_status) { + case SEC_TS_STATUS_BOOT_MODE: input_err(true, &ts->client->dev, - "%s: device is not in bootloader mode!\n", - __func__); - - ts->is_fw_corrupted = true; + "%s: boot timeout(status %#x)! Reflash FW to recover.\n", + __func__, boot_status); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_FW_UPDATE, true); + ret = sec_ts_firmware_update_on_probe(ts, true); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_FW_UPDATE, false); + if (ret) { + ts->is_fw_corrupted = true; + ret = -EPROBE_DEFER; + goto err_init; + } + break; + case SEC_TS_STATUS_APP_MODE: + default: + input_err(true, &ts->client->dev, + "%s: boot timeout(status %#x)! Reset system to recover.\n", + __func__, boot_status); + sec_ts_system_reset(ts, RESET_MODE_HW, true, false); + break; + } } input_info(true, &client->dev, "%s: power enable\n", __func__); @@ -3910,6 +4685,7 @@ static int sec_ts_probe(struct spi_device *client) if (ts->is_fw_corrupted == false) { switch (sec_ts_fw_init(ts)) { case SEC_TS_ERR_INIT: + ret = -EPROBE_DEFER; goto err_init; case SEC_TS_ERR_ALLOC_FRAME: goto err_allocate_frame; @@ -3930,28 +4706,13 @@ static int sec_ts_probe(struct spi_device *client) /* init motion filter mode */ ts->use_default_mf = 0; ts->mf_state = SEC_TS_MF_FILTERED; -#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) - /* - * Heatmap_probe must be called before irq routine is registered, - * because heatmap_read is called from the irq context. - * If the ISR runs before heatmap_probe is finished, it will invoke - * heatmap_read and cause NPE, since read_frame would not yet be set. - */ - ts->v4l2.parent_dev = &ts->client->dev; - ts->v4l2.input_dev = ts->input_dev; - ts->v4l2.read_frame = read_heatmap_raw; - ts->v4l2.width = ts->tx_count; - ts->v4l2.height = ts->rx_count; - /* 120 Hz operation */ - ts->v4l2.timeperframe.numerator = 1; - ts->v4l2.timeperframe.denominator = 120; - ret = heatmap_probe(&ts->v4l2); - if (ret) { - input_err(true, &ts->client->dev, - "%s: Heatmap probe failed\n", __func__); - goto err_irq; + + /* init heatmap */ + if (ts->is_fw_corrupted == false) { + ret = sec_ts_heatmap_init(ts); + if (ret) + goto err_irq; } -#endif input_info(true, &ts->client->dev, "%s: request_irq = %d\n", __func__, client->irq); @@ -3965,11 +4726,11 @@ static int sec_ts_probe(struct spi_device *client) } #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) - ts->offload.caps.touch_offload_major_version = 1; - ts->offload.caps.touch_offload_minor_version = 0; - /* ID equivalent to the 4-byte, little-endian string: '00r3' */ - ts->offload.caps.device_id = - '3' << 24 | 'r' << 16 | '0' << 8 | '0' << 0; + ts->offload.caps.touch_offload_major_version = + TOUCH_OFFLOAD_INTERFACE_MAJOR_VERSION; + ts->offload.caps.touch_offload_minor_version = + TOUCH_OFFLOAD_INTERFACE_MINOR_VERSION; + ts->offload.caps.device_id = ts->plat_data->offload_id; ts->offload.caps.display_width = ts->plat_data->max_x + 1; ts->offload.caps.display_height = ts->plat_data->max_y + 1; ts->offload.caps.tx_size = ts->tx_count; @@ -3991,11 +4752,15 @@ static int sec_ts_probe(struct spi_device *client) TOUCH_DATA_TYPE_COORD | TOUCH_DATA_TYPE_STRENGTH; ts->offload.caps.touch_scan_types = TOUCH_SCAN_TYPE_MUTUAL | TOUCH_SCAN_TYPE_SELF; + ts->offload.caps.context_channel_types = + CONTEXT_CHANNEL_TYPE_DRIVER_STATUS; ts->offload.caps.continuous_reporting = true; ts->offload.caps.noise_reporting = false; ts->offload.caps.cancel_reporting = false; + ts->offload.caps.rotation_reporting = true; ts->offload.caps.size_reporting = true; + ts->offload.caps.auto_reporting = false; ts->offload.caps.filter_grip = true; ts->offload.caps.filter_palm = true; ts->offload.caps.num_sensitivity_settings = 1; @@ -4092,7 +4857,8 @@ err_allocate_input_dev_pad: input_free_device(ts->input_dev); err_allocate_input_dev: #if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) - tbn_cleanup(ts->tbn); + if (ts->tbn_register_mask) + unregister_tbn(&ts->tbn_register_mask); err_init_tbn: #endif @@ -4136,6 +4902,7 @@ error_allocate_pdata: void sec_ts_unlocked_release_all_finger(struct sec_ts_data *ts) { int i; + s64 ms_delta; for (i = 0; i < MAX_SUPPORT_TOUCH_COUNT; i++) { input_mt_slot(ts->input_dev, i); @@ -4149,22 +4916,22 @@ void sec_ts_unlocked_release_all_finger(struct sec_ts_data *ts) SEC_TS_COORDINATE_ACTION_MOVE)) { input_info(true, &ts->client->dev, - "%s: [RA] tID:%d mc:%d tc:%d v:%02X%02X cal:%02X(%02X) id(%d,%d) p:%d\n", + "%s: [RA] tID: %d mc: %d tc: %u v: %02X%02X cal: %02X(%02X) id(%d,%d)\n", __func__, i, ts->coord[i].mcount, ts->touch_count, ts->plat_data->img_version_of_ic[2], ts->plat_data->img_version_of_ic[3], ts->cal_status, ts->nv, ts->tspid_val, - ts->tspicid_val, ts->coord[i].palm_count); + ts->tspicid_val); - ktime_get_real_ts64(&ts->time_released[i]); + ts->coord[i].ktime_released = ktime_get(); + ms_delta = ktime_ms_delta(ts->coord[i].ktime_released, + ts->coord[i].ktime_pressed); + if (ts->longest_duration < ms_delta) + ts->longest_duration = ms_delta; - if (ts->time_longest < - (ts->time_released[i].tv_sec - - ts->time_pressed[i].tv_sec)) - ts->time_longest = - (ts->time_released[i].tv_sec - - ts->time_pressed[i].tv_sec); + /* special case to push into kfifo during release all fingers. */ + sec_ts_kfifo_push_coord(ts, i); } #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) @@ -4172,11 +4939,10 @@ void sec_ts_unlocked_release_all_finger(struct sec_ts_data *ts) ts->offload.coords[i].major = 0; ts->offload.coords[i].minor = 0; ts->offload.coords[i].pressure = 0; + ts->offload.coords[i].rotation = 0; #endif ts->coord[i].action = SEC_TS_COORDINATE_ACTION_RELEASE; ts->coord[i].mcount = 0; - ts->coord[i].palm_count = 0; - } input_mt_slot(ts->input_dev, 0); @@ -4204,7 +4970,6 @@ void sec_ts_unlocked_release_all_finger(struct sec_ts_data *ts) #endif input_report_key(ts->input_dev, KEY_HOMEPAGE, 0); input_sync(ts->input_dev); - } void sec_ts_locked_release_all_finger(struct sec_ts_data *ts) @@ -4232,6 +4997,7 @@ static void sec_ts_reset_work(struct work_struct *work) sec_ts_start_device(ts); ts->reset_is_on_going = false; + ts->plat_data->is_heatmap_enabled = false; sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_RESET, false); } @@ -4256,7 +5022,7 @@ void sec_ts_read_init_info(struct sec_ts_data *ts) SEC_TS_NVM_OFFSET_PRESSURE_DELTA_CAL_COUNT); input_info(true, &ts->client->dev, - "%s: fac_nv:%02X, cal_count:%02X\n", + "%s: fac_nv: %02X, cal_count: %02X\n", __func__, ts->nv, ts->cal_count); #ifdef PAT_CONTROL @@ -4341,10 +5107,11 @@ static void sec_ts_fw_update_work(struct work_struct *work) if (ret == SEC_TS_ERR_NA) { ts->is_fw_corrupted = false; sec_ts_device_init(ts); - } else + } else { input_info(true, &ts->client->dev, "%s: fail to sec_ts_fw_init 0x%x\n", __func__, ret); + } } if (ts->is_fw_corrupted == false) @@ -4586,7 +5353,8 @@ static int sec_ts_remove(struct spi_device *client) ts->plat_data->power(ts, false); #if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) - tbn_cleanup(ts->tbn); + if (ts->tbn_register_mask) + unregister_tbn(&ts->tbn_register_mask); #endif if (gpio_is_valid(ts->plat_data->irq_gpio)) @@ -4603,6 +5371,7 @@ static int sec_ts_remove(struct spi_device *client) #if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) kfree(ts->heatmap_buff); + kfree(ts->encoded_buff); #endif #ifdef USE_STIM_PAD kfree(ts->gainTable); @@ -4686,7 +5455,7 @@ int sec_ts_start_device(struct sec_ts_data *ts) ts->touch_functions = ts->touch_functions | SEC_TS_BIT_SETFUNC_COVER; input_info(true, &ts->client->dev, - "%s: cover cmd write type:%d, mode:%x, ret:%d", + "%s: cover cmd write type: %d, mode: %x, ret: %d", __func__, ts->touch_functions, ts->cover_cmd, ret); } else { @@ -4760,9 +5529,10 @@ static int sec_ts_pm_suspend(struct device *dev) { struct sec_ts_data *ts = dev_get_drvdata(dev); - if (ts->bus_refmask) + if (ts->bus_refmask) { input_info(true, &ts->client->dev, "%s: bus_refmask 0x%X\n", __func__, ts->bus_refmask); + } /* Flush work in case a suspend is in progress */ flush_workqueue(ts->event_wq); @@ -4771,6 +5541,19 @@ static int sec_ts_pm_suspend(struct device *dev) input_err(true, &ts->client->dev, "%s: can't suspend because touch bus is in use!\n", __func__); + if (ts->bus_refmask == SEC_TS_BUS_REF_BUGREPORT) { + s64 delta_ms = ktime_ms_delta(ktime_get(), + ts->bugreport_ktime_start); + + if (delta_ms > 30 * MSEC_PER_SEC) { + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_BUGREPORT, false); + pm_relax(&ts->client->dev); + ts->bugreport_ktime_start = 0; + input_err(true, &ts->client->dev, + "%s: force release SEC_TS_BUS_REF_BUGREPORT(delta: %lld)!\n", + __func__, delta_ms); + } + } return -EBUSY; } @@ -4834,6 +5617,13 @@ static void sec_ts_suspend_work(struct work_struct *work) int ret = 0; input_info(true, &ts->client->dev, "%s\n", __func__); + input_info(true, &ts->client->dev, "%s: encoded skipped %d/%d\n", + __func__, ts->plat_data->encoded_skip_counter, + ts->plat_data->encoded_frame_counter); + if (ts->plat_data->grip_prescreen_mode != GRIP_PRESCREEN_OFF) { + input_info(true, &ts->client->dev, "%s: grip prescreened frames %d.\n", + __func__, sec_ts_ptflib_get_grip_prescreen_frames(ts)); + } mutex_lock(&ts->device_mutex); @@ -4846,6 +5636,8 @@ static void sec_ts_suspend_work(struct work_struct *work) return; } + sec_ts_enable_fw_grip(ts, true); + /* Stop T-IC */ sec_ts_fix_tmode(ts, TOUCH_SYSTEM_MODE_SLEEP, TOUCH_MODE_STATE_STOP); ret = sec_ts_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); @@ -4866,25 +5658,34 @@ static void sec_ts_suspend_work(struct work_struct *work) sec_set_switch_gpio(ts, SEC_SWITCH_GPIO_VALUE_SLPI_MASTER); #if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) - if (ts->tbn) - tbn_release_bus(ts->tbn); + if (ts->tbn_register_mask) + tbn_release_bus(ts->tbn_register_mask); #endif mutex_unlock(&ts->device_mutex); + + sec_ts_debug_dump(ts); } static void sec_ts_resume_work(struct work_struct *work) { struct sec_ts_data *ts = container_of(work, struct sec_ts_data, resume_work); + u8 touch_mode[2] = {0}; int ret = 0; input_info(true, &ts->client->dev, "%s\n", __func__); + ts->comm_err_count = 0; + ts->hw_reset_count = 0; + ts->longest_duration = 0; + ts->pressed_count = 0; + ts->palm_count = 0; + ts->wet_count = 0; mutex_lock(&ts->device_mutex); #if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) - if (ts->tbn) - tbn_request_bus(ts->tbn); + if (ts->tbn_register_mask) + tbn_request_bus(ts->tbn_register_mask); #endif sec_set_switch_gpio(ts, SEC_SWITCH_GPIO_VALUE_AP_MASTER); @@ -4902,10 +5703,62 @@ static void sec_ts_resume_work(struct work_struct *work) ts->power_status = SEC_TS_STATE_POWER_ON; - ret = sec_ts_system_reset(ts); - if (ret < 0) + ret = ts->sec_ts_read(ts, SEC_TS_CMD_CHG_SYSMODE, touch_mode, + sizeof(touch_mode)); + if (ret < 0) { input_err(true, &ts->client->dev, - "%s: reset failed! ret %d\n", __func__, ret); + "%s: read touch mode failed(%d)\n", + __func__, ret); + ret = sec_ts_system_reset(ts, RESET_MODE_HW, false, false); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: reset failed! ret %d\n", __func__, ret); + } + } else { + u8 power_mode = TO_TOUCH_MODE; + u8 state_manage_on = { STATE_MANAGE_ON }; + + input_info(true, &ts->client->dev, + "%s: before resume: mode %#x, state %#x.\n", + __func__, touch_mode[0], touch_mode[1]); + + /* Enable Normal scan. */ + ret = sec_ts_write(ts, SEC_TS_CMD_SET_POWER_MODE, + &power_mode, sizeof(power_mode)); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: set power mode failed(%d)\n", + __func__, ret); + ret = sec_ts_system_reset(ts, RESET_MODE_HW, false, false); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: reset failed! ret %d\n", __func__, ret); + } + } else { + /* Wait at least 50 ms for mode change. */ + sec_ts_delay(50); + } + + ret = ts->sec_ts_read(ts, SEC_TS_CMD_CHG_SYSMODE, touch_mode, + sizeof(touch_mode)); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: read touch mode failed(%d)\n", + __func__, ret); + } else { + input_info(true, &ts->client->dev, + "%s: after resume: mode %#x, state %#x.\n", + __func__, touch_mode[0], touch_mode[1]); + } + + ret = sec_ts_write(ts, SEC_TS_CMD_STATEMANAGE_ON, &state_manage_on, + sizeof(state_manage_on)); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: SEC_TS_CMD_STATEMANAGE_ON failed! ret %d\n", + __func__, ret); + } + } if (ts->plat_data->enable_sync) ts->plat_data->enable_sync(true); @@ -4924,7 +5777,9 @@ static void sec_ts_resume_work(struct work_struct *work) sec_ts_set_custom_library(ts); #endif - sec_ts_set_grip_type(ts, ONLY_EDGE_HANDLER); + ts->plat_data->is_heatmap_enabled = false; + ts->plat_data->encoded_frame_counter = 0; + ts->plat_data->encoded_skip_counter = 0; if (ts->dex_mode) { input_info(true, &ts->client->dev, "%s: set dex mode.\n", @@ -4984,8 +5839,10 @@ static void sec_ts_resume_work(struct work_struct *work) input_info(true, &ts->client->dev, "applying touch_offload settings.\n"); - if (!ts->offload.config.filter_grip) - sec_ts_enable_grip(ts, false); + if (ts->offload.config.filter_grip) { + sec_ts_enable_fw_grip(ts, false); + sec_ts_enable_ptflib(ts, true); + } } #endif @@ -5006,14 +5863,18 @@ static void sec_ts_charger_work(struct work_struct *work) u8 charger_mode = SEC_TS_BIT_CHARGER_MODE_NO; bool usb_present = ts->usb_present; bool wlc_online = ts->wlc_online; + bool force_wlc = ts->force_wlc; + const u64 debounce_ms = 500; /* usb case */ - ret = power_supply_get_property(ts->usb_psy, - POWER_SUPPLY_PROP_PRESENT, &prop); - if (ret == 0) { - usb_present = !!prop.intval; - if (usb_present) - charger_mode = SEC_TS_BIT_CHARGER_MODE_WIRE_CHARGER; + if (ts->usb_psy != NULL) { + ret = power_supply_get_property(ts->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (ret == 0) { + usb_present = !!prop.intval; + if (usb_present) + charger_mode = SEC_TS_BIT_CHARGER_MODE_WIRE_CHARGER; + } } /* wlc case */ @@ -5029,29 +5890,42 @@ static void sec_ts_charger_work(struct work_struct *work) } } - /* rtx case */ - /* ret = power_supply_get_property(ts->wireless_psy, - POWER_SUPPLY_PROP_RTX, &prop); */ - if (ret == 0) - pr_debug("%s: RTX %s", __func__, - (!!prop.intval) ? "ON" : "OFF"); - + /* + * RTX case + * ret = power_supply_get_property(ts->wireless_psy, + * POWER_SUPPLY_PROP_RTX, &prop); + * if (ret == 0) + * pr_debug("%s: RTX %s", __func__, + * (!!prop.intval) ? "ON" : "OFF"); + */ + + /* Check if any change for usb and wlc */ if (usb_present == ts->usb_present && - wlc_online == ts->wlc_online && - ts->keep_wlc_mode == false) + wlc_online == ts->wlc_online) { + input_dbg(true, &ts->client->dev, + "%s: usb_present(%d) and wlc_online(%d) no changed!", + __func__, usb_present, wlc_online); return; + } - /* keep wlc mode if usb plug in w/ wlc off case */ - if (ts->keep_wlc_mode) { - input_info(true, &ts->client->dev, - "keep wlc mode after usb plug in during wlc online"); + /* Force wlc case */ + if (usb_present && + !wlc_online && ts->wlc_online && + ktime_before(ts->wlc_changed_ktime, + ktime_add_ms(ts->usb_changed_ktime, debounce_ms))) { + force_wlc = true; charger_mode = SEC_TS_BIT_CHARGER_MODE_WIRELESS_CHARGER; + input_info(true, &ts->client->dev, + "%s: force wlc mode if usb present during wlc online.", + __func__); + } else { + force_wlc = false; } input_info(true, &ts->client->dev, - "%s: keep_wlc_mode %d, USB(%d->%d), WLC(%d->%d), charger_mode(%#x->%#x)", + "%s: force_wlc(%d->%d), usb_present(%d->%d), wlc_online(%d->%d), charger_mode(%#x->%#x)", __func__, - ts->keep_wlc_mode, + ts->force_wlc, force_wlc, ts->usb_present, usb_present, ts->wlc_online, wlc_online, ts->charger_mode, charger_mode); @@ -5082,7 +5956,7 @@ static void sec_ts_charger_work(struct work_struct *work) /* update final charger state */ ts->wlc_online = wlc_online; ts->usb_present = usb_present; - ts->keep_wlc_mode = false; + ts->force_wlc = force_wlc; } #endif @@ -5195,6 +6069,13 @@ static void panel_bridge_disable(struct drm_bridge *bridge) struct sec_ts_data *ts = container_of(bridge, struct sec_ts_data, panel_bridge); + if (bridge->encoder && bridge->encoder->crtc) { + const struct drm_crtc_state *crtc_state = bridge->encoder->crtc->state; + + if (drm_atomic_crtc_effectively_active(crtc_state)) + return; + } + pr_debug("%s\n", __func__); sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SCREEN_ON, false); } @@ -5271,7 +6152,6 @@ static void unregister_panel_bridge(struct drm_bridge *bridge) static int sec_ts_psy_cb(struct notifier_block *nb, unsigned long val, void *data) { - u64 debounce = 500; struct sec_ts_data *ts = container_of(nb, struct sec_ts_data, psy_nb); pr_debug("%s: val %lu", __func__, val); @@ -5283,22 +6163,11 @@ static int sec_ts_psy_cb(struct notifier_block *nb, return NOTIFY_OK; if (ts->usb_psy == data) { - ts->usb_changed_timestamp = ktime_get(); - if (ts->wlc_online) { - input_dbg(true, &ts->client->dev, - "%s: ignore this usb_psy changed during wlc_online!", - __func__); - return NOTIFY_OK; - } + ts->usb_changed_ktime = ktime_get(); } if (ts->wireless_psy != NULL && ts->wireless_psy == data) { - /* keep wlc mode after usb plug in during wlc online */ - if (ts->wlc_online == true && - ts->usb_present == false && - ktime_before(ktime_get(), - ktime_add_ms(ts->usb_changed_timestamp, debounce))) - ts->keep_wlc_mode = true; + ts->wlc_changed_ktime = ktime_get(); } if (ts->power_status == SEC_TS_STATE_POWER_ON) @@ -5361,7 +6230,7 @@ static int __init sec_ts_init(void) { #ifdef CONFIG_BATTERY_SAMSUNG if (lpcharge == 1) { - pr_err("%s %s: Do not load driver due to : lpm %d\n", + pr_err("%s %s: Do not load driver due to lpm %d\n", SECLOG, __func__, lpcharge); return -ENODEV; } @@ -24,17 +24,18 @@ #include <linux/spi/spi.h> #include <linux/input.h> #if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) -#include <linux/input/heatmap.h> +#include <heatmap.h> #endif #include <linux/input/mt.h> #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) -#include <linux/input/touch_offload.h> +#include <touch_offload.h> #endif #include "sec_cmd.h" #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/kernel.h> +#include <linux/kfifo.h> #include <linux/module.h> #include <drm/drm_bridge.h> #include <drm/drm_device.h> @@ -60,7 +61,7 @@ #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) -#include <linux/input/touch_bus_negotiator.h> +#include <touch_bus_negotiator.h> #endif #define SEC_TS_NAME "sec_ts" @@ -558,6 +559,13 @@ enum { **/ }; +enum RESET_MODE { + RESET_MODE_NA = 0x00, + RESET_MODE_SW = 0x01, + RESET_MODE_HW = 0x02, + RESET_MODE_AUTO = (RESET_MODE_SW | RESET_MODE_HW), +}; + enum CUSTOMLIB_EVENT_TYPE { CUSTOMLIB_EVENT_TYPE_SPAY = 0x04, CUSTOMLIB_EVENT_TYPE_PRESSURE_TOUCHED = 0x05, @@ -579,7 +587,8 @@ enum { SEC_TS_BUS_REF_INPUT_DEV = 0x10, SEC_TS_BUS_REF_READ_INFO = 0x20, SEC_TS_BUS_REF_SYSFS = 0x40, - SEC_TS_BUS_REF_FORCE_ACTIVE = 0x80 + SEC_TS_BUS_REF_FORCE_ACTIVE = 0x80, + SEC_TS_BUS_REF_BUGREPORT = 0x100 }; enum { @@ -650,6 +659,23 @@ enum { HEATMAP_FULL = 2 }; +enum { + GRIP_PRESCREEN_OFF = 0, + GRIP_PRESCREEN_MODE_1 = 1, + GRIP_PRESCREEN_MODE_2 = 2, + GRIP_PRESCREEN_MODE_3 = 3 +}; + +enum { + GRIP_PRESCREEN_TIMEOUT_MIN = 0, + GRIP_PRESCREEN_TIMEOUT_MAX = 480 +}; + +enum { + ENCODED_ENABLE_OFF = 0, + ENCODED_ENABLE_ON = 1 +}; + /* Motion filter finite state machine (FSM) states * SEC_TS_MF_FILTERED - default coordinate filtering * SEC_TS_MF_UNFILTERED - unfiltered single-touch coordinates @@ -675,6 +701,13 @@ struct heatmap_report { /* data is in BE order; order should be enforced after data is read */ strength_t data[LOCAL_HEATMAP_WIDTH * LOCAL_HEATMAP_HEIGHT]; } __packed; + +struct heatmap_data { + ktime_t timestamp; + uint16_t size_x; + uint16_t size_y; + uint8_t *data; +} __packed; #endif #define TEST_MODE_MIN_MAX false @@ -749,12 +782,33 @@ struct sec_ts_gesture_status { /* status id for sec_ts event */ #define SEC_TS_EVENT_STATUS_ID_HOPPING 0x33 #define SEC_TS_EVENT_STATUS_ID_REPORT_RATE 0x34 +#define SEC_TS_EVENT_STATUS_ID_VSYNC 0x35 #define SEC_TS_EVENT_STATUS_ID_NOISE 0x64 #define SEC_TS_EVENT_STATUS_ID_WLC 0x66 #define SEC_TS_EVENT_STATUS_ID_GRIP 0x69 +#define SEC_TS_EVENT_STATUS_ID_FOD 0x6B #define SEC_TS_EVENT_STATUS_ID_PALM 0x70 /* 8 byte */ +struct sec_ts_fod_event { + struct { + u8 type; + u8 id; + u8 status; + u8 x_b7_b0; + union { + struct { + u8 y_b11_b8:4; + u8 x_b11_b8:4; + }; + u8 x_y_b11_b8; + }; + u8 y_b7_b0; + u8 reserved[2]; + }; +} __packed; + +/* 8 byte */ struct sec_ts_event_status { union { struct { @@ -817,12 +871,16 @@ struct sec_ts_coordinate { u8 glove_flag; u8 touch_height; u16 mcount; - u8 major; - u8 minor; + u16 major; + u16 minor; bool palm; - int palm_count; u8 left_event; bool grip; + /* for debug purpose. */ + u16 x_pressed; /* x coord on first down timing. */ + u16 y_pressed; /* y coord on first down timing. */ + ktime_t ktime_pressed; + ktime_t ktime_released; }; struct sec_ts_data { @@ -852,11 +910,7 @@ struct sec_ts_data { * in CLOCK_MONOTONIC **/ - struct timespec64 time_pressed[MAX_SUPPORT_TOUCH_COUNT + - MAX_SUPPORT_HOVER_COUNT]; - struct timespec64 time_released[MAX_SUPPORT_TOUCH_COUNT + - MAX_SUPPORT_HOVER_COUNT]; - long time_longest; + s64 longest_duration; /* ms unit */ u8 lowpower_mode; u8 lowpower_status; @@ -871,7 +925,7 @@ struct sec_ts_data { struct completion bus_resumed; struct completion boot_completed; - int touch_count; + unsigned int touch_count; /* active touch slot(s). */ int tx_count; int rx_count; int io_burstmax; @@ -910,9 +964,14 @@ struct sec_ts_data { u8 print_format; u8 ms_frame_type; u8 ss_frame_type; + int heatmap_dump; #if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) struct v4l2_heatmap v4l2; + struct heatmap_data mutual_strength_heatmap; strength_t *heatmap_buff; + strength_t *encoded_buff; + bool heatmap_init_done; + bool v4l2_mutual_strength_updated; #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) @@ -989,16 +1048,25 @@ struct sec_ts_data { int tune_fix_ver; bool external_factory; + int report_rate; + int vsync; int wet_mode; unsigned char ito_test[4]; /* ito panel tx/rx chanel */ unsigned char check_multi; unsigned int multi_count; /* multi touch count */ + unsigned int palm_count; unsigned int wet_count; /* wet mode count */ unsigned int dive_count; /* dive mode count */ - unsigned int comm_err_count; /* comm error count */ - unsigned int io_err_count; /* io error count */ - unsigned int checksum_result; /* checksum result */ + unsigned int comm_err_count; /* comm error count */ + unsigned int io_err_count; /* io error count */ + unsigned int hw_reset_count; + /* + * accumulated count of pressed + * touch from resume to suspend. + */ + unsigned int pressed_count; + unsigned int checksum_result; /* checksum result */ unsigned char module_id[4]; unsigned int all_finger_count; unsigned int all_force_count; @@ -1016,7 +1084,14 @@ struct sec_ts_data { short pressure_right; u8 pressure_user_level; #endif - int debug; + union { + u32 debug; + struct { + u32 debug_events : 1; + u32 debug_status : 1; + u32 debug_reserved : 30; + }; + }; int fs_postcal_mean; @@ -1047,7 +1122,7 @@ struct sec_ts_data { bool grips_leaved_once; #if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) - struct tbn_context *tbn; + u32 tbn_register_mask; #endif struct power_supply *wireless_psy; @@ -1055,8 +1130,11 @@ struct sec_ts_data { struct notifier_block psy_nb; bool wlc_online; bool usb_present; - bool keep_wlc_mode; - ktime_t usb_changed_timestamp; + bool force_wlc; + ktime_t usb_changed_ktime; + ktime_t wlc_changed_ktime; + + ktime_t bugreport_ktime_start; int (*sec_ts_write)(struct sec_ts_data *ts, u8 reg, u8 *data, int len); @@ -1086,6 +1164,8 @@ struct sec_ts_data { }; struct sec_ts_plat_data { + int fod_x; + int fod_y; int max_x; int max_y; unsigned int irq_gpio; @@ -1095,6 +1175,12 @@ struct sec_ts_plat_data { int bringup; int mis_cal_check; int heatmap_mode; + int grip_prescreen_mode; + int grip_prescreen_timeout; + bool is_heatmap_enabled; + int encoded_enable; + int encoded_frame_counter; + int encoded_skip_counter; #ifdef PAT_CONTROL int pat_function; int afe_base; @@ -1119,7 +1205,7 @@ struct sec_ts_plat_data { void (*enable_sync)(bool on); int tsp_icid; int tsp_id; - int tsp_vsync; + int vsync_gpio; int switch_gpio; int reset_gpio; @@ -1130,13 +1216,21 @@ struct sec_ts_plat_data { struct drm_panel *panel; u32 initial_panel_index; + u32 offload_id; + + /* convert mm to pixel for major and minor */ + u8 mm2px; }; +void sec_ts_debug_dump(struct sec_ts_data *ts); int sec_ts_stop_device(struct sec_ts_data *ts); int sec_ts_start_device(struct sec_ts_data *ts); -int sec_ts_hw_reset(struct sec_ts_data *ts); -int sec_ts_sw_reset(struct sec_ts_data *ts); -int sec_ts_system_reset(struct sec_ts_data *ts); +int sec_ts_hw_reset(struct sec_ts_data *ts, bool wait_for_done); +int sec_ts_sw_reset(struct sec_ts_data *ts, bool wait_for_done); +int sec_ts_system_reset(struct sec_ts_data *ts, + enum RESET_MODE mode, + bool wait_for_done, + bool sense_on); int sec_ts_set_lowpowermode(struct sec_ts_data *ts, u8 mode); int sec_ts_firmware_update_on_probe(struct sec_ts_data *ts, bool force_update); int sec_ts_firmware_update_on_hidden_menu(struct sec_ts_data *ts, diff --git a/sec_ts_fn.c b/sec_ts_fn.c index 26771a6..1a07174 100644 --- a/sec_ts_fn.c +++ b/sec_ts_fn.c @@ -246,7 +246,7 @@ static void set_palm_detection_enable(void *device_data) struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); char buff[4] = { 0 }; u8 para = 0x0; - u8 ret = 0; + int ret; input_info(true, &ts->client->dev, "%s: %d\n", __func__, sec->cmd_param[0]); @@ -292,7 +292,7 @@ static void set_grip_detection_enable(void *device_data) struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); char buff[4] = { 0 }; u8 para = 0x0; - u8 ret = 0; + int ret; input_info(true, &ts->client->dev, "%s: %d\n", __func__, sec->cmd_param[0]); @@ -338,7 +338,7 @@ static void set_wet_mode_enable(void *device_data) struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); char buff[4] = { 0 }; u8 para = 0x0; - u8 ret = 0; + int ret; input_info(true, &ts->client->dev, "%s: %d\n", __func__, sec->cmd_param[0]); @@ -384,7 +384,7 @@ static void set_noise_mode_enable(void *device_data) struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); char buff[4] = { 0 }; u8 para = 0x0; - u8 ret = 0; + int ret; input_info(true, &ts->client->dev, "%s: %d\n", __func__, sec->cmd_param[0]); @@ -430,7 +430,7 @@ static void set_continuous_report_enable(void *device_data) struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); char buff[4] = { 0 }; u8 para = 0x0; - u8 ret = 0; + int ret; input_info(true, &ts->client->dev, "%s: %d\n", __func__, sec->cmd_param[0]); @@ -519,7 +519,7 @@ static ssize_t scrub_pos_show(struct device *dev, "%s: scrub_id: %d\n", __func__, ts->scrub_id); #else input_info(true, &ts->client->dev, - "%s: scrub_id: %d, X:%d, Y:%d\n", __func__, + "%s: scrub_id: %d, X: %d, Y: %d\n", __func__, ts->scrub_id, ts->scrub_x, ts->scrub_y); #endif snprintf(buff, sizeof(buff), "%d %d %d", @@ -730,7 +730,7 @@ static ssize_t holding_time_store(struct device *dev, struct sec_cmd_data *sec = dev_get_drvdata(dev); struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); - ts->time_longest = 0; + ts->longest_duration = 0; input_info(true, &ts->client->dev, "%s: clear\n", __func__); @@ -743,10 +743,10 @@ static ssize_t holding_time_show(struct device *dev, struct sec_cmd_data *sec = dev_get_drvdata(dev); struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); - input_info(true, &ts->client->dev, "%s: %ld\n", __func__, - ts->time_longest); + input_info(true, &ts->client->dev, "%s: %lld ms\n", + __func__, ts->longest_duration); - return snprintf(buf, SEC_CMD_BUF_SIZE, "%ld", ts->time_longest); + return snprintf(buf, SEC_CMD_BUF_SIZE, "%lld ms", ts->longest_duration); } static ssize_t all_touch_count_show(struct device *dev, @@ -756,7 +756,7 @@ static ssize_t all_touch_count_show(struct device *dev, struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); input_info(true, &ts->client->dev, - "%s: touch:%d, force:%d, aod:%d, spay:%d\n", __func__, + "%s: touch: %d, force: %d, aod: %d, spay: %d\n", __func__, ts->all_finger_count, ts->all_force_count, ts->all_aod_tap_count, ts->all_spay_count); @@ -788,7 +788,7 @@ static ssize_t z_value_show(struct device *dev, struct sec_cmd_data *sec = dev_get_drvdata(dev); struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); - input_info(true, &ts->client->dev, "%s: max:%d, min:%d, avg:%d\n", + input_info(true, &ts->client->dev, "%s: max: %d, min: %d, avg: %d\n", __func__, ts->max_z_value, ts->min_z_value, ts->sum_z_value); if (ts->all_finger_count) @@ -1052,6 +1052,204 @@ static ssize_t heatmap_mode_show(struct device *dev, #endif } +/* sysfs file node to store grip prescreen mode + * "echo cmd > grip_prescreen_mode" to change + * Possible commands: + * 0 = GRIP_PRESCREEN_OFF + * 1 = GRIP_PRESCREEN_MODE_1 + * 2 = GRIP_PRESCREEN_MODE_2 + * 3 = GRIP_PRESCREEN_MODE_3 + */ +static ssize_t grip_prescreen_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_plat_data *pdata = ts->plat_data; + int result; + int val; + + result = kstrtoint(buf, 10, &val); + if (result < 0 || val < GRIP_PRESCREEN_OFF || + val > GRIP_PRESCREEN_MODE_3) { + input_err(true, &ts->client->dev, + "%s: Invalid input.\n", __func__); + return -EINVAL; + } + + pdata->grip_prescreen_mode = val; + + return count; +} + +static ssize_t grip_prescreen_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + const struct sec_ts_plat_data *pdata = ts->plat_data; + + return scnprintf(buf, PAGE_SIZE, "%d\n", + pdata->grip_prescreen_mode); +} + +/* sysfs file node to store grip prescreen timeout + * "echo timeout > grip_prescreen_timeout" to change + * Possible timeout range: + * GRIP_PRESCREEN_TIMEOUT_MIN ~ GRIP_PRESCREEN_TIMEOUT_MAX + */ +static ssize_t grip_prescreen_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_plat_data *pdata = ts->plat_data; + int result; + int val; + + result = kstrtoint(buf, 10, &val); + if (result < 0 || val < GRIP_PRESCREEN_TIMEOUT_MIN || + val > GRIP_PRESCREEN_TIMEOUT_MAX) { + input_err(true, &ts->client->dev, + "%s: Invalid input.\n", __func__); + return -EINVAL; + } + + pdata->grip_prescreen_timeout = val; + + return count; +} + +static ssize_t grip_prescreen_timeout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + const struct sec_ts_plat_data *pdata = ts->plat_data; + + return scnprintf(buf, PAGE_SIZE, "%d\n", + pdata->grip_prescreen_timeout); +} + +/* sysfs file node to store encoded_enable control + * "echo cmd > encoded_enable" to change + * Possible commands: + * 0 = ENCODED_ENABLE_OFF + * 1 = ENCODED_ENABLE_ON + */ +static ssize_t encoded_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_plat_data *pdata = ts->plat_data; + int result; + int val; + + result = kstrtoint(buf, 10, &val); + if (result < 0 || val < ENCODED_ENABLE_OFF || + val > ENCODED_ENABLE_ON) { + input_err(true, &ts->client->dev, + "%s: Invalid input.\n", __func__); + return -EINVAL; + } + pdata->encoded_enable = val; + + return count; +#else + return 0; +#endif +} + +static ssize_t encoded_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + const struct sec_ts_plat_data *pdata = ts->plat_data; + + return scnprintf(buf, PAGE_SIZE, "%d\n", pdata->encoded_enable); +#else + return scnprintf(buf, PAGE_SIZE, "N/A\n"); +#endif +} + + +/* sysfs file node to dump heatmap + * "echo cmd > heatmap_dump" to change + * Possible commands: + * 0 = disable + * 1 = enable + */ +static ssize_t heatmap_dump_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + int result; + int val; + + result = kstrtoint(buf, 10, &val); + if (result < 0 || val < 0 || val > 1) { + input_err(true, &ts->client->dev, + "%s: Invalid input.\n", __func__); + return -EINVAL; + } + ts->heatmap_dump = val; + return count; +#else + return 0; +#endif +} + +static ssize_t heatmap_dump_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_plat_data *pdata = ts->plat_data; + int x, y, max_x, max_y, index = 0; + + index += scnprintf(buf + index, PAGE_SIZE - index, + "heatmap dump(mode %d) %s\n", + pdata->heatmap_mode, + (ts->heatmap_dump) ? "ENABLE" : "DISABLE"); + + if (!ts->heatmap_dump) + return index; + + max_x = ts->tx_count; + max_y = ts->rx_count; + + for (y = 0 ; y < max_y ; y++) { + for (x = 0 ; x < max_x ; x++) { + index += scnprintf(buf + index, + PAGE_SIZE - index, + " %3d,", + ts->v4l2.frame[y * max_x + x]); + if (x == max_x - 1) + index += scnprintf(buf + index, + PAGE_SIZE - index, "\n"); + } + } + + return index; +#else + return scnprintf(buf, PAGE_SIZE, "N/A\n"); +#endif +} + static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1136,7 +1334,7 @@ static ssize_t status_show(struct device *dev, goto out; } written += scnprintf(buf + written, PAGE_SIZE - written, - "BOOT STATUS: 0x%02X\n", data[0]); + "Boot status: %#x\n", data[0]); memset(data, 0x0, 4); ret = ts->sec_ts_read(ts, SEC_TS_READ_TS_STATUS, data, 4); @@ -1147,7 +1345,7 @@ static ssize_t status_show(struct device *dev, goto out; } written += scnprintf(buf + written, PAGE_SIZE - written, - "TOUCH STATUS: 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", + "Touch status: %#x, %#x, %#x, %#x\n", data[0], data[1], data[2], data[3]); memset(data, 0x0, 2); @@ -1159,8 +1357,17 @@ static ssize_t status_show(struct device *dev, goto out; } written += scnprintf(buf + written, PAGE_SIZE - written, - "Functions: 0x%02X, 0x%02X\n", data[0], data[1]); - + "Functions: %#x, %#x\n", data[0], data[1]); + written += scnprintf(buf + written, PAGE_SIZE - written, + "Charger mode: %#x\n", ts->charger_mode); + written += scnprintf(buf + written, PAGE_SIZE - written, + "Wet mode: %d\n", ts->wet_mode); + written += scnprintf(buf + written, PAGE_SIZE - written, + "Fingers#: %d\n", ts->touch_count); + written += scnprintf(buf + written, PAGE_SIZE - written, + "Report rate: %d\n", ts->report_rate); + written += scnprintf(buf + written, PAGE_SIZE - written, + "Vsync: %d\n", ts->vsync); out: sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); @@ -1182,6 +1389,10 @@ static DEVICE_ATTR_RW(pressure_enable); static DEVICE_ATTR_RO(get_lp_dump); static DEVICE_ATTR_RO(force_recal_count); static DEVICE_ATTR_RW(heatmap_mode); +static DEVICE_ATTR_RW(grip_prescreen_mode); +static DEVICE_ATTR_RW(grip_prescreen_timeout); +static DEVICE_ATTR_RW(encoded_enable); +static DEVICE_ATTR_RW(heatmap_dump); static DEVICE_ATTR_RO(fw_version); static DEVICE_ATTR_RO(status); @@ -1203,6 +1414,10 @@ static struct attribute *cmd_attributes[] = { &dev_attr_get_lp_dump.attr, &dev_attr_force_recal_count.attr, &dev_attr_heatmap_mode.attr, + &dev_attr_grip_prescreen_mode.attr, + &dev_attr_grip_prescreen_timeout.attr, + &dev_attr_encoded_enable.attr, + &dev_attr_heatmap_dump.attr, &dev_attr_fw_version.attr, &dev_attr_status.attr, NULL, @@ -4622,7 +4837,7 @@ int get_tsp_nvm_data(struct sec_ts_data *ts, u8 offset) } input_info(true, &ts->client->dev, - "%s: offset:%u data:%02X\n", __func__, offset, buff[0]); + "%s: offset: %u data: %02X\n", __func__, offset, buff[0]); out_nvm: ret = ts->sec_ts_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); @@ -4646,7 +4861,7 @@ int get_tsp_nvm_data_by_size(struct sec_ts_data *ts, u8 offset, return -ENOMEM; input_info(true, &ts->client->dev, - "%s: offset:%u, length:%d, size:%d\n", + "%s: offset: %u, length: %d, size: %d\n", __func__, offset, length, sizeof(data)); /* SENSE OFF -> CELAR EVENT STACK -> READ NV -> SENSE ON */ @@ -4876,12 +5091,12 @@ static void get_tsp_test_result(void *device_data) result = (struct sec_ts_test_result *)buff; input_info(true, &ts->client->dev, - "%s: [0x%X][0x%X] M:%d, M:%d, A:%d, A:%d\n", + "%s: [0x%X][0x%X] M: %d, M: %d, A: %d, A: %d\n", __func__, *result->data, buff[0], result->module_result, result->module_count, result->assy_result, result->assy_count); - snprintf(buff, sizeof(buff), "M:%s, M:%d, A:%s, A:%d", + snprintf(buff, sizeof(buff), "M: %s, M: %d, A: %s, A: %d", result->module_result == 0 ? "NONE" : result->module_result == 1 ? "FAIL" : "PASS", result->module_count, @@ -6243,7 +6458,7 @@ static void run_force_pressure_calibration(void *device_data) ts->pressure_cal_base = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_PRESSURE_BASE_CAL_COUNT); - input_info(true, &ts->client->dev, "%s: count:%d\n", + input_info(true, &ts->client->dev, "%s: count: %d\n", __func__, ts->pressure_cal_base); enable_irq(ts->client->irq); @@ -6658,7 +6873,7 @@ static void set_pressure_strength(void *device_data) SEC_TS_NVM_OFFSET_PRESSURE_DELTA_CAL_COUNT); input_info(true, &ts->client->dev, - "%s: count:%d\n", __func__, ts->pressure_cal_delta); + "%s: count: %d\n", __func__, ts->pressure_cal_delta); sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); return; @@ -7395,7 +7610,7 @@ static void set_aod_rect(void *device_data) sec_cmd_set_default_result(sec); - input_info(true, &ts->client->dev, "%s: w:%d, h:%d, x:%d, y:%d\n", + input_info(true, &ts->client->dev, "%s: w: %d, h: %d, x: %d, y: %d\n", __func__, sec->cmd_param[0], sec->cmd_param[1], sec->cmd_param[2], sec->cmd_param[3]); @@ -7469,7 +7684,7 @@ static void get_aod_rect(void *device_data) rect_data[i] = (data[i * 2 + 1] & 0xFF) << 8 | (data[i * 2] & 0xFF); - input_info(true, &ts->client->dev, "%s: w:%d, h:%d, x:%d, y:%d\n", + input_info(true, &ts->client->dev, "%s: w: %d, h: %d, x: %d, y: %d\n", __func__, rect_data[0], rect_data[1], rect_data[2], rect_data[3]); @@ -7874,12 +8089,13 @@ static void force_touch_active(void *device_data) struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); int active, ret; + u16 bus_ref = SEC_TS_BUS_REF_FORCE_ACTIVE; sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); sec_cmd_set_default_result(sec); - if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 2) { sec_cmd_set_cmd_result(sec, "NG", 2); sec_cmd_set_cmd_exit(sec); sec->cmd_state = SEC_CMD_STATUS_FAIL; @@ -7888,15 +8104,29 @@ static void force_touch_active(void *device_data) return; } - active = sec->cmd_param[0]; + /* Specific case for bugreport. */ + if (sec->cmd_param[0] == 2) { + bus_ref = SEC_TS_BUS_REF_BUGREPORT; + active = (sec->cmd_param[1]) ? true : false; + if (active) { + sec_ts_debug_dump(ts); + ts->bugreport_ktime_start = ktime_get(); + } else { + ts->bugreport_ktime_start = 0; + } + } else { + active = sec->cmd_param[0]; + } input_info(true, &ts->client->dev, - "%s: %s\n", __func__, active ? "enable" : "disable"); + "%s: %s %#x\n", __func__, active ? "enable" : "disable", + bus_ref); + if (active) pm_stay_awake(&ts->client->dev); else pm_relax(&ts->client->dev); - ret = sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_FORCE_ACTIVE, active); + ret = sec_ts_set_bus_ref(ts, bus_ref, active); if (ret == 0) { sec_cmd_set_cmd_result(sec, "OK", 2); sec->cmd_state = SEC_CMD_STATUS_OK; @@ -8250,9 +8480,18 @@ static void set_touch_mode(void *device_data) break; case 7: input_info(true, &ts->client->dev, - "%s: param = %d, do touch system reset\n", - __func__, sec->cmd_param[0]); - sec_ts_system_reset(ts); + "%s: param = %d %d, do touch system reset\n", + __func__, sec->cmd_param[0], sec->cmd_param[1]); + switch (sec->cmd_param[1]) { + case RESET_MODE_SW: + sec_ts_system_reset(ts, RESET_MODE_SW, true, true); + break; + case RESET_MODE_HW: + sec_ts_system_reset(ts, RESET_MODE_HW, true, true); + break; + default: + sec_ts_system_reset(ts, RESET_MODE_AUTO, true, true); + } break; case 8: input_info(true, &ts->client->dev, @@ -8400,7 +8639,7 @@ int sec_ts_run_rawdata_type(struct sec_ts_data *ts, struct sec_cmd_data *sec) ts->tsp_dump_lock = 1; input_info(true, &ts->client->dev, - "%s: start (wet:%d)##\n", + "%s: start (wet: %d)##\n", __func__, ts->wet_mode); @@ -8444,7 +8683,7 @@ int sec_ts_run_rawdata_type(struct sec_ts_data *ts, struct sec_cmd_data *sec) if (ret < 0) input_info(true, &ts->client->dev, - "%s: mutual %d : error ## ret:%d\n", + "%s: mutual %d : error ## ret: %d\n", __func__, sec->cmd_param[0], ret); else #ifdef USE_SPEC_CHECK @@ -8491,7 +8730,7 @@ int sec_ts_run_rawdata_type(struct sec_ts_data *ts, struct sec_cmd_data *sec) max, &spec_check); if (ret < 0) input_info(true, &ts->client->dev, - "%s: self %d : error ## ret:%d\n", + "%s: self %d : error ## ret: %d\n", __func__, sec->cmd_param[0], ret); else #ifdef USE_SPEC_CHECK @@ -8586,11 +8825,11 @@ int sec_ts_run_rawdata_type(struct sec_ts_data *ts, struct sec_cmd_data *sec) sec_ts_release_tmode(ts); out: - input_info(true, &ts->client->dev, "%s: ito : %02X %02X %02X %02X\n", + input_info(true, &ts->client->dev, "%s: ito: %02X %02X %02X %02X\n", __func__, ts->ito_test[0], ts->ito_test[1] , ts->ito_test[2], ts->ito_test[3]); - input_info(true, &ts->client->dev, "%s: done (wet:%d)##\n", + input_info(true, &ts->client->dev, "%s: done (wet: %d)##\n", __func__, ts->wet_mode); ts->tsp_dump_lock = 0; @@ -8670,7 +8909,7 @@ void sec_ts_run_rawdata_all(struct sec_ts_data *ts, bool full_read) ts->tsp_dump_lock = 1; input_info(true, &ts->client->dev, - "%s: start (wet:%d)##\n", + "%s: start (wet: %d)##\n", __func__, ts->wet_mode); ret = sec_ts_fix_tmode(ts, TOUCH_SYSTEM_MODE_TOUCH, @@ -8693,7 +8932,7 @@ void sec_ts_run_rawdata_all(struct sec_ts_data *ts, bool full_read) &spec_check); if (ret < 0) input_info(true, &ts->client->dev, - "%s: mutual %d : error ## ret:%d\n", + "%s: mutual %d : error ## ret: %d\n", __func__, test_type[i], ret); else #ifdef USE_SPEC_CHECK @@ -8712,7 +8951,7 @@ void sec_ts_run_rawdata_all(struct sec_ts_data *ts, bool full_read) max, &spec_check); if (ret < 0) input_info(true, &ts->client->dev, - "%s: self %d : error ## ret:%d\n", + "%s: self %d : error ## ret: %d\n", __func__, test_type[i], ret); else #ifdef USE_SPEC_CHECK @@ -8779,11 +9018,11 @@ void sec_ts_run_rawdata_all(struct sec_ts_data *ts, bool full_read) sec_ts_release_tmode(ts); out: - input_info(true, &ts->client->dev, "%s: ito : %02X %02X %02X %02X\n", + input_info(true, &ts->client->dev, "%s: ito: %02X %02X %02X %02X\n", __func__, ts->ito_test[0], ts->ito_test[1] , ts->ito_test[2], ts->ito_test[3]); - input_info(true, &ts->client->dev, "%s: done (wet:%d)##\n", + input_info(true, &ts->client->dev, "%s: done (wet: %d)##\n", __func__, ts->wet_mode); ts->tsp_dump_lock = 0; diff --git a/sec_ts_fw.c b/sec_ts_fw.c index 54e0cd5..7810438 100644 --- a/sec_ts_fw.c +++ b/sec_ts_fw.c @@ -104,14 +104,36 @@ static int sec_ts_enter_fw_mode(struct sec_ts_data *ts) return 1; } -int sec_ts_hw_reset(struct sec_ts_data *ts) +int sec_ts_wait_for_reset_done(struct sec_ts_data *ts) { + int ret = 0; + + if (completion_done(&ts->bus_resumed) && + ts->probe_done == true) { + if (!completion_done(&ts->boot_completed) && + wait_for_completion_timeout(&ts->boot_completed, + msecs_to_jiffies(200) == 0)) + ret = -ETIME; + } else { + ret = sec_ts_wait_for_ready_with_count(ts, + SEC_TS_ACK_BOOT_COMPLETE, 10); + } + + return ret; +} + +int sec_ts_hw_reset(struct sec_ts_data *ts, bool wait_for_done) +{ + int ret = 0; int reset_gpio = ts->plat_data->reset_gpio; - input_info(true, &ts->client->dev, "%s\n", __func__); + input_info(true, &ts->client->dev, "%s: wait_for_done %d.\n", + __func__, wait_for_done); + if (wait_for_done) + reinit_completion(&ts->boot_completed); if (!gpio_is_valid(reset_gpio)) { - input_err(true, &ts->client->dev, "%s: invalid gpio %d\n", + input_err(true, &ts->client->dev, "%s: invalid gpio %d.\n", __func__, reset_gpio); return -EINVAL; } @@ -119,135 +141,104 @@ int sec_ts_hw_reset(struct sec_ts_data *ts) gpio_set_value(reset_gpio, 0); sec_ts_delay(10); gpio_set_value(reset_gpio, 1); - /* wait 70 ms at least from bootloader to applicateion mode */ + + /* Wait 70 ms at least from bootloader to applicateion mode. */ sec_ts_delay(70); - return 0; + if (wait_for_done) { + ret = sec_ts_wait_for_reset_done(ts); + if (!ret) + input_info(true, &ts->client->dev, + "%s: done.\n", __func__); + else + input_err(true, &ts->client->dev, + "%s: hw_reset time out!\n", __func__); + complete_all(&ts->boot_completed); + } + + return ret; } -int sec_ts_sw_reset(struct sec_ts_data *ts) +int sec_ts_sw_reset(struct sec_ts_data *ts, bool wait_for_done) { - int ret; + int ret = 0; - input_info(true, &ts->client->dev, "%s\n", __func__); + input_info(true, &ts->client->dev, "%s: wait_for_done %d.\n", + __func__, wait_for_done); + if (wait_for_done) + reinit_completion(&ts->boot_completed); ret = ts->sec_ts_write(ts, SEC_TS_CMD_SW_RESET, NULL, 0); if (ret < 0) { input_err(true, &ts->client->dev, - "%s: write fail, sw_reset\n", __func__); - return 0; + "%s: failed to write sw_reset.\n", __func__); + return -EIO; } - /* wait 70 ms at least from bootloader to applicateion mode */ + /* Wait 70 ms at least from bootloader to applicateion mode. */ sec_ts_delay(70); - ret = sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); - if (ret < 0) { - input_err(true, &ts->client->dev, "%s: time out\n", __func__); - return 0; - } - - input_info(true, &ts->client->dev, "%s: sw_reset\n", __func__); + if (wait_for_done) { + ret = sec_ts_wait_for_reset_done(ts); + if (!ret) + input_info(true, &ts->client->dev, + "%s: done.\n", __func__); + else + input_err(true, &ts->client->dev, + "%s: sw_reset time out!\n", __func__); - /* Sense_on */ - ret = ts->sec_ts_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); - if (ret < 0) { - input_err(true, &ts->client->dev, - "%s: write fail, Sense_on\n", __func__); - return 0; + complete_all(&ts->boot_completed); } return ret; } -int sec_ts_system_reset(struct sec_ts_data *ts) +int sec_ts_system_reset(struct sec_ts_data *ts, + enum RESET_MODE mode, + bool wait_for_done, + bool sense_on) { - int ret = -1; + int ret = 0; - reinit_completion(&ts->boot_completed); - ret = ts->sec_ts_write(ts, SEC_TS_CMD_SW_RESET, NULL, 0); - if (ret < 0) - input_err(true, &ts->client->dev, "%s: write fail, sw_reset\n", - __func__); - else { - /* wait 70 ms at least from bootloader to applicateion mode */ - sec_ts_delay(70); - if (completion_done(&ts->bus_resumed) && - ts->probe_done == true) { - if (!completion_done(&ts->boot_completed) && - wait_for_completion_timeout(&ts->boot_completed, - msecs_to_jiffies(200) == 0)) - ret = -ETIME; - } else - /* Normally it should not happen with any retry. - * But, if happened, retry less time to wait ack - */ - ret = sec_ts_wait_for_ready_with_count(ts, - SEC_TS_ACK_BOOT_COMPLETE, 10); + input_info(true, &ts->client->dev, + "%s: mode %d, wait_for_done %d, sense_on %d.\n", + __func__, mode, wait_for_done, sense_on); - if (ret < 0) + if (mode & RESET_MODE_SW) { + ret = sec_ts_sw_reset(ts, wait_for_done); + if (ret) input_err(true, &ts->client->dev, - "%s: sw_reset time out!\n", __func__); + "%s: sw reset failed."); else - input_info(true, - &ts->client->dev, "%s: sw_reset done\n", - __func__); + goto sw_reset_done; } - if (ret < 0) { - if (!gpio_is_valid(ts->plat_data->reset_gpio)) { + if (mode & RESET_MODE_HW) { + if (ret) input_err(true, &ts->client->dev, - "%s: reset gpio is unavailable!\n", __func__); - goto err_system_reset; - } - - input_err(true, &ts->client->dev, - "%s: sw_reset failed or time out, try hw_reset to recover!\n", - __func__); - ret = sec_ts_hw_reset(ts); - if (ret) { + "%s: sw_reset failed or time out, try hw_reset to recover!\n", + __func__); + ret = sec_ts_hw_reset(ts, wait_for_done); + if (ret) input_err(true, &ts->client->dev, - "%s: hw_reset failed\n", __func__); - goto err_system_reset; - } - - if (completion_done(&ts->bus_resumed) && - ts->probe_done == true) { - if (!completion_done(&ts->boot_completed) && - wait_for_completion_timeout(&ts->boot_completed, - msecs_to_jiffies(200) == 0)) - ret = -ETIME; - } else - ret = sec_ts_wait_for_ready_with_count(ts, - SEC_TS_ACK_BOOT_COMPLETE, 10); + "%s: hw reset failed."); + } +sw_reset_done: + /* Sense on. */ + if (sense_on) { + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); if (ret < 0) { - input_err(true, - &ts->client->dev, "%s: hw_reset time out\n", + input_err(true, &ts->client->dev, + "%s: failed to write sense_on.\n", __func__); - goto err_system_reset; } - input_info(true, &ts->client->dev, "%s: hw_reset done\n", - __func__); - } - - /* Sense_on */ - ret = ts->sec_ts_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); - if (ret < 0) { - input_err(true, &ts->client->dev, "%s: write fail, Sense_on\n", - __func__); - goto err_system_reset; } - /* initialize wet status */ + /* Initialize wet status. */ ts->wet_mode = 0; ts->wet_count = 0; - return 0; - -err_system_reset: - - complete_all(&ts->boot_completed); return ret; } @@ -281,21 +272,21 @@ static void sec_ts_save_version_of_bin(struct sec_ts_data *ts, ts->plat_data->config_version_of_bin[0] = ((fw_hd->para_ver >> 0) & 0xff); - input_info(true, &ts->client->dev, "%s: img_ver of bin = %x.%x.%x.%x\n", + input_info(true, &ts->client->dev, "%s: img_ver of bin: %x.%x.%x.%x\n", __func__, ts->plat_data->img_version_of_bin[0], ts->plat_data->img_version_of_bin[1], ts->plat_data->img_version_of_bin[2], ts->plat_data->img_version_of_bin[3]); - input_info(true, &ts->client->dev, "%s: core_ver of bin = %x.%x.%x.%x\n", + input_info(true, &ts->client->dev, "%s: core_ver of bin: %x.%x.%x.%x\n", __func__, ts->plat_data->core_version_of_bin[0], ts->plat_data->core_version_of_bin[1], ts->plat_data->core_version_of_bin[2], ts->plat_data->core_version_of_bin[3]); - input_info(true, &ts->client->dev, "%s: config_ver of bin = %x.%x.%x.%x\n", + input_info(true, &ts->client->dev, "%s: config_ver of bin: %x.%x.%x.%x\n", __func__, ts->plat_data->config_version_of_bin[0], ts->plat_data->config_version_of_bin[1], @@ -318,7 +309,7 @@ static int sec_ts_save_version_of_ic(struct sec_ts_data *ts) return -EIO; } input_info(true, &ts->client->dev, - "%s: IC Image version info : %x.%x.%x.%x\n", + "%s: IC Image version info: %x.%x.%x.%x\n", __func__, img_ver[0], img_ver[1], img_ver[2], img_ver[3]); ts->plat_data->img_version_of_ic[0] = img_ver[0]; @@ -334,7 +325,7 @@ static int sec_ts_save_version_of_ic(struct sec_ts_data *ts) return -EIO; } input_info(true, &ts->client->dev, - "%s: IC Core version info : %x.%x.%x.%x,\n", + "%s: IC Core version info: %x.%x.%x.%x,\n", __func__, core_ver[0], core_ver[1], core_ver[2], core_ver[3]); ts->plat_data->core_version_of_ic[0] = core_ver[0]; @@ -350,7 +341,7 @@ static int sec_ts_save_version_of_ic(struct sec_ts_data *ts) return -EIO; } input_info(true, &ts->client->dev, - "%s: IC config version info : %x.%x.%x.%x\n", + "%s: IC config version info: %x.%x.%x.%x\n", __func__, config_ver[0], config_ver[1], config_ver[2], config_ver[3]); @@ -506,7 +497,7 @@ static bool sec_ts_limited_flashpagewrite(struct sec_ts_data *ts, ret = ts->sec_ts_write_burst_heap(ts, tCmd, 1 + copy_cur); if (ret < 0) input_err(true, &ts->client->dev, - "%s: failed, ret:%d\n", __func__, ret); + "%s: failed, ret: %d\n", __func__, ret); copy_size += copy_cur; copy_left -= copy_cur; @@ -832,13 +823,13 @@ static int sec_ts_firmware_update(struct sec_ts_data *ts, const u8 *data, return -1; } - input_err(true, &ts->client->dev, "%s: num_chunk : %d\n", + input_info(true, &ts->client->dev, "%s: num_chunk: %d\n", __func__, fw_hd->num_chunk); for (i = 0; i < fw_hd->num_chunk; i++) { fw_ch = (fw_chunk *)fd; - input_err(true, &ts->client->dev, + input_info(true, &ts->client->dev, "%s: [%d] 0x%08X, 0x%08X, 0x%08X, 0x%08X\n", __func__, i, fw_ch->signature, fw_ch->addr, fw_ch->size, fw_ch->reserved); @@ -861,7 +852,7 @@ static int sec_ts_firmware_update(struct sec_ts_data *ts, const u8 *data, fd += fw_ch->size; } - sec_ts_sw_reset(ts); + sec_ts_system_reset(ts, RESET_MODE_SW, true, true); #ifdef PAT_CONTROL if (restore_cal) { @@ -1182,7 +1173,7 @@ int sec_ts_firmware_update_on_probe(struct sec_ts_data *ts, bool force_update) ts->cal_status = sec_ts_read_calibration_report(ts); input_info(true, &ts->client->dev, - "%s: initial firmware update %s, cal:%X\n", + "%s: initial firmware update %s, cal: %X\n", __func__, fw_path, ts->cal_status); /* Loading Firmware */ @@ -1378,7 +1369,7 @@ static int sec_ts_load_fw_from_ffu(struct sec_ts_data *ts) disable_irq(ts->client->irq); input_info(true, ts->dev, - "%s: Load firmware : %s\n", __func__, fw_path); + "%s: Load firmware: %s\n", __func__, fw_path); /* Loading Firmware */ if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { diff --git a/sec_ts_only_vendor.c b/sec_ts_only_vendor.c index d4a740b..205e801 100644 --- a/sec_ts_only_vendor.c +++ b/sec_ts_only_vendor.c @@ -415,7 +415,7 @@ static ssize_t sec_ts_enter_recovery_store(struct device *dev, ret = kstrtoul(buf, 10, &on); if (ret != 0) { - input_err(true, &ts->client->dev, "%s: failed to read:%d\n", + input_err(true, &ts->client->dev, "%s: failed to read: %d\n", __func__, ret); return -EINVAL; } |