blob: c7a4c46254b571f4dd85fc17ec7f79e1ada01e25 [file] [log] [blame]
// 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 = &gti->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 = &gti->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(&gti->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, &gti->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(&gti->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(&gti->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(&gti->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(&gti->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(&gti->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(&gti->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(&gti->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(&gti->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(&gti->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(&gti->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(&gti->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(&gti->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(&gti->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 = &gti->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 = &gti->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 = &gti->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, &gti->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 = &gti->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 = &gti->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, &gti->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, &gti->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(&gti->debug_fifo_healthcheck))
kfifo_skip(&gti->debug_fifo_healthcheck);
kfifo_in(&gti->debug_fifo_healthcheck, &gti->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(&gti->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(&gti->debug_fifo_input))
kfifo_skip(&gti->debug_fifo_input);
memcpy(&fifo, &gti->debug_input[slot], sizeof(struct gti_debug_input));
kfifo_in(&gti->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, &gti->slot_bit_changed, MAX_SLOTS) {
if (test_bit(slot, &gti->slot_bit_active)) {
gti->debug_input[slot].pressed.time = time;
gti->debug_input[slot].pressed.irq_index = irq_index;
memcpy(&gti->debug_input[slot].pressed.coord,
&gti->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(&gti->debug_input[slot].released.coord,
&gti->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(&gti->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, &gti->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(&gti->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, &gti->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, &gti->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(&gti->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, &gti->panel_notifier);
#endif
if (is_exynos_drm_connector(gti->connector))
exynos_panel_register_notifier(gti->connector, &gti->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(&gti->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, &gti->panel_notifier);
#endif
if (is_exynos_drm_connector(gti->connector))
exynos_panel_unregister_notifier(gti->connector, &gti->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(&gti->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(&gti->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, &gti->cmd.calibrate_cmd);
break;
case GTI_CMD_PING:
ret = gti->options.ping(private_data, &gti->cmd.ping_cmd);
break;
case GTI_CMD_RESET:
ret = gti->options.reset(private_data, &gti->cmd.reset_cmd);
break;
case GTI_CMD_SELFTEST:
ret = gti->options.selftest(private_data, &gti->cmd.selftest_cmd);
break;
case GTI_CMD_GET_CONTEXT_DRIVER:
ret = gti->options.get_context_driver(private_data, &gti->cmd.context_driver_cmd);
break;
case GTI_CMD_GET_CONTEXT_STYLUS:
ret = gti->options.get_context_stylus(private_data, &gti->cmd.context_stylus_cmd);
break;
case GTI_CMD_GET_COORD_FILTER_ENABLED:
ret = gti->options.get_coord_filter_enabled(private_data,
&gti->cmd.coord_filter_cmd);
break;
case GTI_CMD_GET_FW_VERSION:
ret = gti->options.get_fw_version(private_data, &gti->cmd.fw_version_cmd);
break;
case GTI_CMD_GET_GRIP_MODE:
ret = gti->options.get_grip_mode(private_data, &gti->cmd.grip_cmd);
break;
case GTI_CMD_GET_IRQ_MODE:
ret = gti->options.get_irq_mode(private_data, &gti->cmd.irq_cmd);
break;
case GTI_CMD_GET_PALM_MODE:
ret = gti->options.get_palm_mode(private_data, &gti->cmd.palm_cmd);
break;
case GTI_CMD_GET_SCAN_MODE:
ret = gti->options.get_scan_mode(private_data, &gti->cmd.scan_cmd);
break;
case GTI_CMD_GET_SCREEN_PROTECTOR_MODE:
ret = gti->options.get_screen_protector_mode(private_data,
&gti->cmd.screen_protector_mode_cmd);
break;
case GTI_CMD_GET_SENSING_MODE:
ret = gti->options.get_sensing_mode(private_data, &gti->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, &gti->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, &gti->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, &gti->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, &gti->cmd.manual_sensor_data_cmd);
}
break;
case GTI_CMD_NOTIFY_DISPLAY_STATE:
ret = gti->options.notify_display_state(private_data,
&gti->cmd.display_state_cmd);
break;
case GTI_CMD_NOTIFY_DISPLAY_VREFRESH:
ret = gti->options.notify_display_vrefresh(private_data,
&gti->cmd.display_vrefresh_cmd);
break;
case GTI_CMD_SET_CONTINUOUS_REPORT:
ret = gti->options.set_continuous_report(private_data,
&gti->cmd.continuous_report_cmd);
break;
case GTI_CMD_SET_COORD_FILTER_ENABLED:
ret = gti->options.set_coord_filter_enabled(private_data,
&gti->cmd.coord_filter_cmd);
break;
case GTI_CMD_SET_GESTURE_CONFIG:
ret = gti->options.set_gesture_config(private_data, &gti->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, &gti->cmd.grip_cmd);
break;
case GTI_CMD_SET_HEATMAP_ENABLED:
ret = gti->options.set_heatmap_enabled(private_data, &gti->cmd.heatmap_cmd);
break;
case GTI_CMD_SET_IRQ_MODE:
ret = gti->options.set_irq_mode(private_data, &gti->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, &gti->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, &gti->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, &gti->cmd.report_rate_cmd);
break;
case GTI_CMD_SET_SCAN_MODE:
ret = gti->options.set_scan_mode(private_data, &gti->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,
&gti->cmd.screen_protector_mode_cmd);
break;
case GTI_CMD_SET_SENSING_MODE:
ret = gti->options.set_sensing_mode(private_data, &gti->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, &gti->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(&gti->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;
}