| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Google Touch Interface for Pixel devices. |
| * |
| * Copyright 2022 Google LLC. |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/module.h> |
| #include <linux/input/mt.h> |
| #include <linux/of.h> |
| #include <linux/power_supply.h> |
| #include <linux/proc_fs.h> |
| #include <linux/seq_file.h> |
| #include <trace/hooks/systrace.h> |
| #if IS_ENABLED(CONFIG_QCOM_QBT_HANDLER) |
| #include <qbt_handler.h> |
| #endif |
| |
| #if IS_ENABLED(CONFIG_GS_DRM_PANEL_UNIFIED) |
| #include <gs_drm/gs_drm_connector.h> |
| #endif |
| #include <samsung/exynos_drm_connector.h> |
| #include <samsung/panel/panel-samsung-drv.h> |
| |
| #include "goog_touch_interface.h" |
| #include "touch_bus_negotiator.h" |
| |
| static struct class *gti_class; |
| static u8 gti_dev_num; |
| |
| /*----------------------------------------------------------------------------- |
| * GTI/common: forward declarations, structures and functions. |
| */ |
| static void goog_input_flush_offload_fingers(struct goog_touch_interface *gti); |
| static void goog_offload_set_running(struct goog_touch_interface *gti, bool running); |
| static void goog_lookup_touch_report_rate(struct goog_touch_interface *gti); |
| static int goog_precheck_heatmap(struct goog_touch_interface *gti); |
| static void goog_set_display_state(struct goog_touch_interface *gti, |
| enum gti_display_state_setting display_state); |
| static int goog_do_selftest(struct goog_touch_interface *gti); |
| #if IS_ENABLED(CONFIG_QCOM_QBT_HANDLER) |
| void goog_notify_lptw_triggered(struct TbnLptwEvent* lptw, void* data); |
| void goog_notify_lptw_left(void* data); |
| void goog_track_lptw_slot(struct goog_touch_interface *gti, u16 x, u16 y, int slot_bit); |
| #endif |
| static void gti_input_set_timestamp(struct goog_touch_interface *gti, ktime_t timestamp); |
| |
| /*----------------------------------------------------------------------------- |
| * GTI/proc: forward declarations, structures and functions. |
| */ |
| static int goog_proc_dump_show(struct seq_file *m, void *v); |
| static int goog_proc_ms_base_show(struct seq_file *m, void *v); |
| static int goog_proc_ms_diff_show(struct seq_file *m, void *v); |
| static int goog_proc_ms_raw_show(struct seq_file *m, void *v); |
| static int goog_proc_ss_base_show(struct seq_file *m, void *v); |
| static int goog_proc_ss_diff_show(struct seq_file *m, void *v); |
| static int goog_proc_ss_raw_show(struct seq_file *m, void *v); |
| static struct proc_dir_entry *gti_proc_dir_root; |
| static char *gti_proc_name[GTI_PROC_NUM] = { |
| [GTI_PROC_DUMP] = "dump", |
| [GTI_PROC_MS_BASE] = "ms_base", |
| [GTI_PROC_MS_DIFF] = "ms_diff", |
| [GTI_PROC_MS_RAW] = "ms_raw", |
| [GTI_PROC_SS_BASE] = "ss_base", |
| [GTI_PROC_SS_DIFF] = "ss_diff", |
| [GTI_PROC_SS_RAW] = "ss_raw", |
| }; |
| static int (*gti_proc_show[GTI_PROC_NUM]) (struct seq_file *, void *) = { |
| [GTI_PROC_DUMP] = goog_proc_dump_show, |
| [GTI_PROC_MS_BASE] = goog_proc_ms_base_show, |
| [GTI_PROC_MS_DIFF] = goog_proc_ms_diff_show, |
| [GTI_PROC_MS_RAW] = goog_proc_ms_raw_show, |
| [GTI_PROC_SS_BASE] = goog_proc_ss_base_show, |
| [GTI_PROC_SS_DIFF] = goog_proc_ss_diff_show, |
| [GTI_PROC_SS_RAW] = goog_proc_ss_raw_show, |
| }; |
| DEFINE_PROC_SHOW_ATTRIBUTE(goog_proc_dump); |
| DEFINE_PROC_SHOW_ATTRIBUTE(goog_proc_ms_base); |
| DEFINE_PROC_SHOW_ATTRIBUTE(goog_proc_ms_diff); |
| DEFINE_PROC_SHOW_ATTRIBUTE(goog_proc_ms_raw); |
| DEFINE_PROC_SHOW_ATTRIBUTE(goog_proc_ss_base); |
| DEFINE_PROC_SHOW_ATTRIBUTE(goog_proc_ss_diff); |
| DEFINE_PROC_SHOW_ATTRIBUTE(goog_proc_ss_raw); |
| |
| static void goog_proc_heatmap_show(struct seq_file *m, void *v) |
| { |
| struct goog_touch_interface *gti = m->private; |
| struct gti_sensor_data_cmd *cmd = >i->cmd.manual_sensor_data_cmd; |
| u16 tx = gti->offload.caps.tx_size; |
| u16 rx = gti->offload.caps.rx_size; |
| int x, y; |
| |
| if (cmd->size == 0 || cmd->buffer == NULL) { |
| seq_puts(m, "result: N/A!\n"); |
| GOOG_LOGW(gti, "result: N/A!\n"); |
| return; |
| } |
| |
| switch (cmd->type) { |
| case GTI_SENSOR_DATA_TYPE_MS_BASELINE: |
| case GTI_SENSOR_DATA_TYPE_MS_DIFF: |
| case GTI_SENSOR_DATA_TYPE_MS_RAW: |
| if (cmd->size == TOUCH_OFFLOAD_DATA_SIZE_2D(rx, tx)) { |
| seq_puts(m, "result:\n"); |
| if (cmd->is_unsigned) { |
| for (y = 0; y < rx; y++) { |
| for (x = 0; x < tx; x++) { |
| seq_printf(m, "%5u,", |
| ((u16 *)cmd->buffer)[y * tx + x]); |
| } |
| seq_puts(m, "\n"); |
| } |
| } else { |
| for (y = 0; y < rx; y++) { |
| for (x = 0; x < tx; x++) { |
| seq_printf(m, "%5d,", |
| ((s16 *)cmd->buffer)[y * tx + x]); |
| } |
| seq_puts(m, "\n"); |
| } |
| } |
| } else { |
| seq_printf(m, "error: invalid buffer %p or size %d!\n", |
| cmd->buffer, cmd->size); |
| GOOG_LOGW(gti, "error: invalid buffer %p or size %d!\n", |
| cmd->buffer, cmd->size); |
| } |
| break; |
| |
| case GTI_SENSOR_DATA_TYPE_SS_BASELINE: |
| case GTI_SENSOR_DATA_TYPE_SS_DIFF: |
| case GTI_SENSOR_DATA_TYPE_SS_RAW: |
| if (cmd->size == TOUCH_OFFLOAD_DATA_SIZE_1D(rx, tx)) { |
| seq_puts(m, "result:\n"); |
| seq_puts(m, "TX:"); |
| if (cmd->is_unsigned){ |
| for (x = 0; x < tx; x++) |
| seq_printf(m, "%5u,", ((u16 *)cmd->buffer)[x]); |
| seq_puts(m, "\nRX:"); |
| for (y = 0; y < rx; y++) |
| seq_printf(m, "%5u,", ((u16 *)cmd->buffer)[tx + y]); |
| } else { |
| for (x = 0; x < tx; x++) |
| seq_printf(m, "%5d,", ((s16 *)cmd->buffer)[x]); |
| seq_puts(m, "\nRX:"); |
| for (y = 0; y < rx; y++) |
| seq_printf(m, "%5d,", ((s16 *)cmd->buffer)[tx + y]); |
| } |
| seq_puts(m, "\n"); |
| } else { |
| seq_printf(m, "error: invalid buffer %p or size %d!\n", |
| cmd->buffer, cmd->size); |
| GOOG_LOGW(gti, "error: invalid buffer %p or size %d!\n", |
| cmd->buffer, cmd->size); |
| } |
| break; |
| |
| default: |
| seq_printf(m, "error: invalid type %#x!\n", cmd->type); |
| GOOG_LOGE(gti, "error: invalid type %#x!\n", cmd->type); |
| break; |
| } |
| } |
| |
| static int goog_proc_heatmap_process(struct seq_file *m, void *v, enum gti_sensor_data_type type) |
| { |
| struct goog_touch_interface *gti = m->private; |
| struct gti_sensor_data_cmd *cmd = >i->cmd.manual_sensor_data_cmd; |
| int ret = 0; |
| |
| ret = goog_precheck_heatmap(gti); |
| if (ret) { |
| seq_puts(m, "N/A!\n"); |
| goto heatmap_process_err; |
| } |
| |
| switch (type) { |
| case GTI_SENSOR_DATA_TYPE_MS_BASELINE: |
| case GTI_SENSOR_DATA_TYPE_MS_DIFF: |
| case GTI_SENSOR_DATA_TYPE_MS_RAW: |
| case GTI_SENSOR_DATA_TYPE_SS_BASELINE: |
| case GTI_SENSOR_DATA_TYPE_SS_DIFF: |
| case GTI_SENSOR_DATA_TYPE_SS_RAW: |
| cmd->type = type; |
| break; |
| |
| default: |
| seq_printf(m, "error: invalid type %#x!\n", type); |
| GOOG_LOGE(gti, "error: invalid type %#x!\n", type); |
| ret = -EINVAL; |
| break; |
| } |
| |
| if (ret) |
| goto heatmap_process_err; |
| |
| cmd->buffer = NULL; |
| cmd->size = 0; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_GET_SENSOR_DATA_MANUAL); |
| if (ret) { |
| seq_printf(m, "error: %d!\n", ret); |
| GOOG_LOGE(gti, "error: %d!\n", ret); |
| } else { |
| GOOG_LOGI(gti, "type %#x.\n", type); |
| } |
| |
| heatmap_process_err: |
| if (ret) { |
| cmd->buffer = NULL; |
| cmd->size = 0; |
| } |
| return ret; |
| } |
| |
| static int goog_proc_dump_show(struct seq_file *m, void *v) |
| { |
| char trace_tag[128]; |
| u64 i, hc_cnt, input_cnt; |
| int ret, slot; |
| ktime_t delta_time; |
| time64_t time64_utc; |
| s32 remainder; |
| struct tm utc; |
| struct goog_touch_interface *gti = m->private; |
| struct gti_debug_healthcheck *hc_history = gti->debug_healthcheck_history; |
| struct gti_debug_input *input_history = gti->debug_input_history; |
| |
| hc_cnt = min_t(u64, gti->irq_index, GTI_DEBUG_HEALTHCHECK_KFIFO_LEN); |
| input_cnt = min_t(u64, gti->released_index, GTI_DEBUG_INPUT_KFIFO_LEN); |
| |
| ret = mutex_lock_interruptible(>i->input_process_lock); |
| if (ret) { |
| seq_puts(m, "error: has been interrupted!\n"); |
| GOOG_LOGW(gti, "error: has been interrupted!\n"); |
| return ret; |
| } |
| |
| scnprintf(trace_tag, sizeof(trace_tag), "%s\n", __func__); |
| ATRACE_BEGIN(trace_tag); |
| gti_debug_healthcheck_dump(gti); |
| gti_debug_input_dump(gti); |
| |
| seq_puts(m, "\t### Interrupt ###\n"); |
| seq_printf(m, "%23s %8s %8s %12s\n", "TIME(UTC)", "INT#", "INPUT#", "SLOT-STATE"); |
| for (i = 0 ; i < hc_cnt ; i++) { |
| if (hc_history[i].irq_index == 0) |
| continue; |
| |
| time64_utc = div_s64_rem(ktime_to_ns(hc_history[i].irq_time), |
| NSEC_PER_SEC, &remainder); |
| time64_to_tm(time64_utc, 0, &utc); |
| seq_printf(m, "%4ld-%02d-%02d %02d:%02d:%02d.%03ld %8llu %8llu %#12lx\n", |
| utc.tm_year + 1900, utc.tm_mon + 1, utc.tm_mday, |
| utc.tm_hour, utc.tm_min, utc.tm_sec, remainder/NSEC_PER_MSEC, |
| hc_history[i].irq_index, |
| hc_history[i].input_index, hc_history[i].slot_bit_active); |
| } |
| seq_puts(m, "\n"); |
| |
| seq_puts(m, "\t### Coordinate(s) ###\n"); |
| seq_printf(m, "%23s %14s %8s %12s %12s %12s %12s\n", |
| "TIME(UTC)", "DURATION(MS)", "SLOT#", "INT#DOWN", "INT#UP", |
| "X-DELTA(PX)", "Y-DELTA(PX)"); |
| for (i = 0 ; i < input_cnt ; i++) { |
| delta_time = ktime_sub(input_history[i].released.time, |
| input_history[i].pressed.time); |
| if (delta_time <= 0) |
| continue; |
| |
| time64_utc = div_s64_rem(ktime_to_ns(input_history[i].pressed.time), |
| NSEC_PER_SEC, &remainder); |
| time64_to_tm(time64_utc, 0, &utc); |
| seq_printf(m, "%4ld-%02d-%02d %02d:%02d:%02d.%03ld %14lld %8d %12lld %12lld %12d %12d\n", |
| utc.tm_year + 1900, utc.tm_mon + 1, utc.tm_mday, |
| utc.tm_hour, utc.tm_min, utc.tm_sec, remainder/NSEC_PER_MSEC, |
| ktime_to_ms(delta_time), |
| input_history[i].slot, |
| input_history[i].pressed.irq_index, input_history[i].released.irq_index, |
| input_history[i].released.coord.x - input_history[i].pressed.coord.x, |
| input_history[i].released.coord.y - input_history[i].pressed.coord.y); |
| } |
| seq_puts(m, "\n"); |
| |
| seq_puts(m, "\t### Unreleased Coordinate(s) ###\n"); |
| seq_printf(m, "%8s %12s %12s %12s %12s %12s\n", "SLOT#", "X", "Y", |
| "PRESSURE", "MAJOR", "MINOR"); |
| for_each_set_bit(slot, >i->slot_bit_active, MAX_SLOTS) { |
| seq_printf(m, "%8d %12u %12u %12u %12u %12u\n", slot, |
| gti->offload.coords[slot].x, gti->offload.coords[slot].y, |
| gti->offload.coords[slot].pressure, |
| gti->offload.coords[slot].major, gti->offload.coords[slot].minor); |
| } |
| seq_puts(m, "\n"); |
| |
| mutex_unlock(>i->input_process_lock); |
| seq_puts(m, "\n\n"); |
| ATRACE_END(); |
| |
| return ret; |
| } |
| |
| static int goog_proc_ms_base_show(struct seq_file *m, void *v) |
| { |
| struct goog_touch_interface *gti = m->private; |
| int ret; |
| |
| if (!gti->manual_heatmap_from_irq) { |
| ret = mutex_lock_interruptible(>i->input_heatmap_lock); |
| if (ret) { |
| seq_puts(m, "error: has been interrupted!\n"); |
| GOOG_LOGW(gti, "error: has been interrupted!\n"); |
| return ret; |
| } |
| } |
| |
| ret = goog_proc_heatmap_process(m, v, GTI_SENSOR_DATA_TYPE_MS_BASELINE); |
| if (!ret) |
| goog_proc_heatmap_show(m, v); |
| |
| if (!gti->manual_heatmap_from_irq) |
| mutex_unlock(>i->input_heatmap_lock); |
| |
| return ret; |
| } |
| |
| static int goog_proc_ms_diff_show(struct seq_file *m, void *v) |
| { |
| struct goog_touch_interface *gti = m->private; |
| int ret; |
| |
| if (!gti->manual_heatmap_from_irq) { |
| ret = mutex_lock_interruptible(>i->input_heatmap_lock); |
| if (ret) { |
| seq_puts(m, "error: has been interrupted!\n"); |
| GOOG_LOGW(gti, "error: has been interrupted!\n"); |
| return ret; |
| } |
| } |
| |
| ret = goog_proc_heatmap_process(m, v, GTI_SENSOR_DATA_TYPE_MS_DIFF); |
| if (!ret) |
| goog_proc_heatmap_show(m, v); |
| |
| if (!gti->manual_heatmap_from_irq) |
| mutex_unlock(>i->input_heatmap_lock); |
| |
| return ret; |
| } |
| |
| static int goog_proc_ms_raw_show(struct seq_file *m, void *v) |
| { |
| struct goog_touch_interface *gti = m->private; |
| int ret; |
| |
| if (!gti->manual_heatmap_from_irq) { |
| ret = mutex_lock_interruptible(>i->input_heatmap_lock); |
| if (ret) { |
| seq_puts(m, "error: has been interrupted!\n"); |
| GOOG_LOGW(gti, "error: has been interrupted!\n"); |
| return ret; |
| } |
| } |
| |
| ret = goog_proc_heatmap_process(m, v, GTI_SENSOR_DATA_TYPE_MS_RAW); |
| if (!ret) |
| goog_proc_heatmap_show(m, v); |
| |
| if (!gti->manual_heatmap_from_irq) |
| mutex_unlock(>i->input_heatmap_lock); |
| |
| return ret; |
| } |
| |
| static int goog_proc_ss_base_show(struct seq_file *m, void *v) |
| { |
| struct goog_touch_interface *gti = m->private; |
| int ret; |
| |
| if (!gti->manual_heatmap_from_irq) { |
| ret = mutex_lock_interruptible(>i->input_heatmap_lock); |
| if (ret) { |
| seq_puts(m, "error: has been interrupted!\n"); |
| GOOG_LOGW(gti, "error: has been interrupted!\n"); |
| return ret; |
| } |
| } |
| |
| ret = goog_proc_heatmap_process(m, v, GTI_SENSOR_DATA_TYPE_SS_BASELINE); |
| if (!ret) |
| goog_proc_heatmap_show(m, v); |
| |
| if (!gti->manual_heatmap_from_irq) |
| mutex_unlock(>i->input_heatmap_lock); |
| |
| return ret; |
| } |
| |
| static int goog_proc_ss_diff_show(struct seq_file *m, void *v) |
| { |
| struct goog_touch_interface *gti = m->private; |
| int ret; |
| |
| if (!gti->manual_heatmap_from_irq) { |
| ret = mutex_lock_interruptible(>i->input_heatmap_lock); |
| if (ret) { |
| seq_puts(m, "error: has been interrupted!\n"); |
| GOOG_LOGW(gti, "error: has been interrupted!\n"); |
| return ret; |
| } |
| } |
| |
| ret = goog_proc_heatmap_process(m, v, GTI_SENSOR_DATA_TYPE_SS_DIFF); |
| if (!ret) |
| goog_proc_heatmap_show(m, v); |
| |
| if (!gti->manual_heatmap_from_irq) |
| mutex_unlock(>i->input_heatmap_lock); |
| |
| return ret; |
| } |
| |
| static int goog_proc_ss_raw_show(struct seq_file *m, void *v) |
| { |
| struct goog_touch_interface *gti = m->private; |
| int ret; |
| |
| if (!gti->manual_heatmap_from_irq) { |
| ret = mutex_lock_interruptible(>i->input_heatmap_lock); |
| if (ret) { |
| seq_puts(m, "error: has been interrupted!\n"); |
| GOOG_LOGW(gti, "error: has been interrupted!\n"); |
| return ret; |
| } |
| } |
| |
| ret = goog_proc_heatmap_process(m, v, GTI_SENSOR_DATA_TYPE_SS_RAW); |
| if (!ret) |
| goog_proc_heatmap_show(m, v); |
| |
| if (!gti->manual_heatmap_from_irq) |
| mutex_unlock(>i->input_heatmap_lock); |
| |
| return ret; |
| } |
| |
| static void goog_init_proc(struct goog_touch_interface *gti) |
| { |
| int type; |
| |
| if (!gti_proc_dir_root) { |
| gti_proc_dir_root = proc_mkdir(GTI_NAME, NULL); |
| if (!gti_proc_dir_root) { |
| pr_err("%s: proc_mkdir failed for %s!\n", __func__, GTI_NAME); |
| return; |
| } |
| } |
| |
| gti->proc_dir = proc_mkdir_data(dev_name(gti->dev), 0555, gti_proc_dir_root, gti); |
| if (!gti->proc_dir) { |
| GOOG_ERR(gti, "proc_mkdir_data failed!\n"); |
| return; |
| } |
| |
| for (type = GTI_PROC_DUMP; type < GTI_PROC_NUM; type++) { |
| char *name = gti_proc_name[type]; |
| |
| if (gti_proc_show[type]) |
| gti->proc_show[type] = proc_create_single_data( |
| name, 0555, gti->proc_dir, gti_proc_show[type], gti); |
| if (!gti->proc_show[type]) |
| GOOG_ERR(gti, "proc_create_single_data failed for %s!\n", name); |
| } |
| } |
| |
| /*----------------------------------------------------------------------------- |
| * GTI/sysfs: forward declarations, structures and functions. |
| */ |
| static ssize_t config_name_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t force_active_show( |
| struct device *dev, struct device_attribute *attr, char *buf); |
| static ssize_t force_active_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t fw_coord_filter_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t fw_coord_filter_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t fw_grip_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t fw_grip_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t fw_name_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t fw_palm_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t fw_palm_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t fw_ver_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t gesture_config_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t gesture_config_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t irq_enabled_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t irq_enabled_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t mf_mode_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t mf_mode_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t offload_enabled_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t offload_enabled_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t offload_id_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t panel_id_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t ping_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t reset_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t reset_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t scan_mode_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t scan_mode_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t screen_protector_mode_enabled_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t screen_protector_mode_enabled_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t self_test_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t sensing_enabled_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t sensing_enabled_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t test_limits_name_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t v4l2_enabled_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t v4l2_enabled_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t vrr_enabled_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t vrr_enabled_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| static ssize_t interactive_calibrate_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t interactive_calibrate_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size); |
| |
| static DEVICE_ATTR_RO(config_name); |
| static DEVICE_ATTR_RW(force_active); |
| static DEVICE_ATTR_RW(fw_coord_filter); |
| static DEVICE_ATTR_RW(fw_grip); |
| static DEVICE_ATTR_RO(fw_name); |
| static DEVICE_ATTR_RW(fw_palm); |
| static DEVICE_ATTR_RO(fw_ver); |
| static DEVICE_ATTR_RW(gesture_config); |
| static DEVICE_ATTR_RW(irq_enabled); |
| static DEVICE_ATTR_RW(mf_mode); |
| static DEVICE_ATTR_RW(offload_enabled); |
| static DEVICE_ATTR_ADMIN_RO(offload_id); |
| static DEVICE_ATTR_ADMIN_RO(panel_id); |
| static DEVICE_ATTR_RO(ping); |
| static DEVICE_ATTR_RW(reset); |
| static DEVICE_ATTR_RW(scan_mode); |
| static DEVICE_ATTR_RW(screen_protector_mode_enabled); |
| static DEVICE_ATTR_RO(self_test); |
| static DEVICE_ATTR_RW(sensing_enabled); |
| static DEVICE_ATTR_RO(test_limits_name); |
| static DEVICE_ATTR_RW(v4l2_enabled); |
| static DEVICE_ATTR_RW(vrr_enabled); |
| static DEVICE_ATTR_RW(interactive_calibrate); |
| |
| static struct attribute *goog_attributes[] = { |
| &dev_attr_config_name.attr, |
| &dev_attr_force_active.attr, |
| &dev_attr_fw_coord_filter.attr, |
| &dev_attr_fw_grip.attr, |
| &dev_attr_fw_name.attr, |
| &dev_attr_fw_palm.attr, |
| &dev_attr_fw_ver.attr, |
| &dev_attr_gesture_config.attr, |
| &dev_attr_irq_enabled.attr, |
| &dev_attr_mf_mode.attr, |
| &dev_attr_offload_enabled.attr, |
| &dev_attr_offload_id.attr, |
| &dev_attr_panel_id.attr, |
| &dev_attr_ping.attr, |
| &dev_attr_reset.attr, |
| &dev_attr_scan_mode.attr, |
| &dev_attr_screen_protector_mode_enabled.attr, |
| &dev_attr_self_test.attr, |
| &dev_attr_sensing_enabled.attr, |
| &dev_attr_test_limits_name.attr, |
| &dev_attr_v4l2_enabled.attr, |
| &dev_attr_vrr_enabled.attr, |
| &dev_attr_interactive_calibrate.attr, |
| NULL, |
| }; |
| |
| static struct attribute_group goog_attr_group = { |
| .attrs = goog_attributes, |
| }; |
| |
| static ssize_t config_name_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| if (gti->config_name[0] == '\0') { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: not supported!\n"); |
| } else { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %s\n", gti->config_name); |
| } |
| GOOG_INFO(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t force_active_show( |
| struct device *dev, struct device_attribute *attr, char *buf) |
| { |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| ssize_t buf_idx = 0; |
| bool locked = false; |
| |
| if (gti->ignore_force_active) { |
| GOOG_LOGW(gti, "operation not supported!\n"); |
| return -EOPNOTSUPP; |
| } |
| |
| locked = goog_pm_wake_check_locked(gti, GTI_PM_WAKELOCK_TYPE_FORCE_ACTIVE); |
| buf_idx += scnprintf(buf, PAGE_SIZE - buf_idx, "result: %s\n", |
| locked ? "locked" : "unlocked"); |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t force_active_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| u32 locked = 0; |
| int ret = 0; |
| |
| if (buf == NULL || size < 0) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return -EINVAL; |
| } |
| |
| if (kstrtou32(buf, 10, &locked)) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return -EINVAL; |
| } |
| |
| if (locked > 1) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return -EINVAL; |
| } |
| |
| if (locked) { |
| if (gti->ignore_force_active) |
| GOOG_LOGW(gti, "operation not supported!\n"); |
| else |
| ret = goog_pm_wake_lock(gti, GTI_PM_WAKELOCK_TYPE_FORCE_ACTIVE, false); |
| } else { |
| if (gti->ignore_force_active) |
| GOOG_LOGW(gti, "operation not supported!\n"); |
| else |
| ret = goog_pm_wake_unlock(gti, GTI_PM_WAKELOCK_TYPE_FORCE_ACTIVE); |
| } |
| |
| if (ret < 0) { |
| GOOG_LOGE(gti, "error: %d!\n", ret); |
| return ret; |
| } |
| return size; |
| } |
| |
| static ssize_t fw_coord_filter_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int ret = 0; |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| struct gti_coord_filter_cmd *cmd = >i->cmd.coord_filter_cmd; |
| |
| if (!gti->coord_filter_enabled) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: not supported!\n"); |
| GOOG_LOGE(gti, "%s", buf); |
| return buf_idx; |
| } |
| |
| cmd->setting = GTI_COORD_FILTER_DISABLE; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_GET_COORD_FILTER_ENABLED); |
| if (ret == -EOPNOTSUPP) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: not supported!\n"); |
| } else if (ret) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: %d!\n", ret); |
| } else { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %u\n", cmd->setting | (gti->ignore_coord_filter_update << 1)); |
| } |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t fw_coord_filter_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int ret = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| int fw_coord_filter; |
| |
| if (kstrtou32(buf, 10, &fw_coord_filter)) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return -EINVAL; |
| } |
| |
| if (!gti->coord_filter_enabled) { |
| GOOG_LOGE(gti, "error: not supported!\n"); |
| return -EOPNOTSUPP; |
| } |
| |
| gti->fw_coord_filter_enabled = fw_coord_filter & 0x01; |
| gti->ignore_coord_filter_update = (fw_coord_filter >> 1) & 0x01; |
| gti->cmd.coord_filter_cmd.setting = gti->fw_coord_filter_enabled ? |
| GTI_COORD_FILTER_ENABLE : GTI_COORD_FILTER_DISABLE; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_COORD_FILTER_ENABLED); |
| if (ret == -EOPNOTSUPP) |
| GOOG_LOGE(gti, "error: not supported!\n"); |
| else if (ret) |
| GOOG_LOGE(gti, "error: %d!\n", ret); |
| else |
| GOOG_LOGI(gti, "fw_coord_filter= %u\n", fw_coord_filter); |
| |
| return size; |
| } |
| |
| static ssize_t fw_grip_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int ret = 0; |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| struct gti_grip_cmd *cmd = >i->cmd.grip_cmd; |
| |
| cmd->setting = GTI_GRIP_DISABLE; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_GET_GRIP_MODE); |
| if (ret == -EOPNOTSUPP) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: not supported!\n"); |
| } else if (ret) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: %d!\n", ret); |
| } else { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %u\n", cmd->setting | (gti->ignore_grip_update << 1)); |
| } |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t fw_grip_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int ret = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| int fw_grip_mode = 0; |
| bool enabled = false; |
| |
| if (kstrtou32(buf, 10, &fw_grip_mode)) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return size; |
| } |
| |
| enabled = fw_grip_mode & 0x01; |
| gti->ignore_grip_update = (fw_grip_mode >> 1) & 0x01; |
| gti->cmd.grip_cmd.setting = enabled ? GTI_GRIP_ENABLE : GTI_GRIP_DISABLE; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_GRIP_MODE); |
| if (ret == -EOPNOTSUPP) |
| GOOG_LOGE(gti, "error: not supported!\n"); |
| else if (ret) |
| GOOG_LOGE(gti, "error: %d!\n", ret); |
| else |
| GOOG_LOGI(gti, "fw_grip_mode: %u\n", fw_grip_mode); |
| |
| return size; |
| } |
| |
| static ssize_t fw_name_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| if (gti->fw_name[0] == '\0') { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: not supported!\n"); |
| } else { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %s\n", gti->fw_name); |
| } |
| GOOG_INFO(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t fw_palm_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int ret = 0; |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| struct gti_palm_cmd *cmd = >i->cmd.palm_cmd; |
| |
| cmd->setting = GTI_PALM_DISABLE; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_GET_PALM_MODE); |
| if (ret == -EOPNOTSUPP) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: not supported!\n"); |
| } else if (ret) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: %d!\n", ret); |
| } else { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %u\n", cmd->setting | (gti->ignore_palm_update << 1)); |
| } |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t fw_palm_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int ret = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| int fw_palm_mode; |
| bool enabled; |
| |
| if (kstrtou32(buf, 10, &fw_palm_mode)) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return -EINVAL; |
| } |
| |
| enabled = fw_palm_mode & 0x01; |
| gti->ignore_palm_update = (fw_palm_mode >> 1) & 0x01; |
| gti->cmd.palm_cmd.setting = enabled ? GTI_PALM_ENABLE : GTI_PALM_DISABLE; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_PALM_MODE); |
| if (ret == -EOPNOTSUPP) |
| GOOG_LOGE(gti, "error: not supported!\n"); |
| else if (ret) |
| GOOG_LOGE(gti, "error: %d!\n", ret); |
| else |
| GOOG_LOGI(gti, "fw_palm_mode= %u\n", fw_palm_mode); |
| |
| return size; |
| } |
| |
| static ssize_t fw_ver_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int ret; |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| memset(gti->cmd.fw_version_cmd.buffer, 0, sizeof(gti->cmd.fw_version_cmd.buffer)); |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_GET_FW_VERSION); |
| if (ret == -EOPNOTSUPP) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: not supported!\n"); |
| } else if (ret) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: %d!\n", ret); |
| } else { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %s\n", gti->cmd.fw_version_cmd.buffer); |
| } |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t gesture_config_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ssize_t buf_idx = 0; |
| int i = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| for (i = 0; i < GTI_GESTURE_PARAMS_MAX; i++) { |
| buf_idx += sysfs_emit_at(buf, buf_idx, "%s %u\n", |
| gesture_params_list[i], |
| gti->cmd.gesture_config_cmd.params[i]); |
| } |
| |
| return buf_idx; |
| } |
| static ssize_t gesture_config_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| char *p, *temp_buf, *token; |
| u16 config = 0; |
| int retval = 0; |
| int i = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| temp_buf = kstrdup(buf, GFP_KERNEL); |
| if (!temp_buf) |
| return -ENOMEM; |
| |
| goog_pm_wake_lock(gti, GTI_PM_WAKELOCK_TYPE_SYSFS, false); |
| |
| p = temp_buf; |
| |
| token = strsep(&p, " "); |
| |
| if (!token || *token == '\0' || !p) { |
| retval = -EINVAL; |
| goto exit; |
| } |
| |
| if (kstrtou16(p, 10, &config)) { |
| retval = -EINVAL; |
| goto exit; |
| } |
| |
| memset(gti->cmd.gesture_config_cmd.updating_params, 0, GTI_GESTURE_PARAMS_MAX); |
| |
| /* Set gesture parameters */ |
| for (i = 0; i < GTI_GESTURE_PARAMS_MAX; i++) { |
| if (!strncmp(token, gesture_params_list[i], strlen(gesture_params_list[i]))) { |
| gti->cmd.gesture_config_cmd.params[i] = config; |
| gti->cmd.gesture_config_cmd.updating_params[i] = 1; |
| retval = goog_process_vendor_cmd(gti, GTI_CMD_SET_GESTURE_CONFIG); |
| if (retval) { |
| GOOG_ERR(gti, "Fail to set param %s, ret = %d!\n", |
| gesture_params_list[i], retval); |
| retval = -EBADRQC; |
| goto exit; |
| } |
| gti->gesture_config_enabled = true; |
| retval = size; |
| } |
| } |
| |
| if (retval == 0) |
| retval = -EINVAL; |
| |
| exit: |
| goog_pm_wake_unlock(gti, GTI_PM_WAKELOCK_TYPE_SYSFS); |
| kfree(temp_buf); |
| return retval; |
| } |
| |
| static ssize_t irq_enabled_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int ret; |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| gti->cmd.irq_cmd.setting = GTI_IRQ_MODE_NA; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_GET_IRQ_MODE); |
| if (ret == -EOPNOTSUPP) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: not supported!\n"); |
| } else if (ret) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: %d!\n", ret); |
| } else { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %u\n", gti->cmd.irq_cmd.setting); |
| } |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t irq_enabled_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int ret; |
| bool enabled; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| if (kstrtobool(buf, &enabled)) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return size; |
| } |
| |
| gti->cmd.irq_cmd.setting = enabled; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_IRQ_MODE); |
| if (ret == -EOPNOTSUPP) |
| GOOG_LOGE(gti, "error: not supported!\n"); |
| else if (ret) |
| GOOG_LOGE(gti, "error: %d!\n", ret); |
| else |
| GOOG_LOGI(gti, "irq_enabled= %u\n", gti->cmd.irq_cmd.setting); |
| |
| return size; |
| } |
| |
| static ssize_t mf_mode_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %u\n", gti->mf_mode); |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t mf_mode_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| enum gti_mf_mode mode = 0; |
| |
| if (buf == NULL || size < 0) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return size; |
| } |
| |
| if (kstrtou32(buf, 10, &mode)) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return size; |
| } |
| |
| if (mode < GTI_MF_MODE_UNFILTER || |
| mode > GTI_MF_MODE_AUTO_REPORT) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return size; |
| } |
| |
| gti->mf_mode = mode; |
| GOOG_LOGI(gti, "mf_mode= %u\n", gti->mf_mode); |
| |
| return size; |
| } |
| |
| static ssize_t offload_enabled_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %d\n", gti->offload_enabled); |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t offload_enabled_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| if (kstrtobool(buf, >i->offload_enabled)) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| } else { |
| GOOG_LOGI(gti, "offload_enabled= %d\n", gti->offload_enabled); |
| /* Force to turn off offload by request. */ |
| if (!gti->offload_enabled) |
| goog_offload_set_running(gti, false); |
| } |
| |
| return size; |
| } |
| |
| static ssize_t offload_id_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, "result: %c%c%c%c\n", |
| gti->offload_id_byte[0], gti->offload_id_byte[1], gti->offload_id_byte[2], |
| gti->offload_id_byte[3]); |
| GOOG_INFO(gti, "%s", buf); |
| return buf_idx; |
| } |
| |
| static ssize_t panel_id_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| if (gti->panel_id < 0) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: not supported!\n"); |
| } else { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %d\n", gti->panel_id); |
| } |
| GOOG_INFO(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t ping_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int ret; |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| gti->cmd.ping_cmd.setting = GTI_PING_ENABLE; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_PING); |
| if (ret == -EOPNOTSUPP) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: not supported!\n"); |
| gti->cmd.ping_cmd.setting = GTI_PING_NA; |
| } else if (ret) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: %d!\n", ret); |
| gti->cmd.ping_cmd.setting = GTI_PING_NA; |
| } else { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: success.\n"); |
| } |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t reset_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| if (gti->cmd.reset_cmd.setting == GTI_RESET_MODE_NOP || |
| gti->cmd.reset_cmd.setting == GTI_RESET_MODE_NA) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: %d!\n", gti->cmd.reset_cmd.setting); |
| } else { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: success.\n"); |
| } |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t reset_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int ret; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| enum gti_reset_mode mode = 0; |
| |
| if (buf == NULL || size < 0) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return -EINVAL; |
| } |
| |
| if (kstrtou32(buf, 10, &mode)) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return -EINVAL; |
| } |
| |
| if (mode <= GTI_RESET_MODE_NOP || |
| mode > GTI_RESET_MODE_AUTO) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return -EINVAL; |
| } |
| |
| gti->cmd.reset_cmd.setting = mode; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_RESET); |
| if (ret == -EOPNOTSUPP) { |
| GOOG_LOGE(gti, "error: not supported!\n"); |
| gti->cmd.reset_cmd.setting = GTI_RESET_MODE_NA; |
| } else if (ret) { |
| GOOG_LOGE(gti, "error: %d!\n", ret); |
| gti->cmd.reset_cmd.setting = GTI_RESET_MODE_NA; |
| } else { |
| GOOG_LOGI(gti, "reset= 0x%x\n", mode); |
| } |
| |
| return size; |
| } |
| |
| static ssize_t scan_mode_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int ret; |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| gti->cmd.scan_cmd.setting = GTI_SCAN_MODE_NA; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_GET_SCAN_MODE); |
| if (ret == -EOPNOTSUPP) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: not supported!\n"); |
| } else if (ret) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: %d!\n", ret); |
| } else { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %u\n", gti->cmd.scan_cmd.setting); |
| } |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t scan_mode_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int ret; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| enum gti_scan_mode mode = 0; |
| |
| if (buf == NULL || size < 0) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return size; |
| } |
| |
| if (kstrtou32(buf, 10, &mode)) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return size; |
| } |
| |
| if (mode < GTI_SCAN_MODE_AUTO || |
| mode > GTI_SCAN_MODE_LP_IDLE) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return size; |
| } |
| |
| gti->cmd.scan_cmd.setting = mode; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_SCAN_MODE); |
| if (ret == -EOPNOTSUPP) |
| GOOG_LOGE(gti, "error: not supported!\n"); |
| else if (ret) |
| GOOG_LOGE(gti, "error: %d!\n", ret); |
| else |
| GOOG_LOGI(gti, "scan_mode= %u\n", mode); |
| |
| return size; |
| } |
| |
| static ssize_t screen_protector_mode_enabled_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int ret = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| struct gti_screen_protector_mode_cmd *cmd = >i->cmd.screen_protector_mode_cmd; |
| bool enabled = false; |
| |
| if (kstrtobool(buf, &enabled)) { |
| GOOG_LOGE(gti, "invalid input!\n"); |
| return -EINVAL; |
| } |
| |
| cmd->setting = enabled ? GTI_SCREEN_PROTECTOR_MODE_ENABLE : GTI_SCREEN_PROTECTOR_MODE_DISABLE; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_SCREEN_PROTECTOR_MODE); |
| if (ret == -EOPNOTSUPP) |
| GOOG_LOGE(gti, "error: not supported!\n"); |
| else if (ret) |
| GOOG_LOGE(gti, "error: %d!\n", ret); |
| else |
| GOOG_LOGI(gti, "enabled= %u\n", enabled); |
| gti->screen_protector_mode_setting = enabled ? |
| GTI_SCREEN_PROTECTOR_MODE_ENABLE : GTI_SCREEN_PROTECTOR_MODE_DISABLE; |
| return size; |
| } |
| |
| static ssize_t screen_protector_mode_enabled_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int ret = 0; |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| struct gti_screen_protector_mode_cmd *cmd = >i->cmd.screen_protector_mode_cmd; |
| |
| cmd->setting = GTI_SCREEN_PROTECTOR_MODE_NA; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_GET_SCREEN_PROTECTOR_MODE); |
| if (ret == 0) { |
| buf_idx += scnprintf(buf, PAGE_SIZE - buf_idx, "result: %d\n", |
| cmd->setting == GTI_SCREEN_PROTECTOR_MODE_ENABLE); |
| } else { |
| buf_idx += scnprintf(buf, PAGE_SIZE - buf_idx, "error: %d\n", ret); |
| } |
| GOOG_LOGI(gti, "%s", buf); |
| return buf_idx; |
| } |
| |
| static ssize_t self_test_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int ret; |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| gti->cmd.selftest_cmd.result = GTI_SELFTEST_RESULT_NA; |
| gti->cmd.selftest_cmd.is_ical = false; |
| memset(gti->cmd.selftest_cmd.buffer, 0, sizeof(gti->cmd.selftest_cmd.buffer)); |
| ret = goog_do_selftest(gti); |
| if (ret == -EOPNOTSUPP) { |
| buf_idx += sysfs_emit_at(buf, buf_idx, "error: not supported!\n"); |
| } else if (ret) { |
| buf_idx += sysfs_emit_at(buf, buf_idx, "error: %d!\n", ret); |
| } else { |
| switch (gti->cmd.selftest_cmd.result) { |
| case GTI_SELFTEST_RESULT_PASS: |
| buf_idx += sysfs_emit_at(buf, buf_idx, "result: PASS\n"); |
| buf_idx += sysfs_emit_at(buf, buf_idx, "%s\n", |
| gti->cmd.selftest_cmd.buffer); |
| break; |
| case GTI_SELFTEST_RESULT_FAIL: |
| buf_idx += sysfs_emit_at(buf, buf_idx, "result: FAIL\n"); |
| buf_idx += sysfs_emit_at(buf, buf_idx, "%s\n", |
| gti->cmd.selftest_cmd.buffer); |
| break; |
| case GTI_SELFTEST_RESULT_SHELL_CMDS_REDIRECT: |
| buf_idx += sysfs_emit_at(buf, buf_idx, "redirect: %s\n", |
| gti->cmd.selftest_cmd.buffer); |
| break; |
| default: |
| buf_idx += sysfs_emit_at(buf, buf_idx, "error: N/A!\n"); |
| break; |
| } |
| } |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t sensing_enabled_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int ret; |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| gti->cmd.sensing_cmd.setting = GTI_SENSING_MODE_NA; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_GET_SENSING_MODE); |
| if (ret == -EOPNOTSUPP) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: not supported!\n"); |
| } else if (ret) { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: %d!\n", ret); |
| } else { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %u\n", gti->cmd.sensing_cmd.setting); |
| } |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t sensing_enabled_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int ret; |
| bool enabled; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| if (kstrtobool(buf, &enabled)) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| return size; |
| } |
| |
| gti->cmd.sensing_cmd.setting = enabled; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_SENSING_MODE); |
| if (ret == -EOPNOTSUPP) |
| GOOG_LOGE(gti, "error: not supported!\n"); |
| else if (ret) |
| GOOG_LOGE(gti, "error: %d!\n", ret); |
| else |
| GOOG_LOGI(gti, "sensing_enabled= %u\n", gti->cmd.sensing_cmd.setting); |
| |
| return size; |
| } |
| |
| static ssize_t test_limits_name_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| if (gti->test_limits_name[0] == '\0') { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "error: not supported!\n"); |
| } else { |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %s\n", gti->test_limits_name); |
| } |
| GOOG_INFO(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t v4l2_enabled_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE - buf_idx, |
| "result: %d\n", gti->v4l2_enabled); |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t v4l2_enabled_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| if (kstrtobool(buf, >i->v4l2_enabled)) |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| else |
| GOOG_LOGI(gti, "v4l2_enabled= %d\n", gti->v4l2_enabled); |
| |
| return size; |
| } |
| |
| static ssize_t vrr_enabled_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| buf_idx += scnprintf(buf + buf_idx, PAGE_SIZE, |
| "result: %d\n", gti->vrr_enabled); |
| GOOG_LOGI(gti, "%s", buf); |
| |
| return buf_idx; |
| } |
| |
| static ssize_t vrr_enabled_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| if (kstrtobool(buf, >i->vrr_enabled)) { |
| GOOG_LOGE(gti, "error: invalid input!\n"); |
| } else if (gti->report_rate_table_size == 0) { |
| GOOG_LOGE(gti, "error: No valid report rate table!\n"); |
| } else { |
| GOOG_LOGI(gti, "vrr_enabled= %d\n", gti->vrr_enabled); |
| if (gti->vrr_enabled) |
| goog_lookup_touch_report_rate(gti); |
| } |
| |
| return size; |
| } |
| |
| /* ----------------------------------------------------------------------- |
| * Interactive calibration states |
| * |
| * State "IDLE"/ 0 - idle, no operation underway => return to this state after |
| * an error and after certain timeouts have elapsed |
| * |
| * State "INIT_X" / X01 (101, 201, ...) - operation is beginning. The client |
| * has displayed warnings and will begin transitioning itself to the |
| * "screen off" / "do not touch" state |
| * |
| * State "RUN_X" / X02 (102, 202, ...) - screen is off and nothing is touching |
| * the screen. Operation can begin immediately when this state is entered |
| * |
| * State "END_X" / X03 (103, 203, ...) - the client has waited the designated |
| * time and will assume operation is complete, will read the status of this |
| * state as the final operation status. Transition back to the IDLE state will |
| * occur automatically. |
| */ |
| bool ical_state_idle(struct goog_touch_interface *gti, u32 next_state, |
| u64 elapsed) |
| { |
| /* Valid next-states are 'INIT_X', or 'IDLE' */ |
| if (next_state == ICAL_STATE_IDLE) { |
| gti->ical_result = ICAL_RES_SUCCESS; |
| gti->ical_func_result = ICAL_RES_SUCCESS; |
| /* Do not update the ical timestamp */ |
| return false; |
| } else if ((next_state == ICAL_STATE_INIT_CAL || |
| next_state == ICAL_STATE_INIT_TEST || |
| next_state == ICAL_STATE_INIT_RESET) && |
| elapsed > MIN_DELAY_IDLE) { |
| gti->ical_state = next_state; |
| gti->ical_result = ICAL_RES_SUCCESS; |
| } else { |
| GOOG_ERR(gti, |
| "ical - error: invalid transition or time! %u => %u, min=%lluns, t=%lluns\n", |
| gti->ical_state, next_state, MIN_DELAY_IDLE, |
| elapsed); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_result = ICAL_RES_FAIL; |
| } |
| return true; |
| } |
| |
| void ical_state_init_cal(struct goog_touch_interface *gti, u32 next_state, |
| u64 elapsed) |
| { |
| int pm_ret = 0; |
| u32 ret; |
| |
| pm_ret = goog_pm_wake_lock(gti, GTI_PM_WAKELOCK_TYPE_SYSFS, false); |
| if (pm_ret < 0 && gti->tbn_enabled) { |
| GOOG_ERR(gti, "ical - error: invalid touch bus access!\n"); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_result = ICAL_RES_FAIL_INVALID_BUS_ACCESS; |
| return; |
| } |
| |
| /* only valid next-state is 'RUN_CAL', as long as time elapsed |
| * is within range. When 'RUN_CAL' is received calibration begins. |
| */ |
| if (next_state == ICAL_STATE_RUN_CAL && |
| elapsed > MIN_DELAY_INIT_CAL && elapsed < MAX_DELAY_INIT_CAL) { |
| /* Begin calibration */ |
| |
| gti->cmd.calibrate_cmd.result = GTI_CALIBRATE_RESULT_NA; |
| memset(gti->cmd.calibrate_cmd.buffer, 0, |
| sizeof(gti->cmd.calibrate_cmd.buffer)); |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_CALIBRATE); |
| if (ret == 0) { |
| if (gti->cmd.calibrate_cmd.result == GTI_CALIBRATE_RESULT_DONE) { |
| gti->ical_func_result = gti->cmd.calibrate_cmd.result; |
| GOOG_INFO(gti, |
| "ical - CALIBRATE_RESULT_DONE - [%s]\n", |
| gti->cmd.calibrate_cmd.buffer); |
| } else { |
| gti->ical_func_result = ICAL_RES_FAIL; |
| GOOG_ERR(gti, |
| "ical - calibrate result other/fail - N/A or [%s]\n", |
| gti->cmd.calibrate_cmd.buffer); |
| } |
| |
| gti->ical_state = ICAL_STATE_RUN_CAL; |
| gti->ical_result = ICAL_RES_SUCCESS; |
| } else { |
| GOOG_ERR(gti, "ical - GTI_CMD_CALIBRATE fail(%d)\n", ret); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_result = ICAL_RES_FAIL; |
| } |
| } else { |
| GOOG_ERR(gti, |
| "ical - error: invalid transition or time! %u => %u, min=%lluns, t=%lluns, max=%lluns\n", |
| gti->ical_state, next_state, |
| MIN_DELAY_INIT_CAL, elapsed, |
| MAX_DELAY_INIT_CAL); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_result = ICAL_RES_FAIL; |
| } |
| |
| if (pm_ret == 0) |
| goog_pm_wake_unlock_nosync(gti, GTI_PM_WAKELOCK_TYPE_SYSFS); |
| |
| } |
| |
| void ical_state_run_cal(struct goog_touch_interface *gti, u32 next_state, |
| u64 elapsed) |
| { |
| /* only valid next-state is 'END_CAL', as long as time elapsed |
| * is within ranged. |
| */ |
| if (next_state == ICAL_STATE_END_CAL && |
| elapsed > MIN_DELAY_RUN_CAL && elapsed < MAX_DELAY_RUN_CAL) { |
| GOOG_INFO(gti, |
| "ical - Calibration complete after %lluns\n", |
| elapsed); |
| |
| gti->ical_state = ICAL_STATE_END_CAL; |
| gti->ical_result = ICAL_RES_SUCCESS; |
| |
| } else { |
| GOOG_ERR(gti, |
| "ical - error: invalid transition or time! %u => %u, min=%lluns, t=%lluns, max=%lluns\n", |
| gti->ical_state, next_state, |
| MIN_DELAY_RUN_CAL, elapsed, |
| MAX_DELAY_RUN_CAL); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_result = ICAL_RES_FAIL; |
| } |
| } |
| |
| void ical_state_end_cal(struct goog_touch_interface *gti, u32 next_state, |
| u64 elapsed) |
| { |
| /* Nothing to do but accept a transition back to IDLE. |
| * Necessary because the interface only executes when called |
| */ |
| if (next_state == ICAL_STATE_IDLE && |
| elapsed > MIN_DELAY_END_CAL && elapsed < MAX_DELAY_END_CAL) { |
| gti->ical_result = ICAL_RES_SUCCESS; |
| } else { |
| GOOG_ERR(gti, |
| "ical - error: invalid transition or time! %u => %u, min=%lluns, t=%lluns, max=%lluns\n", |
| gti->ical_state, next_state, |
| MIN_DELAY_END_CAL, elapsed, |
| MAX_DELAY_END_CAL); |
| gti->ical_result = ICAL_RES_FAIL; |
| } |
| gti->ical_state = ICAL_STATE_IDLE; |
| } |
| |
| void ical_state_init_test(struct goog_touch_interface *gti, u32 next_state, |
| u64 elapsed) |
| { |
| int pm_ret = 0; |
| u32 ret; |
| |
| pm_ret = goog_pm_wake_lock(gti, GTI_PM_WAKELOCK_TYPE_SYSFS, false); |
| if (pm_ret < 0 && gti->tbn_enabled) { |
| GOOG_ERR(gti, "ical - error: invalid touch bus access!\n"); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_result = ICAL_RES_FAIL_INVALID_BUS_ACCESS; |
| return; |
| } |
| |
| /* only valid next-state is 'RUN_TEST', as long as time elapsed |
| * is within range. When 'RUN_TEST' is received test begins. |
| */ |
| if (next_state == ICAL_STATE_RUN_TEST && |
| elapsed > MIN_DELAY_INIT_TEST && elapsed < MAX_DELAY_INIT_TEST) { |
| /* Begin selftest */ |
| |
| gti->cmd.selftest_cmd.result = GTI_SELFTEST_RESULT_NA; |
| gti->cmd.selftest_cmd.is_ical = true; |
| memset(gti->cmd.selftest_cmd.buffer, 0, |
| sizeof(gti->cmd.selftest_cmd.buffer)); |
| ret = goog_do_selftest(gti); |
| if (ret == 0) { |
| if (gti->cmd.selftest_cmd.result == |
| GTI_SELFTEST_RESULT_DONE) { |
| gti->ical_func_result = gti->cmd.selftest_cmd.result; |
| |
| GOOG_INFO(gti, |
| "ical - SELFTEST_RESULT_DONE - [%s]\n", |
| gti->cmd.selftest_cmd.buffer); |
| } else if (gti->cmd.selftest_cmd.result == |
| GTI_SELFTEST_RESULT_SHELL_CMDS_REDIRECT) { |
| gti->ical_func_result = ICAL_RES_SUCCESS; |
| |
| GOOG_ERR(gti, |
| "ical - SELFTEST_RESULT_SHELL_CMDS_REDIRECT - [%s]\n", |
| gti->cmd.selftest_cmd.buffer); |
| } else { |
| gti->ical_func_result = ICAL_RES_FAIL; |
| |
| GOOG_ERR(gti, |
| "ical - selftest result other/fail - N/A or [%s]\n", |
| gti->cmd.selftest_cmd.buffer); |
| } |
| |
| gti->ical_state = ICAL_STATE_RUN_TEST; |
| gti->ical_result = ICAL_RES_SUCCESS; |
| } else { |
| GOOG_ERR(gti, "ical - GTI_CMD_SELFTEST fail(%d)\n", ret); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_result = ICAL_RES_FAIL; |
| } |
| } else { |
| GOOG_ERR(gti, |
| "ical - error: invalid transition or time! %u => %u, min=%lluns, t=%lluns, max=%lluns\n", |
| gti->ical_state, next_state, |
| MIN_DELAY_INIT_TEST, elapsed, |
| MAX_DELAY_INIT_TEST); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_result = ICAL_RES_FAIL; |
| } |
| |
| if (pm_ret == 0) |
| goog_pm_wake_unlock_nosync(gti, GTI_PM_WAKELOCK_TYPE_SYSFS); |
| |
| } |
| |
| void ical_state_run_test(struct goog_touch_interface *gti, u32 next_state, |
| u64 elapsed) |
| { |
| /* only valid next-state is 'END_TEST', as long as time elapsed |
| * is within ranged. |
| */ |
| if (next_state == ICAL_STATE_END_TEST && |
| elapsed > MIN_DELAY_RUN_TEST && elapsed < MAX_DELAY_RUN_TEST) { |
| /* Check and evaluate self-test here */ |
| gti->ical_state = ICAL_STATE_END_TEST; |
| gti->ical_result = ICAL_RES_SUCCESS; |
| |
| } else { |
| GOOG_ERR(gti, |
| "ical - error: invalid transition or time! %u => %u, min=%lluns, t=%lluns, max=%lluns\n", |
| gti->ical_state, next_state, |
| MIN_DELAY_RUN_TEST, elapsed, |
| MAX_DELAY_RUN_TEST); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_result = ICAL_RES_FAIL; |
| } |
| } |
| |
| void ical_state_end_test(struct goog_touch_interface *gti, u32 next_state, |
| u64 elapsed) |
| { |
| /* Nothing to do but accept a transition back to IDLE. |
| * Necessary because the interface only executes when called |
| */ |
| if (next_state == ICAL_STATE_IDLE && |
| elapsed > MIN_DELAY_END_TEST && elapsed < MAX_DELAY_END_TEST) { |
| gti->ical_result = ICAL_RES_SUCCESS; |
| } else { |
| GOOG_ERR(gti, |
| "ical - error: invalid transition or time! %u => %u, min=%lluns, t=%lluns, max=%lluns\n", |
| gti->ical_state, next_state, |
| MIN_DELAY_END_TEST, elapsed, |
| MAX_DELAY_END_TEST); |
| gti->ical_result = ICAL_RES_FAIL; |
| } |
| gti->ical_state = ICAL_STATE_IDLE; |
| } |
| |
| void ical_state_init_reset(struct goog_touch_interface *gti, u32 next_state, |
| u64 elapsed) |
| { |
| int pm_ret = 0; |
| u32 ret; |
| |
| pm_ret = goog_pm_wake_lock(gti, GTI_PM_WAKELOCK_TYPE_SYSFS, false); |
| if (pm_ret < 0 && gti->tbn_enabled) { |
| GOOG_ERR(gti, "ical - error: invalid touch bus access!\n"); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_result = ICAL_RES_FAIL_INVALID_BUS_ACCESS; |
| return; |
| } |
| |
| /* only valid next-state is 'RUN_RESET', as long as time elapsed |
| * is within range. When 'RUN_RESET' is received reset begins. |
| */ |
| if (next_state == ICAL_STATE_RUN_RESET && |
| elapsed > MIN_DELAY_INIT_RESET && elapsed < MAX_DELAY_INIT_RESET) { |
| /* Begin reset */ |
| gti->cmd.reset_cmd.setting = GTI_RESET_MODE_AUTO; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_RESET); |
| if (ret == 0) { |
| GOOG_INFO(gti, "ical - RESET_DONE\n"); |
| gti->ical_state = ICAL_STATE_RUN_RESET; |
| gti->ical_func_result = ICAL_RES_SUCCESS; |
| gti->ical_result = ICAL_RES_SUCCESS; |
| } else { |
| GOOG_ERR(gti, "ical - GTI_CMD_RESET fail(%d)\n", ret); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_func_result = ICAL_RES_NA; |
| gti->ical_result = ICAL_RES_FAIL; |
| } |
| } else { |
| GOOG_ERR(gti, |
| "ical - error: invalid transition or time! %u => %u, min=%lluns, t=%lluns, max=%lluns\n", |
| gti->ical_state, next_state, |
| MIN_DELAY_INIT_RESET, elapsed, |
| MAX_DELAY_INIT_RESET); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_result = ICAL_RES_FAIL; |
| } |
| |
| if (pm_ret == 0) |
| goog_pm_wake_unlock_nosync(gti, GTI_PM_WAKELOCK_TYPE_SYSFS); |
| |
| } |
| |
| void ical_state_run_reset(struct goog_touch_interface *gti, u32 next_state, |
| u64 elapsed) |
| { |
| /* only valid next-state is 'END_RESET', as long as time elapsed |
| * is within ranged. |
| */ |
| if (next_state == ICAL_STATE_END_RESET && |
| elapsed > MIN_DELAY_RUN_RESET && elapsed < MAX_DELAY_RUN_RESET) { |
| /* Check and evaluate reset here */ |
| gti->ical_state = ICAL_STATE_END_RESET; |
| gti->ical_result = ICAL_RES_SUCCESS; |
| } else { |
| GOOG_ERR(gti, |
| "ical - error: invalid transition or time! %u => %u, min=%lluns, t=%lluns, max=%lluns\n", |
| gti->ical_state, next_state, |
| MIN_DELAY_RUN_RESET, elapsed, |
| MAX_DELAY_RUN_RESET); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_result = ICAL_RES_FAIL; |
| } |
| } |
| |
| void ical_state_end_reset(struct goog_touch_interface *gti, u32 next_state, |
| u64 elapsed) |
| { |
| /* Nothing to do but accept a transition back to IDLE. |
| * Necessary because the interface only executes when called |
| */ |
| if (next_state == ICAL_STATE_IDLE && |
| elapsed > MIN_DELAY_END_RESET && elapsed < MAX_DELAY_END_RESET) { |
| gti->ical_result = ICAL_RES_SUCCESS; |
| } else { |
| GOOG_ERR(gti, |
| "ical - error: invalid transition or time! %u => %u, min=%lluns, t=%lluns, max=%lluns\n", |
| gti->ical_state, next_state, |
| MIN_DELAY_END_RESET, elapsed, |
| MAX_DELAY_END_RESET); |
| gti->ical_result = ICAL_RES_FAIL; |
| } |
| gti->ical_state = ICAL_STATE_IDLE; |
| } |
| |
| /* Advance the interactive calibration state machine */ |
| static ssize_t interactive_calibrate_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| u32 next_state = 0; |
| u64 entry_time = ktime_get_ns(); |
| u64 elapsed = entry_time - gti->ical_timestamp_ns; |
| |
| if (kstrtou32(buf, 10, &next_state)) { |
| GOOG_ERR(gti, "error: invalid input!\n"); |
| return size; |
| } |
| |
| GOOG_INFO(gti, "ical - [%u] start\n", next_state); |
| |
| switch (gti->ical_state) { |
| case ICAL_STATE_IDLE: |
| if (ical_state_idle(gti, next_state, elapsed)) |
| gti->ical_timestamp_ns = entry_time; |
| break; |
| |
| case ICAL_STATE_INIT_CAL: |
| ical_state_init_cal(gti, next_state, elapsed); |
| gti->ical_timestamp_ns = entry_time; |
| break; |
| |
| case ICAL_STATE_RUN_CAL: |
| ical_state_run_cal(gti, next_state, elapsed); |
| gti->ical_timestamp_ns = entry_time; |
| break; |
| |
| case ICAL_STATE_END_CAL: |
| ical_state_end_cal(gti, next_state, elapsed); |
| gti->ical_timestamp_ns = entry_time; |
| break; |
| |
| case ICAL_STATE_INIT_TEST: |
| ical_state_init_test(gti, next_state, elapsed); |
| gti->ical_timestamp_ns = entry_time; |
| break; |
| |
| case ICAL_STATE_RUN_TEST: |
| ical_state_run_test(gti, next_state, elapsed); |
| gti->ical_timestamp_ns = entry_time; |
| break; |
| |
| case ICAL_STATE_END_TEST: |
| ical_state_end_test(gti, next_state, elapsed); |
| gti->ical_timestamp_ns = entry_time; |
| break; |
| |
| case ICAL_STATE_INIT_RESET: |
| ical_state_init_reset(gti, next_state, elapsed); |
| gti->ical_timestamp_ns = entry_time; |
| break; |
| |
| case ICAL_STATE_RUN_RESET: |
| ical_state_run_reset(gti, next_state, elapsed); |
| gti->ical_timestamp_ns = entry_time; |
| break; |
| |
| case ICAL_STATE_END_RESET: |
| ical_state_end_reset(gti, next_state, elapsed); |
| gti->ical_timestamp_ns = entry_time; |
| break; |
| |
| default: |
| GOOG_ERR(gti, "ical - unknown/invalid current state = %u, but will go back to 0.\n", |
| gti->ical_state); |
| gti->ical_state = ICAL_STATE_IDLE; |
| gti->ical_result = ICAL_RES_SUCCESS; |
| break; |
| } |
| |
| return size; |
| } |
| |
| /* Show result/status of the calibrate state machine */ |
| static ssize_t interactive_calibrate_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ssize_t buf_idx = 0; |
| struct goog_touch_interface *gti = dev_get_drvdata(dev); |
| |
| buf_idx += sysfs_emit_at(buf, buf_idx, "%d - %d\n", |
| gti->ical_result, gti->ical_func_result); |
| |
| GOOG_INFO(gti, "ical - [%u](%d, %d) return\n", |
| gti->ical_state, gti->ical_result, gti->ical_func_result); |
| |
| return buf_idx; |
| } |
| |
| /*----------------------------------------------------------------------------- |
| * Debug: functions. |
| */ |
| #ifdef GTI_DEBUG_HEALTHCHECK_KFIFO_LEN |
| inline void gti_debug_healthcheck_push(struct goog_touch_interface *gti) |
| { |
| /* |
| * Use kfifo as circular buffer by skipping one element |
| * when fifo is full. |
| */ |
| if (kfifo_is_full(>i->debug_fifo_healthcheck)) |
| kfifo_skip(>i->debug_fifo_healthcheck); |
| kfifo_in(>i->debug_fifo_healthcheck, >i->debug_healthcheck, 1); |
| } |
| |
| inline void gti_debug_healthcheck_update(struct goog_touch_interface *gti, bool from_top_half) |
| { |
| if (from_top_half) { |
| gti->debug_healthcheck.irq_time = ktime_get_real(); |
| gti->debug_healthcheck.irq_index = gti->irq_index; |
| } else { |
| gti->debug_healthcheck.input_index = gti->input_index; |
| gti->debug_healthcheck.slot_bit_active = gti->slot_bit_active; |
| gti_debug_healthcheck_push(gti); |
| } |
| } |
| |
| void gti_debug_healthcheck_dump(struct goog_touch_interface *gti) |
| { |
| s16 i, count; |
| s64 delta; |
| s64 sec_delta; |
| u32 ms_delta; |
| ktime_t current_time = ktime_get_real(); |
| struct gti_debug_healthcheck *last_fifo = gti->debug_healthcheck_history; |
| |
| /* |
| * Use peek to keep data without pop-out to support different timing |
| * print-out by each caller. |
| */ |
| count = kfifo_out_peek(>i->debug_fifo_healthcheck, last_fifo, |
| GTI_DEBUG_HEALTHCHECK_KFIFO_LEN); |
| |
| i = max_t(s16, 0, count - GTI_DEBUG_HEALTHCHECK_LOGS_LEN); |
| for (; i < count ; i++) { |
| sec_delta = -1; |
| ms_delta = 0; |
| /* |
| * Calculate the delta time between irq triggered and current time. |
| */ |
| delta = ktime_ms_delta(current_time, last_fifo[i].irq_time); |
| if (delta > 0) |
| sec_delta = div_u64_rem(delta, MSEC_PER_SEC, &ms_delta); |
| GOOG_INFO(gti, "dump-int: #%llu(%lld.%u): C#%llu(0x%lx).\n", |
| last_fifo[i].irq_index, sec_delta, ms_delta, |
| last_fifo[i].input_index, last_fifo[i].slot_bit_active); |
| } |
| } |
| #endif /* GTI_DEBUG_HEALTHCHECK_KFIFO_LEN */ |
| |
| #ifdef GTI_DEBUG_INPUT_KFIFO_LEN |
| inline void gti_debug_input_push(struct goog_touch_interface *gti, int slot) |
| { |
| struct gti_debug_input fifo; |
| |
| if (slot < 0 || slot >= MAX_SLOTS) { |
| GOOG_ERR(gti, "Invalid slot: %d\n", slot); |
| return; |
| } |
| |
| /* |
| * Use kfifo as circular buffer by skipping one element |
| * when fifo is full. |
| */ |
| if (kfifo_is_full(>i->debug_fifo_input)) |
| kfifo_skip(>i->debug_fifo_input); |
| |
| memcpy(&fifo, >i->debug_input[slot], sizeof(struct gti_debug_input)); |
| kfifo_in(>i->debug_fifo_input, &fifo, 1); |
| } |
| |
| inline void gti_debug_input_update(struct goog_touch_interface *gti) |
| { |
| int slot; |
| u64 irq_index = gti->irq_index; |
| ktime_t time = ktime_get_real(); |
| |
| for_each_set_bit(slot, >i->slot_bit_changed, MAX_SLOTS) { |
| if (test_bit(slot, >i->slot_bit_active)) { |
| gti->debug_input[slot].pressed.time = time; |
| gti->debug_input[slot].pressed.irq_index = irq_index; |
| memcpy(>i->debug_input[slot].pressed.coord, |
| >i->offload.coords[slot], |
| sizeof(struct TouchOffloadCoord)); |
| } else { |
| gti->released_index++; |
| gti->debug_input[slot].released.time = time; |
| gti->debug_input[slot].released.irq_index = irq_index; |
| memcpy(>i->debug_input[slot].released.coord, |
| >i->offload.coords[slot], |
| sizeof(struct TouchOffloadCoord)); |
| gti_debug_input_push(gti, slot); |
| } |
| } |
| gti->slot_bit_changed = 0; |
| } |
| |
| void gti_debug_input_dump(struct goog_touch_interface *gti) |
| { |
| int slot; |
| s16 i, count; |
| s64 delta; |
| 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_real(); |
| struct gti_debug_input *last_fifo = gti->debug_input_history; |
| |
| /* |
| * Use peek to keep data without pop-out to support different timing |
| * print-out by each caller. |
| */ |
| count = kfifo_out_peek(>i->debug_fifo_input, last_fifo, |
| GTI_DEBUG_INPUT_KFIFO_LEN); |
| |
| i = max_t(s16, 0, count - GTI_DEBUG_INPUT_LOGS_LEN); |
| for ( ; i < count ; i++) { |
| if (last_fifo[i].slot < 0 || |
| last_fifo[i].slot >= MAX_SLOTS) { |
| GOOG_INFO(gti, "dump: #%d: invalid slot #!\n", last_fifo[i].slot); |
| continue; |
| } |
| sec_delta_down = -1; |
| ms_delta_down = 0; |
| /* |
| * Calculate the delta time of finger down from current time. |
| */ |
| delta = ktime_ms_delta(current_time, last_fifo[i].pressed.time); |
| if (delta > 0) |
| sec_delta_down = div_u64_rem(delta, MSEC_PER_SEC, &ms_delta_down); |
| |
| /* |
| * Calculate the delta time of finger duration from finger up to down. |
| */ |
| sec_delta_duration = -1; |
| ms_delta_duration = 0; |
| px_delta_x = 0; |
| px_delta_y = 0; |
| if (ktime_compare(last_fifo[i].released.time, |
| last_fifo[i].pressed.time) > 0) { |
| delta = ktime_ms_delta(last_fifo[i].released.time, |
| last_fifo[i].pressed.time); |
| if (delta > 0) { |
| sec_delta_duration = div_u64_rem(delta, MSEC_PER_SEC, |
| &ms_delta_duration); |
| px_delta_x = last_fifo[i].released.coord.x - |
| last_fifo[i].pressed.coord.x; |
| px_delta_y = last_fifo[i].released.coord.y - |
| last_fifo[i].pressed.coord.y; |
| } |
| } |
| |
| GOOG_INFO(gti, "dump: #%d: %lld.%u(%lld.%u) D(%d, %d) I(%llu, %llu).\n", |
| last_fifo[i].slot, |
| sec_delta_down, ms_delta_down, |
| sec_delta_duration, ms_delta_duration, |
| px_delta_x, px_delta_y, |
| last_fifo[i].pressed.irq_index, last_fifo[i].released.irq_index); |
| GOOG_DBG(gti, "dump-dbg: #%d: P(%u, %u) -> R(%u, %u).\n\n", |
| last_fifo[i].slot, |
| last_fifo[i].pressed.coord.x, last_fifo[i].pressed.coord.y, |
| last_fifo[i].released.coord.x, last_fifo[i].released.coord.y); |
| } |
| /* Extra check for unexpected case. */ |
| for_each_set_bit(slot, >i->slot_bit_active, MAX_SLOTS) { |
| GOOG_INFO(gti, "slot #%d(%u, %u, %u) is active!\n", slot, |
| gti->offload.coords[slot].x, gti->offload.coords[slot].y, |
| gti->offload.coords[slot].pressure); |
| } |
| } |
| #endif /* GTI_DEBUG_INPUT_KFIFO_LEN */ |
| |
| /*----------------------------------------------------------------------------- |
| * DRM: functions and structures. |
| */ |
| struct drm_connector *get_bridge_connector(struct drm_bridge *bridge) |
| { |
| struct drm_connector *connector; |
| struct drm_connector_list_iter conn_iter; |
| |
| drm_connector_list_iter_begin(bridge->dev, &conn_iter); |
| drm_for_each_connector_iter(connector, &conn_iter) { |
| if (connector->encoder == bridge->encoder) |
| break; |
| } |
| drm_connector_list_iter_end(&conn_iter); |
| return connector; |
| } |
| |
| static void panel_set_op_hz(struct work_struct *work) |
| { |
| struct goog_touch_interface *gti = container_of(work, |
| struct goog_touch_interface, set_op_hz_work); |
| int ret = 0; |
| |
| GOOG_LOGI(gti, "set panel op_hz: %d\n", gti->panel_op_hz); |
| |
| gti->cmd.panel_speed_mode_cmd.setting = gti->panel_op_hz == 120 ? |
| GTI_PANEL_SPEED_MODE_HS : GTI_PANEL_SPEED_MODE_NS; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_PANEL_SPEED_MODE); |
| if (ret) |
| GOOG_LOGW(gti, "unexpected return(%d)!", ret); |
| } |
| |
| static int panel_notifier_call( |
| struct notifier_block *nb, unsigned long id, void *data) |
| { |
| struct goog_touch_interface *gti = |
| (struct goog_touch_interface *)container_of(nb, |
| struct goog_touch_interface, panel_notifier); |
| |
| GOOG_LOGI(gti, "\n"); |
| |
| if (!gti->connector || !gti->connector->state) |
| gti->connector = get_bridge_connector(>i->panel_bridge); |
| |
| if (!gti->connector) return 0; |
| |
| #if IS_ENABLED(CONFIG_GS_DRM_PANEL_UNIFIED) |
| if (is_gs_drm_connector(gti->connector)) { |
| if (id == GS_PANEL_NOTIFIER_SET_OP_HZ) { |
| gti->panel_op_hz = *(unsigned int*)data; |
| if (gti->event_wq != NULL) |
| queue_work(gti->event_wq, >i->set_op_hz_work); |
| } |
| } |
| #endif |
| if (is_exynos_drm_connector(gti->connector)) { |
| if (id == EXYNOS_PANEL_NOTIFIER_SET_OP_HZ) { |
| gti->panel_op_hz = *(unsigned int*)data; |
| if (gti->event_wq != NULL) |
| queue_work(gti->event_wq, >i->set_op_hz_work); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int panel_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) |
| { |
| struct goog_touch_interface *gti = |
| container_of(bridge, struct goog_touch_interface, panel_bridge); |
| |
| if (gti->panel_notifier_enabled) { |
| if (!gti->connector || !gti->connector->state) |
| gti->connector = get_bridge_connector(>i->panel_bridge); |
| |
| if (!gti->connector) { |
| GOOG_LOGW(gti, "can't get panel connector to resgister notification!\n"); |
| return 0; |
| } |
| |
| gti->panel_notifier.notifier_call = panel_notifier_call; |
| #if IS_ENABLED(CONFIG_GS_DRM_PANEL_UNIFIED) |
| if (is_gs_drm_connector(gti->connector)) |
| gs_connector_register_op_hz_notifier(gti->connector, >i->panel_notifier); |
| #endif |
| if (is_exynos_drm_connector(gti->connector)) |
| exynos_panel_register_notifier(gti->connector, >i->panel_notifier); |
| } |
| |
| return 0; |
| } |
| |
| static void panel_bridge_detach(struct drm_bridge *bridge) |
| { |
| struct goog_touch_interface *gti = |
| container_of(bridge, struct goog_touch_interface, panel_bridge); |
| |
| if (gti->panel_notifier_enabled) { |
| if (!gti->connector || !gti->connector->state) |
| gti->connector = get_bridge_connector(>i->panel_bridge); |
| |
| if (!gti->connector) return; |
| |
| #if IS_ENABLED(CONFIG_GS_DRM_PANEL_UNIFIED) |
| if (is_gs_drm_connector(gti->connector)) |
| gs_connector_unregister_op_hz_notifier(gti->connector, >i->panel_notifier); |
| #endif |
| if (is_exynos_drm_connector(gti->connector)) |
| exynos_panel_unregister_notifier(gti->connector, >i->panel_notifier); |
| } |
| } |
| |
| static void panel_bridge_enable(struct drm_bridge *bridge) |
| { |
| struct goog_touch_interface *gti = |
| container_of(bridge, struct goog_touch_interface, panel_bridge); |
| |
| if (gti->panel_is_lp_mode) { |
| GOOG_DBG(gti, "skip screen-on because of panel_is_lp_mode enabled!\n"); |
| return; |
| } |
| |
| goog_set_display_state(gti, GTI_DISPLAY_STATE_ON); |
| } |
| |
| static void panel_bridge_disable(struct drm_bridge *bridge) |
| { |
| struct goog_touch_interface *gti = |
| container_of(bridge, struct goog_touch_interface, 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; |
| } |
| |
| goog_set_display_state(gti, GTI_DISPLAY_STATE_OFF); |
| } |
| |
| static bool panel_bridge_is_lp_mode(struct drm_connector *connector) |
| { |
| if (connector && connector->state) { |
| #if IS_ENABLED(CONFIG_GS_DRM_PANEL_UNIFIED) |
| if (is_gs_drm_connector(connector)) { |
| struct gs_drm_connector_state *s = |
| to_gs_connector_state(connector->state); |
| |
| return s->gs_mode.is_lp_mode; |
| } |
| #endif |
| if (is_exynos_drm_connector(connector)) { |
| struct exynos_drm_connector_state *s = |
| to_exynos_connector_state(connector->state); |
| |
| return s->exynos_mode.is_lp_mode; |
| } |
| } |
| return false; |
| } |
| |
| static void panel_bridge_mode_set(struct drm_bridge *bridge, |
| const struct drm_display_mode *mode, |
| const struct drm_display_mode *adjusted_mode) |
| { |
| int ret = 0; |
| bool panel_is_lp_mode; |
| struct goog_touch_interface *gti = |
| container_of(bridge, struct goog_touch_interface, panel_bridge); |
| |
| if (!gti->connector || !gti->connector->state) |
| gti->connector = get_bridge_connector(bridge); |
| |
| panel_is_lp_mode = panel_bridge_is_lp_mode(gti->connector); |
| if (gti->panel_is_lp_mode != panel_is_lp_mode) { |
| GOOG_INFO(gti, "panel_is_lp_mode changed from %d to %d.\n", |
| gti->panel_is_lp_mode, panel_is_lp_mode); |
| |
| if (panel_is_lp_mode) |
| goog_set_display_state(gti, GTI_DISPLAY_STATE_OFF); |
| else |
| goog_set_display_state(gti, GTI_DISPLAY_STATE_ON); |
| } |
| gti->panel_is_lp_mode = panel_is_lp_mode; |
| |
| if (mode) { |
| int vrefresh = drm_mode_vrefresh(mode); |
| |
| if (gti->display_vrefresh != vrefresh) { |
| GOOG_DBG(gti, "display_vrefresh(Hz) changed to %d from %d.\n", |
| vrefresh, gti->display_vrefresh); |
| gti->display_vrefresh = vrefresh; |
| gti->cmd.display_vrefresh_cmd.setting = vrefresh; |
| gti->context_changed.display_refresh_rate = 1; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_NOTIFY_DISPLAY_VREFRESH); |
| if (ret && ret != -EOPNOTSUPP) |
| GOOG_LOGW(gti, "unexpected return(%d)!", ret); |
| |
| if (gti->vrr_enabled) |
| goog_lookup_touch_report_rate(gti); |
| } |
| } |
| } |
| |
| static const struct drm_bridge_funcs panel_bridge_funcs = { |
| .attach = panel_bridge_attach, |
| .detach = panel_bridge_detach, |
| .enable = panel_bridge_enable, |
| .disable = panel_bridge_disable, |
| .mode_set = panel_bridge_mode_set, |
| }; |
| |
| static int register_panel_bridge(struct goog_touch_interface *gti) |
| { |
| GOOG_LOGI(gti, "\n"); |
| |
| INIT_WORK(>i->set_op_hz_work, panel_set_op_hz); |
| |
| #ifdef CONFIG_OF |
| gti->panel_bridge.of_node = gti->vendor_dev->of_node; |
| #endif |
| gti->panel_bridge.funcs = &panel_bridge_funcs; |
| drm_bridge_add(>i->panel_bridge); |
| |
| return 0; |
| } |
| |
| static void unregister_panel_bridge(struct drm_bridge *bridge) |
| { |
| struct goog_touch_interface *gti = |
| container_of(bridge, struct goog_touch_interface, panel_bridge); |
| struct drm_bridge *node; |
| |
| GOOG_LOGI(gti, "\n"); |
| drm_bridge_remove(bridge); |
| |
| if (!bridge->dev) /* not attached */ |
| return; |
| |
| drm_modeset_lock(&bridge->dev->mode_config.connection_mutex, NULL); |
| list_for_each_entry(node, &bridge->encoder->bridge_chain, chain_node) { |
| if (node == bridge) { |
| if (bridge->funcs->detach) |
| bridge->funcs->detach(bridge); |
| list_del(&bridge->chain_node); |
| break; |
| } |
| } |
| drm_modeset_unlock(&bridge->dev->mode_config.connection_mutex); |
| bridge->dev = NULL; |
| } |
| |
| /*----------------------------------------------------------------------------- |
| * GTI: functions. |
| */ |
| static int goog_precheck_heatmap(struct goog_touch_interface *gti) |
| { |
| int ret = 0; |
| |
| /* |
| * Check the PM wakelock state and pm state for bus ownership before |
| * data request. |
| */ |
| if (!goog_pm_wake_get_locks(gti) || gti->pm.state == GTI_PM_SUSPEND) { |
| GOOG_WARN(gti, "N/A during inactive bus!\n"); |
| ret = -ENODATA; |
| } |
| |
| return ret; |
| } |
| |
| static void goog_set_display_state(struct goog_touch_interface *gti, |
| enum gti_display_state_setting display_state) |
| { |
| int ret = 0; |
| |
| if (gti->display_state == display_state) |
| return; |
| |
| switch (display_state) { |
| case GTI_DISPLAY_STATE_OFF: |
| GOOG_INFO(gti, "screen-off.\n"); |
| ret = goog_pm_wake_unlock_nosync(gti, GTI_PM_WAKELOCK_TYPE_SCREEN_ON); |
| if (ret < 0) |
| GOOG_INFO(gti, "Error while obtaining screen-off wakelock: %d!\n", ret); |
| |
| break; |
| case GTI_DISPLAY_STATE_ON: |
| GOOG_INFO(gti, "screen-on.\n"); |
| ret = goog_pm_wake_lock_nosync(gti, GTI_PM_WAKELOCK_TYPE_SCREEN_ON, false); |
| if (ret < 0) |
| GOOG_INFO(gti, "Error while obtaining screen-on wakelock: %d!\n", ret); |
| |
| break; |
| default: |
| GOOG_ERR(gti, "Unexpected value(0x%X) of display state parameter.\n", |
| display_state); |
| return; |
| } |
| |
| gti->context_changed.screen_state = 1; |
| gti->display_state = display_state; |
| gti->cmd.display_state_cmd.setting = display_state; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_NOTIFY_DISPLAY_STATE); |
| if (ret && ret != -EOPNOTSUPP) |
| GOOG_WARN(gti, "Unexpected vendor_cmd return(%d)!\n", ret); |
| } |
| |
| static int goog_do_selftest(struct goog_touch_interface *gti) |
| { |
| int ret = 0; |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_SELFTEST); |
| if (gti->reset_after_selftest && ret != EOPNOTSUPP) { |
| gti->cmd.reset_cmd.setting = GTI_RESET_MODE_AUTO; |
| goog_process_vendor_cmd(gti, GTI_CMD_RESET); |
| } |
| return ret; |
| } |
| |
| bool goog_check_spi_dma_enabled(struct spi_device *spi_dev) |
| { |
| bool ret = false; |
| |
| if (spi_dev && spi_dev->controller) { |
| struct device_node *np = spi_dev->controller->dev.of_node; |
| |
| /* |
| * Check the SPI controller(s3c64xx-spi) whether support DMA |
| * or not. |
| */ |
| ret = of_property_read_bool(np, "dma-mode"); |
| } |
| |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(goog_check_spi_dma_enabled); |
| |
| int goog_get_max_touch_report_rate(struct goog_touch_interface *gti) |
| { |
| int max_idx; |
| |
| if (!gti || |
| !gti->report_rate_table_size || |
| !gti->touch_report_rate_table) { |
| return -EOPNOTSUPP; |
| } |
| |
| max_idx = gti->report_rate_table_size - 1; |
| return gti->touch_report_rate_table[max_idx]; |
| } |
| EXPORT_SYMBOL_GPL(goog_get_max_touch_report_rate); |
| |
| int goog_get_panel_id(struct device_node *node) |
| { |
| int id = -1; |
| int err; |
| int index; |
| struct of_phandle_args panelmap; |
| struct drm_panel *panel = NULL; |
| |
| if (!of_property_read_bool(node, "goog,panel_map")) { |
| pr_warn("%s: panel_map doesn't exist!\n", __func__); |
| return id; |
| } |
| |
| for (index = 0;; index++) { |
| err = of_parse_phandle_with_fixed_args( |
| node, "goog,panel_map", 1, index, &panelmap); |
| if (err) { |
| pr_warn("%s: Fail to find panel for index: %d!\n", __func__, index); |
| break; |
| } |
| |
| panel = of_drm_find_panel(panelmap.np); |
| of_node_put(panelmap.np); |
| if (IS_ERR_OR_NULL(panel)) |
| continue; |
| |
| id = panelmap.args[0]; |
| break; |
| } |
| |
| return id; |
| } |
| EXPORT_SYMBOL_GPL(goog_get_panel_id); |
| |
| int goog_get_firmware_name(struct device_node *node, int id, char *name, size_t size) |
| { |
| int err; |
| const char *fw_name; |
| |
| err = of_property_read_string_index(node, "goog,firmware_names", id, &fw_name); |
| if (err == 0) { |
| strlcpy(name, fw_name, size); |
| pr_info("%s: found firmware name: %s\n", __func__, name); |
| } else { |
| pr_warn("%s: Fail to find firmware name!\n", __func__); |
| } |
| return err; |
| } |
| EXPORT_SYMBOL_GPL(goog_get_firmware_name); |
| |
| int goog_get_config_name(struct device_node *node, int id, char *name, size_t size) |
| { |
| int err; |
| const char *config_name; |
| |
| err = of_property_read_string_index(node, "goog,config_names", id, &config_name); |
| if (err == 0) { |
| strncpy(name, config_name, size); |
| pr_info("%s: found config name: %s\n", __func__, name); |
| } else { |
| pr_warn("%s: Fail to find config name!\n", __func__); |
| } |
| return err; |
| } |
| EXPORT_SYMBOL_GPL(goog_get_config_name); |
| |
| int goog_get_test_limits_name(struct device_node *node, int id, char *name, size_t size) |
| { |
| int err; |
| const char *limits_name; |
| |
| err = of_property_read_string_index(node, "goog,test_limits_names", id, &limits_name); |
| if (err == 0) { |
| strncpy(name, limits_name, size); |
| pr_info("%s: found test limits name: %s\n", __func__, name); |
| } else { |
| pr_warn("%s: Fail to find test limits name!\n", __func__); |
| } |
| return err; |
| } |
| EXPORT_SYMBOL_GPL(goog_get_test_limits_name); |
| |
| int goog_process_vendor_cmd(struct goog_touch_interface *gti, enum gti_cmd_type cmd_type) |
| { |
| void *private_data = gti->vendor_private_data; |
| int ret = -ESRCH; |
| |
| /* Use optional vendor operation if available. */ |
| switch (cmd_type) { |
| case GTI_CMD_CALIBRATE: |
| ret = gti->options.calibrate(private_data, >i->cmd.calibrate_cmd); |
| break; |
| case GTI_CMD_PING: |
| ret = gti->options.ping(private_data, >i->cmd.ping_cmd); |
| break; |
| case GTI_CMD_RESET: |
| ret = gti->options.reset(private_data, >i->cmd.reset_cmd); |
| break; |
| case GTI_CMD_SELFTEST: |
| ret = gti->options.selftest(private_data, >i->cmd.selftest_cmd); |
| break; |
| case GTI_CMD_GET_CONTEXT_DRIVER: |
| ret = gti->options.get_context_driver(private_data, >i->cmd.context_driver_cmd); |
| break; |
| case GTI_CMD_GET_CONTEXT_STYLUS: |
| ret = gti->options.get_context_stylus(private_data, >i->cmd.context_stylus_cmd); |
| break; |
| case GTI_CMD_GET_COORD_FILTER_ENABLED: |
| ret = gti->options.get_coord_filter_enabled(private_data, |
| >i->cmd.coord_filter_cmd); |
| break; |
| case GTI_CMD_GET_FW_VERSION: |
| ret = gti->options.get_fw_version(private_data, >i->cmd.fw_version_cmd); |
| break; |
| case GTI_CMD_GET_GRIP_MODE: |
| ret = gti->options.get_grip_mode(private_data, >i->cmd.grip_cmd); |
| break; |
| case GTI_CMD_GET_IRQ_MODE: |
| ret = gti->options.get_irq_mode(private_data, >i->cmd.irq_cmd); |
| break; |
| case GTI_CMD_GET_PALM_MODE: |
| ret = gti->options.get_palm_mode(private_data, >i->cmd.palm_cmd); |
| break; |
| case GTI_CMD_GET_SCAN_MODE: |
| ret = gti->options.get_scan_mode(private_data, >i->cmd.scan_cmd); |
| break; |
| case GTI_CMD_GET_SCREEN_PROTECTOR_MODE: |
| ret = gti->options.get_screen_protector_mode(private_data, |
| >i->cmd.screen_protector_mode_cmd); |
| break; |
| case GTI_CMD_GET_SENSING_MODE: |
| ret = gti->options.get_sensing_mode(private_data, >i->cmd.sensing_cmd); |
| break; |
| case GTI_CMD_GET_SENSOR_DATA: |
| if (gti->cmd.sensor_data_cmd.type & TOUCH_SCAN_TYPE_MUTUAL) { |
| ret = gti->options.get_mutual_sensor_data( |
| private_data, >i->cmd.sensor_data_cmd); |
| } else if (gti->cmd.sensor_data_cmd.type & TOUCH_SCAN_TYPE_SELF) { |
| ret = gti->options.get_self_sensor_data( |
| private_data, >i->cmd.sensor_data_cmd); |
| } |
| break; |
| case GTI_CMD_GET_SENSOR_DATA_MANUAL: |
| if (gti->cmd.manual_sensor_data_cmd.type & TOUCH_SCAN_TYPE_MUTUAL) { |
| ret = gti->options.get_mutual_sensor_data( |
| private_data, >i->cmd.manual_sensor_data_cmd); |
| } else if (gti->cmd.manual_sensor_data_cmd.type & TOUCH_SCAN_TYPE_SELF) { |
| ret = gti->options.get_self_sensor_data( |
| private_data, >i->cmd.manual_sensor_data_cmd); |
| } |
| break; |
| case GTI_CMD_NOTIFY_DISPLAY_STATE: |
| ret = gti->options.notify_display_state(private_data, |
| >i->cmd.display_state_cmd); |
| break; |
| case GTI_CMD_NOTIFY_DISPLAY_VREFRESH: |
| ret = gti->options.notify_display_vrefresh(private_data, |
| >i->cmd.display_vrefresh_cmd); |
| break; |
| case GTI_CMD_SET_CONTINUOUS_REPORT: |
| ret = gti->options.set_continuous_report(private_data, |
| >i->cmd.continuous_report_cmd); |
| break; |
| case GTI_CMD_SET_COORD_FILTER_ENABLED: |
| ret = gti->options.set_coord_filter_enabled(private_data, |
| >i->cmd.coord_filter_cmd); |
| break; |
| case GTI_CMD_SET_GESTURE_CONFIG: |
| ret = gti->options.set_gesture_config(private_data, >i->cmd.gesture_config_cmd); |
| break; |
| case GTI_CMD_SET_GRIP_MODE: |
| GOOG_INFO(gti, "Set firmware grip %s", |
| gti->cmd.grip_cmd.setting == GTI_GRIP_ENABLE ? |
| "enabled" : "disabled"); |
| ret = gti->options.set_grip_mode(private_data, >i->cmd.grip_cmd); |
| break; |
| case GTI_CMD_SET_HEATMAP_ENABLED: |
| ret = gti->options.set_heatmap_enabled(private_data, >i->cmd.heatmap_cmd); |
| break; |
| case GTI_CMD_SET_IRQ_MODE: |
| ret = gti->options.set_irq_mode(private_data, >i->cmd.irq_cmd); |
| break; |
| case GTI_CMD_SET_PALM_MODE: |
| GOOG_INFO(gti, "Set firmware palm %s", |
| gti->cmd.palm_cmd.setting == GTI_PALM_ENABLE ? |
| "enabled" : "disabled"); |
| ret = gti->options.set_palm_mode(private_data, >i->cmd.palm_cmd); |
| break; |
| case GTI_CMD_SET_PANEL_SPEED_MODE: |
| GOOG_INFO(gti, "Set panel speed mode: %s", |
| gti->cmd.panel_speed_mode_cmd.setting == GTI_PANEL_SPEED_MODE_NS ? |
| "NS" : "HS"); |
| ret = gti->options.set_panel_speed_mode(private_data, >i->cmd.panel_speed_mode_cmd); |
| break; |
| case GTI_CMD_SET_REPORT_RATE: |
| GOOG_INFO(gti, "Set touch report rate as %d Hz", gti->cmd.report_rate_cmd.setting); |
| ret = gti->options.set_report_rate(private_data, >i->cmd.report_rate_cmd); |
| break; |
| case GTI_CMD_SET_SCAN_MODE: |
| ret = gti->options.set_scan_mode(private_data, >i->cmd.scan_cmd); |
| break; |
| case GTI_CMD_SET_SCREEN_PROTECTOR_MODE: |
| GOOG_INFO(gti, "Set screen protector mode %s", |
| gti->cmd.screen_protector_mode_cmd.setting == |
| GTI_SCREEN_PROTECTOR_MODE_ENABLE |
| ? "enabled" : "disabled"); |
| ret = gti->options.set_screen_protector_mode(private_data, |
| >i->cmd.screen_protector_mode_cmd); |
| break; |
| case GTI_CMD_SET_SENSING_MODE: |
| ret = gti->options.set_sensing_mode(private_data, >i->cmd.sensing_cmd); |
| break; |
| default: |
| break; |
| } |
| |
| /* Back to vendor default handler if no optional operation available. */ |
| if (ret == -ESRCH) |
| ret = gti->vendor_default_handler(private_data, cmd_type, >i->cmd); |
| |
| /* Take unsupported cmd_type as debug logs for compatibility check. */ |
| if (ret == -EOPNOTSUPP) { |
| GOOG_DBG(gti, "unsupported request cmd_type %#x!\n", cmd_type); |
| ret = 0; |
| } else if (ret == -ESRCH) { |
| GOOG_WARN(gti, "No handler for cmd_type %#x!\n", cmd_type); |
| ret = 0; |
| } |
| |
| return ret; |
| } |
| |
| void goog_update_motion_filter(struct goog_touch_interface *gti, unsigned long slot_bit) |
| { |
| int ret = 0; |
| const u32 mf_timeout_ms = 500; |
| unsigned long touches = hweight_long(slot_bit); |
| u32 next_state = gti->mf_state; |
| |
| switch (gti->mf_mode) { |
| case GTI_MF_MODE_AUTO_REPORT: |
| case GTI_MF_MODE_UNFILTER: |
| next_state = GTI_MF_STATE_UNFILTERED; |
| break; |
| case GTI_MF_MODE_FILTER: |
| next_state = GTI_MF_STATE_FILTERED; |
| break; |
| case GTI_MF_MODE_DYNAMIC: |
| default: |
| /* |
| * Determine the next filter state. The motion filter is enabled by |
| * default and it is disabled while a single finger is touching the |
| * screen. If another finger is touched down or if a timeout expires, |
| * the motion filter is reenabled and remains enabled until all fingers |
| * are lifted. |
| */ |
| switch (next_state) { |
| case GTI_MF_STATE_FILTERED: |
| if (touches == 1) { |
| next_state = GTI_MF_STATE_UNFILTERED; |
| gti->mf_downtime = ktime_get(); |
| } |
| break; |
| case GTI_MF_STATE_UNFILTERED: |
| if (touches == 0) { |
| next_state = GTI_MF_STATE_FILTERED; |
| } else if (touches > 1 || |
| ktime_after(ktime_get(), |
| ktime_add_ms(gti->mf_downtime, mf_timeout_ms))) { |
| next_state = GTI_MF_STATE_FILTERED_LOCKED; |
| } |
| break; |
| case GTI_MF_STATE_FILTERED_LOCKED: |
| if (touches == 0) |
| next_state = GTI_MF_STATE_FILTERED; |
| break; |
| } |
| break; |
| } |
| |
| /* Send command to setup continuous report. */ |
| if ((next_state == GTI_MF_STATE_UNFILTERED) != |
| (gti->mf_state == GTI_MF_STATE_UNFILTERED)) { |
| gti->cmd.continuous_report_cmd.setting = GTI_CONTINUOUS_REPORT_DISABLE; |
| |
| if (next_state == GTI_MF_STATE_UNFILTERED) |
| gti->cmd.continuous_report_cmd.setting = GTI_CONTINUOUS_REPORT_ENABLE; |
| |
| ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_CONTINUOUS_REPORT); |
| if (ret) |
| GOOG_LOGW(gti, "unexpected return(%d)!", ret); |
| } |
| |
| if (ret == 0) |
| gti->mf_state = next_state; |
| } |
| |
| bool goog_v4l2_read_frame_cb(struct v4l2_heatmap *v4l2) |
| { |
| struct goog_touch_interface *gti = container_of(v4l2, struct goog_touch_interface, v4l2); |
| bool ret = false; |
| u32 v4l2_size = gti->v4l2.width * gti->v4l2.height * 2; |
| |
| if (gti->heatmap_buf && v4l2_size == gti->heatmap_buf_size) { |
| memcpy(v4l2->frame, gti->heatmap_buf, v4l2_size); |
| ret = true; |
| } else { |
| GOOG_LOGE(gti, "wrong pointer(%p) or size (W: %lu, H: %lu) vs %u\n", |
| gti->heatmap_buf, gti->v4l2.width, gti->v4l2.height, gti->heatmap_buf_size); |
| } |
| |
| return ret; |
| } |
| |
| void goog_v4l2_read(struct goog_touch_interface *gti, ktime_t timestamp, u64 frame_index) |
| { |
| if (gti->v4l2_enabled) { |
| gti->v4l2.frame_index = frame_index; |
| heatmap_read(>i->v4l2, ktime_to_ns(timestamp)); |
| } |
| } |
| |
| int goog_get_driver_status(struct goog_touch_interface *gti, |
| struct gti_context_driver_cmd *driver_cmd) |
| { |
| gti->context_changed.offload_timestamp = 1; |
| |
| driver_cmd->context_changed.value = gti->context_changed.value; |
| driver_cmd->screen_state = gti->display_state; |
| driver_cmd->display_refresh_rate = gti->display_vrefresh; |
| driver_cmd->touch_report_rate = gti->report_rate_setting; |
| driver_cmd->noise_state = gti->fw_status.noise_level; |
| driver_cmd->water_mode = gti->fw_status.water_mode; |
| driver_cmd->charger_state = gti->charger_state; |
| driver_cmd->offload_timestamp = ktime_get(); |
| |
| /* vendor driver overwrite the context */ |
| return goog_process_vendor_cmd(gti, GTI_CMD_GET_CONTEXT_DRIVER); |
| } |
| |
| int goog_offload_populate_coordinate_channel(struct goog_touch_interface *gti, |
| struct touch_offload_frame *frame, int channel) |
| { |
| int i; |
| struct TouchOffloadDataCoord *dc; |
| |
| if (channel < 0 || channel >= MAX_CHANNELS) { |
| GOOG_LOGE(gti, "Invalid channel: %d\n", channel); |
| return -EINVAL; |
| } |
| |
| dc = (struct TouchOffloadDataCoord *)frame->channel_data[channel]; |
| memset(dc, 0, frame->channel_data_size[channel]); |
| dc->header.channel_type = TOUCH_DATA_TYPE_COORD; |
| dc->header.channel_size = TOUCH_OFFLOAD_FRAME_SIZE_COORD; |
| |
| for (i = 0; i < MAX_SLOTS; i++) { |
| dc->coords[i].x = gti->offload.coords[i].x; |
| dc->coords[i].y = gti->offload.coords[i].y; |
| dc->coords[i].major = gti->offload.coords[i].major; |
| dc->coords[i].minor = gti->offload.coords[i].minor; |
| dc->coords[i].pressure = gti->offload.coords[i].pressure; |
| dc->coords[i].rotation = gti->offload.coords[i].rotation; |
| dc->coords[i].status = gti->offload.coords[i].status; |
| } |
| |