blob: 875670bc96a5adb23cd487ba097f099a966ce0ab [file] [log] [blame]
/*
* Synaptics DSX touchscreen driver
*
* Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved.
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
* DOLLARS.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/input/synaptics_dsx.h>
#include "synaptics_dsx_core.h"
#define GESTURE_PHYS_NAME "synaptics_dsx/gesture"
#define TUNING_SYSFS_DIR_NAME "tuning"
#define STORE_GESTURES
#ifdef STORE_GESTURES
#define GESTURES_TO_STORE 10
#endif
#define CTRL23_FINGER_REPORT_ENABLE_BIT 0
#define CTRL27_UDG_ENABLE_BIT 4
#define WAKEUP_GESTURE_MODE 0x02
static ssize_t udg_sysfs_engine_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t udg_sysfs_detection_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t udg_sysfs_detection_score_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_detection_index_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_registration_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t udg_sysfs_registration_begin_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t udg_sysfs_registration_status_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_template_size_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_template_max_index_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_template_detection_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_template_index_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t udg_sysfs_template_valid_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_template_valid_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t udg_sysfs_template_clear_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t udg_sysfs_trace_size_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_template_data_show(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count);
static ssize_t udg_sysfs_template_data_store(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count);
static ssize_t udg_sysfs_trace_data_show(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count);
static ssize_t udg_sysfs_template_displacement_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_template_displacement_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t udg_sysfs_rotation_invariance_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_rotation_invariance_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t udg_sysfs_scale_invariance_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_scale_invariance_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t udg_sysfs_threshold_factor_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_threshold_factor_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t udg_sysfs_match_metric_threshold_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_match_metric_threshold_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t udg_sysfs_max_inter_stroke_time_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t udg_sysfs_max_inter_stroke_time_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static int udg_read_tuning_params(void);
static int udg_write_tuning_params(void);
static int udg_detection_enable(bool enable);
static int udg_engine_enable(bool enable);
static int udg_set_index(unsigned char index);
#ifdef STORE_GESTURES
static int udg_read_valid_data(void);
static int udg_write_valid_data(void);
static int udg_read_template_data(unsigned char index);
static int udg_write_template_data(void);
#endif
enum gesture_type {
DETECTION = 0x0f,
REGISTRATION = 0x10,
};
struct udg_tuning {
union {
struct {
unsigned char maximum_number_of_templates;
unsigned char template_size;
unsigned char template_disp_lsb;
unsigned char template_disp_msb;
unsigned char rotation_inv_lsb;
unsigned char rotation_inv_msb;
unsigned char scale_inv_lsb;
unsigned char scale_inv_msb;
unsigned char thres_factor_lsb;
unsigned char thres_factor_msb;
unsigned char metric_thres_lsb;
unsigned char metric_thres_msb;
unsigned char inter_stroke_lsb;
unsigned char inter_stroke_msb;
} __packed;
unsigned char data[14];
};
};
struct udg_addr {
unsigned short data_4;
unsigned short ctrl_18;
unsigned short ctrl_20;
unsigned short ctrl_23;
unsigned short ctrl_27;
unsigned short ctrl_41;
unsigned short trace_x;
unsigned short trace_y;
unsigned short trace_segment;
unsigned short template_helper;
unsigned short template_data;
unsigned short template_flags;
};
struct synaptics_rmi4_f12_query_0 {
union {
struct {
struct {
unsigned char has_register_descriptors:1;
unsigned char has_closed_cover:1;
unsigned char has_fast_glove_detect:1;
unsigned char has_dribble:1;
unsigned char has_4p4_jitter_filter_strength:1;
unsigned char f12_query0_s0_b5__7:3;
} __packed;
struct {
unsigned char max_num_templates:4;
unsigned char f12_query0_s1_b4__7:4;
unsigned char template_size_lsb;
unsigned char template_size_msb;
} __packed;
};
unsigned char data[4];
};
};
struct synaptics_rmi4_f12_query_5 {
union {
struct {
unsigned char size_of_query6;
struct {
unsigned char ctrl0_is_present:1;
unsigned char ctrl1_is_present:1;
unsigned char ctrl2_is_present:1;
unsigned char ctrl3_is_present:1;
unsigned char ctrl4_is_present:1;
unsigned char ctrl5_is_present:1;
unsigned char ctrl6_is_present:1;
unsigned char ctrl7_is_present:1;
} __packed;
struct {
unsigned char ctrl8_is_present:1;
unsigned char ctrl9_is_present:1;
unsigned char ctrl10_is_present:1;
unsigned char ctrl11_is_present:1;
unsigned char ctrl12_is_present:1;
unsigned char ctrl13_is_present:1;
unsigned char ctrl14_is_present:1;
unsigned char ctrl15_is_present:1;
} __packed;
struct {
unsigned char ctrl16_is_present:1;
unsigned char ctrl17_is_present:1;
unsigned char ctrl18_is_present:1;
unsigned char ctrl19_is_present:1;
unsigned char ctrl20_is_present:1;
unsigned char ctrl21_is_present:1;
unsigned char ctrl22_is_present:1;
unsigned char ctrl23_is_present:1;
} __packed;
struct {
unsigned char ctrl24_is_present:1;
unsigned char ctrl25_is_present:1;
unsigned char ctrl26_is_present:1;
unsigned char ctrl27_is_present:1;
unsigned char ctrl28_is_present:1;
unsigned char ctrl29_is_present:1;
unsigned char ctrl30_is_present:1;
unsigned char ctrl31_is_present:1;
} __packed;
struct {
unsigned char ctrl32_is_present:1;
unsigned char ctrl33_is_present:1;
unsigned char ctrl34_is_present:1;
unsigned char ctrl35_is_present:1;
unsigned char ctrl36_is_present:1;
unsigned char ctrl37_is_present:1;
unsigned char ctrl38_is_present:1;
unsigned char ctrl39_is_present:1;
} __packed;
struct {
unsigned char ctrl40_is_present:1;
unsigned char ctrl41_is_present:1;
unsigned char ctrl42_is_present:1;
unsigned char ctrl43_is_present:1;
unsigned char ctrl44_is_present:1;
unsigned char ctrl45_is_present:1;
unsigned char ctrl46_is_present:1;
unsigned char ctrl47_is_present:1;
} __packed;
};
unsigned char data[7];
};
};
struct synaptics_rmi4_f12_query_8 {
union {
struct {
unsigned char size_of_query9;
struct {
unsigned char data0_is_present:1;
unsigned char data1_is_present:1;
unsigned char data2_is_present:1;
unsigned char data3_is_present:1;
unsigned char data4_is_present:1;
unsigned char data5_is_present:1;
unsigned char data6_is_present:1;
unsigned char data7_is_present:1;
} __packed;
struct {
unsigned char data8_is_present:1;
unsigned char data9_is_present:1;
unsigned char data10_is_present:1;
unsigned char data11_is_present:1;
unsigned char data12_is_present:1;
unsigned char data13_is_present:1;
unsigned char data14_is_present:1;
unsigned char data15_is_present:1;
} __packed;
struct {
unsigned char data16_is_present:1;
unsigned char data17_is_present:1;
unsigned char data18_is_present:1;
unsigned char data19_is_present:1;
unsigned char data20_is_present:1;
unsigned char data21_is_present:1;
unsigned char data22_is_present:1;
unsigned char data23_is_present:1;
} __packed;
};
unsigned char data[4];
};
};
struct synaptics_rmi4_f12_control_41 {
union {
struct {
unsigned char enable_registration:1;
unsigned char template_index:4;
unsigned char begin:1;
unsigned char f12_ctrl41_b6__7:2;
} __packed;
unsigned char data[1];
};
};
struct synaptics_rmi4_udg_handle {
atomic_t attn_event;
unsigned char intr_mask;
unsigned char report_flags;
unsigned char object_type_enable1;
unsigned char object_type_enable2;
unsigned char trace_size;
unsigned char template_index;
unsigned char max_num_templates;
unsigned char detection_score;
unsigned char detection_index;
unsigned char detection_status;
unsigned char registration_status;
unsigned char *ctrl_buf;
unsigned char *trace_data_buf;
unsigned char *template_data_buf;
#ifdef STORE_GESTURES
unsigned char gestures_to_store;
unsigned char *storage_buf;
unsigned char valid_buf[2];
#endif
unsigned short trace_data_buf_size;
unsigned short template_size;
unsigned short template_data_size;
unsigned short query_base_addr;
unsigned short control_base_addr;
unsigned short data_base_addr;
unsigned short command_base_addr;
unsigned short ctrl_18_sub10_off;
unsigned short ctrl_20_sub1_off;
unsigned short ctrl_23_sub3_off;
unsigned short ctrl_27_sub5_off;
struct input_dev *udg_dev;
struct kobject *tuning_dir;
struct udg_addr addr;
struct udg_tuning tuning;
struct synaptics_rmi4_data *rmi4_data;
};
static struct device_attribute attrs[] = {
__ATTR(engine_enable, 0220,
synaptics_rmi4_show_error,
udg_sysfs_engine_enable_store),
__ATTR(detection_enable, 0220,
synaptics_rmi4_show_error,
udg_sysfs_detection_enable_store),
__ATTR(detection_score, 0444,
udg_sysfs_detection_score_show,
synaptics_rmi4_store_error),
__ATTR(detection_index, 0444,
udg_sysfs_detection_index_show,
synaptics_rmi4_store_error),
__ATTR(registration_enable, 0220,
synaptics_rmi4_show_error,
udg_sysfs_registration_enable_store),
__ATTR(registration_begin, 0220,
synaptics_rmi4_show_error,
udg_sysfs_registration_begin_store),
__ATTR(registration_status, 0444,
udg_sysfs_registration_status_show,
synaptics_rmi4_store_error),
__ATTR(template_size, 0444,
udg_sysfs_template_size_show,
synaptics_rmi4_store_error),
__ATTR(template_max_index, 0444,
udg_sysfs_template_max_index_show,
synaptics_rmi4_store_error),
__ATTR(template_detection, 0444,
udg_sysfs_template_detection_show,
synaptics_rmi4_store_error),
__ATTR(template_index, 0220,
synaptics_rmi4_show_error,
udg_sysfs_template_index_store),
__ATTR(template_valid, 0664,
udg_sysfs_template_valid_show,
udg_sysfs_template_valid_store),
__ATTR(template_clear, 0220,
synaptics_rmi4_show_error,
udg_sysfs_template_clear_store),
__ATTR(trace_size, 0444,
udg_sysfs_trace_size_show,
synaptics_rmi4_store_error),
};
static struct bin_attribute template_data = {
.attr = {
.name = "template_data",
.mode = 0664,
},
.size = 0,
.read = udg_sysfs_template_data_show,
.write = udg_sysfs_template_data_store,
};
static struct bin_attribute trace_data = {
.attr = {
.name = "trace_data",
.mode = 0444,
},
.size = 0,
.read = udg_sysfs_trace_data_show,
.write = NULL,
};
static struct device_attribute params[] = {
__ATTR(template_displacement, 0664,
udg_sysfs_template_displacement_show,
udg_sysfs_template_displacement_store),
__ATTR(rotation_invariance, 0664,
udg_sysfs_rotation_invariance_show,
udg_sysfs_rotation_invariance_store),
__ATTR(scale_invariance, 0664,
udg_sysfs_scale_invariance_show,
udg_sysfs_scale_invariance_store),
__ATTR(threshold_factor, 0664,
udg_sysfs_threshold_factor_show,
udg_sysfs_threshold_factor_store),
__ATTR(match_metric_threshold, 0664,
udg_sysfs_match_metric_threshold_show,
udg_sysfs_match_metric_threshold_store),
__ATTR(max_inter_stroke_time, 0664,
udg_sysfs_max_inter_stroke_time_show,
udg_sysfs_max_inter_stroke_time_store),
};
static struct synaptics_rmi4_udg_handle *udg;
static unsigned char ctrl_18_sub_size[] = {10, 10, 10, 2, 3, 4, 3, 3, 1, 1};
static unsigned char ctrl_20_sub_size[] = {2};
static unsigned char ctrl_23_sub_size[] = {1, 1, 1};
static unsigned char ctrl_27_sub_size[] = {1, 5, 2, 1, 7};
DECLARE_COMPLETION(udg_remove_complete);
static ssize_t udg_sysfs_engine_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
bool enable;
unsigned int input;
if (kstrtouint(buf, 10, &input) != 1)
return -EINVAL;
if (input == 1)
enable = true;
else if (input == 0)
enable = false;
else
return -EINVAL;
retval = udg_engine_enable(enable);
if (retval < 0)
return retval;
return count;
}
static ssize_t udg_sysfs_detection_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
bool enable;
unsigned int input;
if (kstrtouint(buf, 10, &input) != 1)
return -EINVAL;
if (input == 1)
enable = true;
else if (input == 0)
enable = false;
else
return -EINVAL;
udg->detection_status = 0;
retval = udg_detection_enable(enable);
if (retval < 0)
return retval;
return count;
}
static ssize_t udg_sysfs_detection_score_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", udg->detection_score);
}
static ssize_t udg_sysfs_detection_index_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", udg->detection_index);
}
static ssize_t udg_sysfs_registration_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
bool enable;
unsigned int input;
struct synaptics_rmi4_f12_control_41 control_41;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
if (kstrtouint(buf, 10, &input) != 1)
return -EINVAL;
if (input == 1)
enable = true;
else if (input == 0)
enable = false;
else
return -EINVAL;
if (enable) {
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.ctrl_23,
udg->ctrl_buf,
udg->ctrl_23_sub3_off + 1);
if (retval < 0)
return retval;
udg->ctrl_buf[0] = 0;
udg->ctrl_buf[0] |= (1 << CTRL23_FINGER_REPORT_ENABLE_BIT);
if (udg->ctrl_23_sub3_off)
udg->ctrl_buf[udg->ctrl_23_sub3_off] = 0;
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.ctrl_23,
udg->ctrl_buf,
udg->ctrl_23_sub3_off + 1);
if (retval < 0)
return retval;
} else {
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.ctrl_23,
udg->ctrl_buf,
udg->ctrl_23_sub3_off + 1);
if (retval < 0)
return retval;
udg->ctrl_buf[0] = udg->object_type_enable1;
if (udg->ctrl_23_sub3_off) {
udg->ctrl_buf[udg->ctrl_23_sub3_off] =
udg->object_type_enable2;
}
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.ctrl_23,
udg->ctrl_buf,
udg->ctrl_23_sub3_off + 1);
if (retval < 0)
return retval;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.ctrl_41,
control_41.data,
sizeof(control_41.data));
if (retval < 0)
return retval;
control_41.enable_registration = enable ? 1 : 0;
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.ctrl_41,
control_41.data,
sizeof(control_41.data));
if (retval < 0)
return retval;
return count;
}
static ssize_t udg_sysfs_registration_begin_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
bool begin;
unsigned int input;
struct synaptics_rmi4_f12_control_41 control_41;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
if (kstrtouint(buf, 10, &input) != 1)
return -EINVAL;
if (input == 1)
begin = true;
else if (input == 0)
begin = false;
else
return -EINVAL;
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.ctrl_41,
control_41.data,
sizeof(control_41.data));
if (retval < 0)
return retval;
control_41.begin = begin ? 1 : 0;
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.ctrl_41,
control_41.data,
sizeof(control_41.data));
if (retval < 0)
return retval;
return count;
}
static ssize_t udg_sysfs_registration_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "0x%02x\n", udg->registration_status);
}
static ssize_t udg_sysfs_template_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", udg->template_size);
}
static ssize_t udg_sysfs_template_max_index_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", udg->max_num_templates - 1);
}
static ssize_t udg_sysfs_template_detection_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int retval;
int attn_event;
unsigned char detection_status;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
attn_event = atomic_read(&udg->attn_event);
atomic_set(&udg->attn_event, 0);
if (attn_event == 0)
return snprintf(buf, PAGE_SIZE, "0\n");
if (udg->detection_status == 0) {
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.data_4,
rmi4_data->gesture_detection,
sizeof(rmi4_data->gesture_detection));
if (retval < 0)
return retval;
udg->detection_status = rmi4_data->gesture_detection[0];
}
detection_status = udg->detection_status;
udg->detection_status = 0;
switch (detection_status) {
case DETECTION:
udg->detection_score = rmi4_data->gesture_detection[1];
udg->detection_index = rmi4_data->gesture_detection[4];
udg->trace_size = rmi4_data->gesture_detection[3];
break;
case REGISTRATION:
udg->registration_status = rmi4_data->gesture_detection[1];
udg->trace_size = rmi4_data->gesture_detection[3];
break;
default:
return snprintf(buf, PAGE_SIZE, "0\n");
}
return snprintf(buf, PAGE_SIZE, "0x%02x\n", detection_status);
}
static ssize_t udg_sysfs_template_index_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned long index;
retval = sstrtoul(buf, 10, &index);
if (retval)
return retval;
retval = udg_set_index((unsigned char)index);
if (retval < 0)
return retval;
return count;
}
static ssize_t udg_sysfs_template_valid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int retval;
unsigned char valid;
unsigned char offset;
unsigned char byte_num;
unsigned char template_flags[2];
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
byte_num = udg->template_index / 8;
offset = udg->template_index % 8;
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.template_flags,
template_flags,
sizeof(template_flags));
if (retval < 0)
return retval;
valid = (template_flags[byte_num] & (1 << offset)) >> offset;
return snprintf(buf, PAGE_SIZE, "%u\n", valid);
}
static ssize_t udg_sysfs_template_valid_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned long valid;
unsigned char offset;
unsigned char byte_num;
unsigned char template_flags[2];
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
retval = sstrtoul(buf, 10, &valid);
if (retval)
return retval;
if (valid > 0)
valid = 1;
byte_num = udg->template_index / 8;
offset = udg->template_index % 8;
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.template_flags,
template_flags,
sizeof(template_flags));
if (retval < 0)
return retval;
if (valid)
template_flags[byte_num] |= (1 << offset);
else
template_flags[byte_num] &= ~(1 << offset);
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.template_flags,
template_flags,
sizeof(template_flags));
if (retval < 0)
return retval;
#ifdef STORE_GESTURES
udg_read_valid_data();
#endif
return count;
}
static ssize_t udg_sysfs_template_clear_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned int input;
const char cmd[] = {'0', 0};
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
if (kstrtouint(buf, 10, &input) != 1)
return -EINVAL;
if (input != 1)
return -EINVAL;
memset(udg->template_data_buf, 0x00, udg->template_data_size);
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.template_data,
udg->template_data_buf,
udg->template_data_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to clear template data\n",
__func__);
return retval;
}
retval = udg_sysfs_template_valid_store(dev, attr, cmd, 1);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to clear valid bit\n",
__func__);
return retval;
}
#ifdef STORE_GESTURES
udg_read_template_data(udg->template_index);
udg_read_valid_data();
#endif
return count;
}
static ssize_t udg_sysfs_trace_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", udg->trace_size);
}
static ssize_t udg_sysfs_trace_data_show(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count)
{
int retval;
unsigned short index = 0;
unsigned short trace_data_size;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
trace_data_size = udg->trace_size * 5;
if (trace_data_size == 0)
return -EINVAL;
if (count < trace_data_size) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Not enough space (%d bytes) in buffer\n",
__func__, (unsigned int)count);
return -EINVAL;
}
if (udg->trace_data_buf_size < trace_data_size) {
if (udg->trace_data_buf_size)
kfree(udg->trace_data_buf);
udg->trace_data_buf = kzalloc(trace_data_size, GFP_KERNEL);
if (!udg->trace_data_buf) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for trace data buffer\n",
__func__);
udg->trace_data_buf_size = 0;
return -ENOMEM;
}
udg->trace_data_buf_size = trace_data_size;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.trace_x,
&udg->trace_data_buf[index],
udg->trace_size * 2);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read trace X data\n",
__func__);
return retval;
} else {
index += udg->trace_size * 2;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.trace_y,
&udg->trace_data_buf[index],
udg->trace_size * 2);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read trace Y data\n",
__func__);
return retval;
} else {
index += udg->trace_size * 2;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.trace_segment,
&udg->trace_data_buf[index],
udg->trace_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read trace segment data\n",
__func__);
return retval;
}
retval = secure_memcpy(buf, count, udg->trace_data_buf,
udg->trace_data_buf_size, trace_data_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy trace data\n",
__func__);
return retval;
}
return trace_data_size;
}
static ssize_t udg_sysfs_template_data_show(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
if (count < udg->template_data_size) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Not enough space (%d bytes) in buffer\n",
__func__, (unsigned int)count);
return -EINVAL;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.template_data,
udg->template_data_buf,
udg->template_data_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read template data\n",
__func__);
return retval;
}
retval = secure_memcpy(buf, count, udg->template_data_buf,
udg->template_data_size, udg->template_data_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy template data\n",
__func__);
return retval;
}
#ifdef STORE_GESTURES
udg_read_template_data(udg->template_index);
udg_read_valid_data();
#endif
return udg->template_data_size;
}
static ssize_t udg_sysfs_template_data_store(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
retval = secure_memcpy(udg->template_data_buf, udg->template_data_size,
buf, count, count);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to copy template data\n",
__func__);
return retval;
}
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.template_data,
udg->template_data_buf,
count);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write template data\n",
__func__);
return retval;
}
#ifdef STORE_GESTURES
udg_read_template_data(udg->template_index);
udg_read_valid_data();
#endif
return count;
}
static ssize_t udg_sysfs_template_displacement_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int retval;
unsigned short template_displacement;
retval = udg_read_tuning_params();
if (retval < 0)
return retval;
template_displacement =
((unsigned short)udg->tuning.template_disp_lsb << 0) |
((unsigned short)udg->tuning.template_disp_msb << 8);
return snprintf(buf, PAGE_SIZE, "%u\n", template_displacement);
}
static ssize_t udg_sysfs_template_displacement_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned long input;
retval = sstrtoul(buf, 10, &input);
if (retval)
return retval;
retval = udg_read_tuning_params();
if (retval < 0)
return retval;
udg->tuning.template_disp_lsb = (unsigned char)(input >> 0);
udg->tuning.template_disp_msb = (unsigned char)(input >> 8);
retval = udg_write_tuning_params();
if (retval < 0)
return retval;
return count;
}
static ssize_t udg_sysfs_rotation_invariance_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int retval;
unsigned short rotation_invariance;
retval = udg_read_tuning_params();
if (retval < 0)
return retval;
rotation_invariance =
((unsigned short)udg->tuning.rotation_inv_lsb << 0) |
((unsigned short)udg->tuning.rotation_inv_msb << 8);
return snprintf(buf, PAGE_SIZE, "%u\n", rotation_invariance);
}
static ssize_t udg_sysfs_rotation_invariance_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned long input;
retval = sstrtoul(buf, 10, &input);
if (retval)
return retval;
retval = udg_read_tuning_params();
if (retval < 0)
return retval;
udg->tuning.rotation_inv_lsb = (unsigned char)(input >> 0);
udg->tuning.rotation_inv_msb = (unsigned char)(input >> 8);
retval = udg_write_tuning_params();
if (retval < 0)
return retval;
return count;
}
static ssize_t udg_sysfs_scale_invariance_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int retval;
unsigned short scale_invariance;
retval = udg_read_tuning_params();
if (retval < 0)
return retval;
scale_invariance =
((unsigned short)udg->tuning.scale_inv_lsb << 0) |
((unsigned short)udg->tuning.scale_inv_msb << 8);
return snprintf(buf, PAGE_SIZE, "%u\n", scale_invariance);
}
static ssize_t udg_sysfs_scale_invariance_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned long input;
retval = sstrtoul(buf, 10, &input);
if (retval)
return retval;
retval = udg_read_tuning_params();
if (retval < 0)
return retval;
udg->tuning.scale_inv_lsb = (unsigned char)(input >> 0);
udg->tuning.scale_inv_msb = (unsigned char)(input >> 8);
retval = udg_write_tuning_params();
if (retval < 0)
return retval;
return count;
}
static ssize_t udg_sysfs_threshold_factor_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int retval;
unsigned short threshold_factor;
retval = udg_read_tuning_params();
if (retval < 0)
return retval;
threshold_factor =
((unsigned short)udg->tuning.thres_factor_lsb << 0) |
((unsigned short)udg->tuning.thres_factor_msb << 8);
return snprintf(buf, PAGE_SIZE, "%u\n", threshold_factor);
}
static ssize_t udg_sysfs_threshold_factor_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned long input;
retval = sstrtoul(buf, 10, &input);
if (retval)
return retval;
retval = udg_read_tuning_params();
if (retval < 0)
return retval;
udg->tuning.thres_factor_lsb = (unsigned char)(input >> 0);
udg->tuning.thres_factor_msb = (unsigned char)(input >> 8);
retval = udg_write_tuning_params();
if (retval < 0)
return retval;
return count;
}
static ssize_t udg_sysfs_match_metric_threshold_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int retval;
unsigned short match_metric_threshold;
retval = udg_read_tuning_params();
if (retval < 0)
return retval;
match_metric_threshold =
((unsigned short)udg->tuning.metric_thres_lsb << 0) |
((unsigned short)udg->tuning.metric_thres_msb << 8);
return snprintf(buf, PAGE_SIZE, "%u\n", match_metric_threshold);
}
static ssize_t udg_sysfs_match_metric_threshold_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned long input;
retval = sstrtoul(buf, 10, &input);
if (retval)
return retval;
retval = udg_read_tuning_params();
if (retval < 0)
return retval;
udg->tuning.metric_thres_lsb = (unsigned char)(input >> 0);
udg->tuning.metric_thres_msb = (unsigned char)(input >> 8);
retval = udg_write_tuning_params();
if (retval < 0)
return retval;
return count;
}
static ssize_t udg_sysfs_max_inter_stroke_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int retval;
unsigned short max_inter_stroke_time;
retval = udg_read_tuning_params();
if (retval < 0)
return retval;
max_inter_stroke_time =
((unsigned short)udg->tuning.inter_stroke_lsb << 0) |
((unsigned short)udg->tuning.inter_stroke_msb << 8);
return snprintf(buf, PAGE_SIZE, "%u\n", max_inter_stroke_time);
}
static ssize_t udg_sysfs_max_inter_stroke_time_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned long input;
retval = sstrtoul(buf, 10, &input);
if (retval)
return retval;
retval = udg_read_tuning_params();
if (retval < 0)
return retval;
udg->tuning.inter_stroke_lsb = (unsigned char)(input >> 0);
udg->tuning.inter_stroke_msb = (unsigned char)(input >> 8);
retval = udg_write_tuning_params();
if (retval < 0)
return retval;
return count;
}
static int udg_ctrl_subpacket(unsigned char ctrlreg,
unsigned char subpacket,
struct synaptics_rmi4_f12_query_5 *query_5)
{
int retval;
unsigned char cnt;
unsigned char regnum;
unsigned char bitnum;
unsigned char q5_index;
unsigned char q6_index;
unsigned char offset;
unsigned char max_ctrlreg;
unsigned char *query_6;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
max_ctrlreg = (sizeof(query_5->data) - 1) * 8 - 1;
if (ctrlreg > max_ctrlreg) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Control register number (%d) over limit\n",
__func__, ctrlreg);
return -EINVAL;
}
q5_index = ctrlreg / 8 + 1;
bitnum = ctrlreg % 8;
if ((query_5->data[q5_index] & (1 << bitnum)) == 0x00) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Control %d is not present\n",
__func__, ctrlreg);
return -EINVAL;
}
query_6 = kmalloc(query_5->size_of_query6, GFP_KERNEL);
if (!query_6) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for query 6\n",
__func__);
return -ENOMEM;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->query_base_addr + 6,
query_6,
query_5->size_of_query6);
if (retval < 0)
goto exit;
q6_index = 0;
for (regnum = 0; regnum < ctrlreg; regnum++) {
q5_index = regnum / 8 + 1;
bitnum = regnum % 8;
if ((query_5->data[q5_index] & (1 << bitnum)) == 0x00)
continue;
if (query_6[q6_index] == 0x00)
q6_index += 3;
else
q6_index++;
while (query_6[q6_index] & ~MASK_7BIT)
q6_index++;
q6_index++;
}
cnt = 0;
q6_index++;
offset = subpacket / 7;
bitnum = subpacket % 7;
do {
if (cnt == offset) {
if (query_6[q6_index + cnt] & (1 << bitnum))
retval = 1;
else
retval = 0;
goto exit;
}
cnt++;
} while (query_6[q6_index + cnt - 1] & ~MASK_7BIT);
retval = 0;
exit:
kfree(query_6);
return retval;
}
static int udg_read_tuning_params(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.ctrl_18,
udg->ctrl_buf,
udg->ctrl_18_sub10_off + sizeof(struct udg_tuning));
if (retval < 0)
return retval;
secure_memcpy(udg->tuning.data,
sizeof(udg->tuning.data),
(unsigned char *)&udg->ctrl_buf[udg->ctrl_18_sub10_off],
sizeof(struct udg_tuning),
sizeof(struct udg_tuning));
return 0;
}
static int udg_write_tuning_params(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
secure_memcpy((unsigned char *)&udg->ctrl_buf[udg->ctrl_18_sub10_off],
sizeof(struct udg_tuning),
udg->tuning.data,
sizeof(udg->tuning.data),
sizeof(struct udg_tuning));
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.ctrl_18,
udg->ctrl_buf,
udg->ctrl_18_sub10_off + sizeof(struct udg_tuning));
if (retval < 0)
return retval;
return 0;
}
static int udg_detection_enable(bool enable)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.ctrl_20,
udg->ctrl_buf,
udg->ctrl_20_sub1_off + 1);
if (retval < 0)
return retval;
if (enable)
udg->ctrl_buf[udg->ctrl_20_sub1_off] = WAKEUP_GESTURE_MODE;
else
udg->ctrl_buf[udg->ctrl_20_sub1_off] = udg->report_flags;
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.ctrl_20,
udg->ctrl_buf,
udg->ctrl_20_sub1_off + 1);
if (retval < 0)
return retval;
return 0;
}
static int udg_engine_enable(bool enable)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
if (enable) {
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.ctrl_27,
udg->ctrl_buf,
udg->ctrl_27_sub5_off + 1);
if (retval < 0)
return retval;
udg->ctrl_buf[udg->ctrl_27_sub5_off] |=
(1 << CTRL27_UDG_ENABLE_BIT);
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.ctrl_27,
udg->ctrl_buf,
udg->ctrl_27_sub5_off + 1);
if (retval < 0)
return retval;
} else {
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.ctrl_27,
udg->ctrl_buf,
udg->ctrl_27_sub5_off + 1);
if (retval < 0)
return retval;
udg->ctrl_buf[udg->ctrl_27_sub5_off] &=
~(1 << CTRL27_UDG_ENABLE_BIT);
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.ctrl_27,
udg->ctrl_buf,
udg->ctrl_27_sub5_off + 1);
if (retval < 0)
return retval;
}
return 0;
}
static void udg_report(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
atomic_set(&udg->attn_event, 1);
if (rmi4_data->suspend) {
if (rmi4_data->gesture_detection[0] == 0) {
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.data_4,
rmi4_data->gesture_detection,
sizeof(rmi4_data->gesture_detection));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read gesture detection\n",
__func__);
return;
}
}
udg->detection_status = rmi4_data->gesture_detection[0];
rmi4_data->gesture_detection[0] = 0;
if (udg->detection_status == DETECTION) {
input_report_key(udg->udg_dev, KEY_WAKEUP, 1);
input_sync(udg->udg_dev);
input_report_key(udg->udg_dev, KEY_WAKEUP, 0);
input_sync(udg->udg_dev);
rmi4_data->suspend = false;
}
}
return;
}
static int udg_set_index(unsigned char index)
{
int retval;
struct synaptics_rmi4_f12_control_41 control_41;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
if (index >= udg->max_num_templates)
return -EINVAL;
udg->template_index = index;
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.ctrl_41,
control_41.data,
sizeof(control_41.data));
if (retval < 0)
return retval;
control_41.template_index = udg->template_index;
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.ctrl_41,
control_41.data,
sizeof(control_41.data));
if (retval < 0)
return retval;
return 0;
}
#ifdef STORE_GESTURES
static int udg_read_valid_data(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.template_flags,
udg->valid_buf,
sizeof(udg->valid_buf));
if (retval < 0)
return retval;
return 0;
}
static int udg_write_valid_data(void)
{
int retval;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.template_flags,
udg->valid_buf,
sizeof(udg->valid_buf));
if (retval < 0)
return retval;
return 0;
}
static int udg_read_template_data(unsigned char index)
{
int retval;
unsigned char *storage;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
udg_set_index(index);
storage = &(udg->storage_buf[index * udg->template_data_size]);
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.template_data,
storage,
udg->template_data_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read template data\n",
__func__);
return retval;
}
return 0;
}
static int udg_write_template_data(void)
{
int retval;
unsigned char ii;
unsigned char *storage;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
for (ii = 0; ii < udg->gestures_to_store; ii++) {
udg_set_index(ii);
storage = &(udg->storage_buf[ii * udg->template_data_size]);
retval = synaptics_rmi4_reg_write(rmi4_data,
udg->addr.template_data,
storage,
udg->template_data_size);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write template data\n",
__func__);
return retval;
}
}
return 0;
}
#endif
static int udg_reg_init(void)
{
int retval;
unsigned char ii;
unsigned char data_offset;
unsigned char size_of_query;
unsigned char ctrl_18_offset;
unsigned char ctrl_20_offset;
unsigned char ctrl_23_offset;
unsigned char ctrl_27_offset;
unsigned char ctrl_41_offset;
struct synaptics_rmi4_f12_query_0 query_0;
struct synaptics_rmi4_f12_query_5 query_5;
struct synaptics_rmi4_f12_query_8 query_8;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->query_base_addr + 7,
&size_of_query,
sizeof(size_of_query));
if (retval < 0)
return retval;
if (size_of_query < 4) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: User defined gesture support unavailable (missing data registers)\n",
__func__);
retval = -ENODEV;
return retval;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->query_base_addr + 8,
query_8.data,
sizeof(query_8.data));
if (retval < 0)
return retval;
if ((query_8.data16_is_present) &&
(query_8.data17_is_present) &&
(query_8.data18_is_present) &&
(query_8.data19_is_present) &&
(query_8.data20_is_present) &&
(query_8.data21_is_present)) {
data_offset = query_8.data0_is_present +
query_8.data1_is_present +
query_8.data2_is_present +
query_8.data3_is_present;
udg->addr.data_4 = udg->data_base_addr + data_offset;
data_offset = data_offset +
query_8.data4_is_present +
query_8.data5_is_present +
query_8.data6_is_present +
query_8.data7_is_present +
query_8.data8_is_present +
query_8.data9_is_present +
query_8.data10_is_present +
query_8.data11_is_present +
query_8.data12_is_present +
query_8.data13_is_present +
query_8.data14_is_present +
query_8.data15_is_present;
udg->addr.trace_x = udg->data_base_addr + data_offset;
udg->addr.trace_y = udg->addr.trace_x + 1;
udg->addr.trace_segment = udg->addr.trace_y + 1;
udg->addr.template_helper = udg->addr.trace_segment + 1;
udg->addr.template_data = udg->addr.template_helper + 1;
udg->addr.template_flags = udg->addr.template_data + 1;
} else {
dev_err(rmi4_data->pdev->dev.parent,
"%s: User defined gesture support unavailable (missing data registers)\n",
__func__);
retval = -ENODEV;
return retval;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->query_base_addr + 4,
&size_of_query,
sizeof(size_of_query));
if (retval < 0)
return retval;
if (size_of_query < 7) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: User defined gesture support unavailable (missing control registers)\n",
__func__);
retval = -ENODEV;
return retval;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->query_base_addr + 5,
query_5.data,
sizeof(query_5.data));
if (retval < 0)
return retval;
ctrl_18_offset = query_5.ctrl0_is_present +
query_5.ctrl1_is_present +
query_5.ctrl2_is_present +
query_5.ctrl3_is_present +
query_5.ctrl4_is_present +
query_5.ctrl5_is_present +
query_5.ctrl6_is_present +
query_5.ctrl7_is_present +
query_5.ctrl8_is_present +
query_5.ctrl9_is_present +
query_5.ctrl10_is_present +
query_5.ctrl11_is_present +
query_5.ctrl12_is_present +
query_5.ctrl13_is_present +
query_5.ctrl14_is_present +
query_5.ctrl15_is_present +
query_5.ctrl16_is_present +
query_5.ctrl17_is_present;
ctrl_20_offset = ctrl_18_offset +
query_5.ctrl18_is_present +
query_5.ctrl19_is_present;
ctrl_23_offset = ctrl_20_offset +
query_5.ctrl20_is_present +
query_5.ctrl21_is_present +
query_5.ctrl22_is_present;
ctrl_27_offset = ctrl_23_offset+
query_5.ctrl23_is_present +
query_5.ctrl24_is_present +
query_5.ctrl25_is_present +
query_5.ctrl26_is_present;
ctrl_41_offset = ctrl_27_offset+
query_5.ctrl27_is_present +
query_5.ctrl28_is_present +
query_5.ctrl29_is_present +
query_5.ctrl30_is_present +
query_5.ctrl31_is_present +
query_5.ctrl32_is_present +
query_5.ctrl33_is_present +
query_5.ctrl34_is_present +
query_5.ctrl35_is_present +
query_5.ctrl36_is_present +
query_5.ctrl37_is_present +
query_5.ctrl38_is_present +
query_5.ctrl39_is_present +
query_5.ctrl40_is_present;
udg->addr.ctrl_18 = udg->control_base_addr + ctrl_18_offset;
udg->addr.ctrl_20 = udg->control_base_addr + ctrl_20_offset;
udg->addr.ctrl_23 = udg->control_base_addr + ctrl_23_offset;
udg->addr.ctrl_27 = udg->control_base_addr + ctrl_27_offset;
udg->addr.ctrl_41 = udg->control_base_addr + ctrl_41_offset;
udg->ctrl_18_sub10_off = 0;
for (ii = 0; ii < 10; ii++) {
retval = udg_ctrl_subpacket(18, ii, &query_5);
if (retval == 1)
udg->ctrl_18_sub10_off += ctrl_18_sub_size[ii];
else if (retval < 0)
return retval;
}
udg->ctrl_20_sub1_off = 0;
for (ii = 0; ii < 1; ii++) {
retval = udg_ctrl_subpacket(20, ii, &query_5);
if (retval == 1)
udg->ctrl_20_sub1_off += ctrl_20_sub_size[ii];
else if (retval < 0)
return retval;
}
udg->ctrl_23_sub3_off = 0;
for (ii = 0; ii < 3; ii++) {
retval = udg_ctrl_subpacket(23, ii, &query_5);
if (retval == 1)
udg->ctrl_23_sub3_off += ctrl_23_sub_size[ii];
else if (retval < 0)
return retval;
}
retval = udg_ctrl_subpacket(23, 3, &query_5);
if (retval == 0)
udg->ctrl_23_sub3_off = 0;
else if (retval < 0)
return retval;
udg->ctrl_27_sub5_off = 0;
for (ii = 0; ii < 5; ii++) {
retval = udg_ctrl_subpacket(27, ii, &query_5);
if (retval == 1)
udg->ctrl_27_sub5_off += ctrl_27_sub_size[ii];
else if (retval < 0)
return retval;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->query_base_addr + 0,
query_0.data,
sizeof(query_0.data));
if (retval < 0)
return retval;
udg->max_num_templates = query_0.max_num_templates;
udg->template_size =
((unsigned short)query_0.template_size_lsb << 0) |
((unsigned short)query_0.template_size_msb << 8);
udg->template_data_size = udg->template_size * 4 * 2 + 4 + 1;
#ifdef STORE_GESTURES
udg->gestures_to_store = udg->max_num_templates;
if (GESTURES_TO_STORE < udg->gestures_to_store)
udg->gestures_to_store = GESTURES_TO_STORE;
#endif
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.ctrl_20,
udg->ctrl_buf,
udg->ctrl_20_sub1_off + 1);
if (retval < 0)
return retval;
udg->report_flags = udg->ctrl_buf[udg->ctrl_20_sub1_off];
retval = synaptics_rmi4_reg_read(rmi4_data,
udg->addr.ctrl_23,
udg->ctrl_buf,
udg->ctrl_23_sub3_off + 1);
if (retval < 0)
return retval;
udg->object_type_enable1 = udg->ctrl_buf[0];
if (udg->ctrl_23_sub3_off)
udg->object_type_enable2 = udg->ctrl_buf[udg->ctrl_23_sub3_off];
return retval;
}
static int udg_scan_pdt(void)
{
int retval;
unsigned char ii;
unsigned char page;
unsigned char intr_count = 0;
unsigned char intr_off;
unsigned char intr_src;
unsigned short addr;
struct synaptics_rmi4_fn_desc fd;
struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
for (page = 0; page < PAGES_TO_SERVICE; page++) {
for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
addr |= (page << 8);
retval = synaptics_rmi4_reg_read(rmi4_data,
addr,
(unsigned char *)&fd,
sizeof(fd));
if (retval < 0)
return retval;
addr &= ~(MASK_8BIT << 8);
if (fd.fn_number) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Found F%02x\n",
__func__, fd.fn_number);
switch (fd.fn_number) {
case SYNAPTICS_RMI4_F12:
goto f12_found;
break;
}
} else {
break;
}
intr_count += fd.intr_src_count;
}
}
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to find F12\n",
__func__);
return -EINVAL;
f12_found:
udg->query_base_addr = fd.query_base_addr | (page << 8);
udg->control_base_addr = fd.ctrl_base_addr | (page << 8);
udg->data_base_addr = fd.data_base_addr | (page << 8);
udg->command_base_addr = fd.cmd_base_addr | (page << 8);
retval = udg_reg_init();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to initialize user defined gesture registers\n",
__func__);
return retval;
}
udg->intr_mask = 0;
intr_src = fd.intr_src_count;
intr_off = intr_count % 8;
for (ii = intr_off;
ii < (intr_src + intr_off);
ii++) {
udg->intr_mask |= 1 << ii;
}
rmi4_data->intr_mask[0] |= udg->intr_mask;
addr = rmi4_data->f01_ctrl_base_addr + 1;
retval = synaptics_rmi4_reg_write(rmi4_data,
addr,
&rmi4_data->intr_mask[0],
sizeof(rmi4_data->intr_mask[0]));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to set interrupt enable bit\n",
__func__);
return retval;
}
return 0;
}
static void synaptics_rmi4_udg_attn(struct synaptics_rmi4_data *rmi4_data,
unsigned char intr_mask)
{
if (!udg)
return;
if (udg->intr_mask & intr_mask)
udg_report();
return;
}
static int synaptics_rmi4_udg_init(struct synaptics_rmi4_data *rmi4_data)
{
int retval;
unsigned char ii;
unsigned char size;
unsigned char attr_count;
unsigned char param_count;
if (udg) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Handle already exists\n",
__func__);
return 0;
}
udg = kzalloc(sizeof(*udg), GFP_KERNEL);
if (!udg) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for udg\n",
__func__);
retval = -ENOMEM;
goto exit;
}
size = 0;
for (ii = 0; ii < sizeof(ctrl_18_sub_size); ii++)
size += ctrl_18_sub_size[ii];
size += sizeof(struct udg_tuning);
udg->ctrl_buf = kzalloc(size, GFP_KERNEL);
if (!udg->ctrl_buf) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for ctrl_buf\n",
__func__);
retval = -ENOMEM;
goto exit_free_udg;
}
udg->rmi4_data = rmi4_data;
retval = udg_scan_pdt();
if (retval < 0)
goto exit_free_ctrl_buf;
udg->template_data_buf = kzalloc(udg->template_data_size, GFP_KERNEL);
if (!udg->template_data_buf) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for template_data_buf\n",
__func__);
retval = -ENOMEM;
goto exit_free_ctrl_buf;
}
#ifdef STORE_GESTURES
udg->storage_buf = kzalloc(
udg->template_data_size * udg->gestures_to_store,
GFP_KERNEL);
if (!udg->storage_buf) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to alloc mem for storage_buf\n",
__func__);
kfree(udg->template_data_buf);
retval = -ENOMEM;
goto exit_free_ctrl_buf;
}
#endif
udg->udg_dev = input_allocate_device();
if (udg->udg_dev == NULL) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to allocate gesture device\n",
__func__);
retval = -ENOMEM;
goto exit_free_template_data_buf;
}
udg->udg_dev->name = GESTURE_DRIVER_NAME;
udg->udg_dev->phys = GESTURE_PHYS_NAME;
udg->udg_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
udg->udg_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
udg->udg_dev->dev.parent = rmi4_data->pdev->dev.parent;
input_set_drvdata(udg->udg_dev, rmi4_data);
set_bit(EV_KEY, udg->udg_dev->evbit);
set_bit(KEY_WAKEUP, udg->udg_dev->keybit);
input_set_capability(udg->udg_dev, EV_KEY, KEY_WAKEUP);
retval = input_register_device(udg->udg_dev);
if (retval) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to register gesture device\n",
__func__);
input_free_device(udg->udg_dev);
goto exit_free_template_data_buf;
}
udg->tuning_dir = kobject_create_and_add(TUNING_SYSFS_DIR_NAME,
&udg->udg_dev->dev.kobj);
if (!udg->tuning_dir) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to create tuning sysfs directory\n",
__func__);
goto exit_unregister_input_device;
}
retval = sysfs_create_bin_file(&udg->udg_dev->dev.kobj, &template_data);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to create template data bin file\n",
__func__);
goto exit_remove_sysfs_directory;
}
retval = sysfs_create_bin_file(&udg->udg_dev->dev.kobj, &trace_data);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to create trace data bin file\n",
__func__);
goto exit_remove_bin_file;
}
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
retval = sysfs_create_file(&udg->udg_dev->dev.kobj,
&attrs[attr_count].attr);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to create sysfs attributes\n",
__func__);
retval = -ENODEV;
goto exit_remove_attrs;
}
}
for (param_count = 0; param_count < ARRAY_SIZE(params); param_count++) {
retval = sysfs_create_file(udg->tuning_dir,
&params[param_count].attr);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to create tuning parameters\n",
__func__);
retval = -ENODEV;
goto exit_remove_params;
}
}
retval = udg_engine_enable(true);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to enable gesture engine\n",
__func__);
goto exit_remove_params;
}
return 0;
exit_remove_params:
for (param_count--; param_count >= 0; param_count--) {
sysfs_remove_file(udg->tuning_dir,
&params[param_count].attr);
}
exit_remove_attrs:
for (attr_count--; attr_count >= 0; attr_count--) {
sysfs_remove_file(&udg->udg_dev->dev.kobj,
&attrs[attr_count].attr);
}
sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &trace_data);
exit_remove_bin_file:
sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &template_data);
exit_remove_sysfs_directory:
kobject_put(udg->tuning_dir);
exit_unregister_input_device:
input_unregister_device(udg->udg_dev);
exit_free_template_data_buf:
#ifdef STORE_GESTURES
kfree(udg->storage_buf);
#endif
kfree(udg->template_data_buf);
exit_free_ctrl_buf:
kfree(udg->ctrl_buf);
exit_free_udg:
kfree(udg);
udg = NULL;
exit:
return retval;
}
static void synaptics_rmi4_udg_remove(struct synaptics_rmi4_data *rmi4_data)
{
unsigned char count;
if (!udg)
goto exit;
for (count = 0; count < ARRAY_SIZE(params); count++) {
sysfs_remove_file(udg->tuning_dir,
&params[count].attr);
}
for (count = 0; count < ARRAY_SIZE(attrs); count++) {
sysfs_remove_file(&udg->udg_dev->dev.kobj,
&attrs[count].attr);
}
sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &trace_data);
sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &template_data);
kobject_put(udg->tuning_dir);
input_unregister_device(udg->udg_dev);
#ifdef STORE_GESTURES
kfree(udg->storage_buf);
#endif
kfree(udg->template_data_buf);
kfree(udg->trace_data_buf);
kfree(udg->ctrl_buf);
kfree(udg);
udg = NULL;
exit:
complete(&udg_remove_complete);
return;
}
static void synaptics_rmi4_udg_reset(struct synaptics_rmi4_data *rmi4_data)
{
if (!udg) {
synaptics_rmi4_udg_init(rmi4_data);
return;
}
udg_scan_pdt();
udg_engine_enable(true);
#ifdef STORE_GESTURES
udg_write_template_data();
udg_write_valid_data();
#endif
return;
}
static void synaptics_rmi4_udg_reinit(struct synaptics_rmi4_data *rmi4_data)
{
if (!udg)
return;
udg_engine_enable(true);
#ifdef STORE_GESTURES
udg_write_template_data();
udg_write_valid_data();
#endif
return;
}
static void synaptics_rmi4_udg_e_suspend(struct synaptics_rmi4_data *rmi4_data)
{
if (!udg)
return;
rmi4_data->sleep_enable(rmi4_data, false);
rmi4_data->irq_enable(rmi4_data, true, false);
enable_irq_wake(rmi4_data->irq);
udg_engine_enable(true);
udg_detection_enable(true);
return;
}
static void synaptics_rmi4_udg_suspend(struct synaptics_rmi4_data *rmi4_data)
{
if (!udg)
return;
rmi4_data->sleep_enable(rmi4_data, false);
rmi4_data->irq_enable(rmi4_data, true, false);
enable_irq_wake(rmi4_data->irq);
udg_engine_enable(true);
udg_detection_enable(true);
return;
}
static void synaptics_rmi4_udg_resume(struct synaptics_rmi4_data *rmi4_data)
{
if (!udg)
return;
disable_irq_wake(rmi4_data->irq);
udg_detection_enable(false);
return;
}
static void synaptics_rmi4_udg_l_resume(struct synaptics_rmi4_data *rmi4_data)
{
if (!udg)
return;
disable_irq_wake(rmi4_data->irq);
udg_detection_enable(false);
return;
}
static struct synaptics_rmi4_exp_fn gesture_module = {
.fn_type = RMI_GESTURE,
.init = synaptics_rmi4_udg_init,
.remove = synaptics_rmi4_udg_remove,
.reset = synaptics_rmi4_udg_reset,
.reinit = synaptics_rmi4_udg_reinit,
.early_suspend = synaptics_rmi4_udg_e_suspend,
.suspend = synaptics_rmi4_udg_suspend,
.resume = synaptics_rmi4_udg_resume,
.late_resume = synaptics_rmi4_udg_l_resume,
.attn = synaptics_rmi4_udg_attn,
};
static int __init rmi4_gesture_module_init(void)
{
synaptics_rmi4_new_function(&gesture_module, true);
return 0;
}
static void __exit rmi4_gesture_module_exit(void)
{
synaptics_rmi4_new_function(&gesture_module, false);
wait_for_completion(&udg_remove_complete);
return;
}
module_init(rmi4_gesture_module_init);
module_exit(rmi4_gesture_module_exit);
MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX User Defined Gesture Module");
MODULE_LICENSE("GPL v2");