[PATCH] mouse: Add new i2c-based input mouse driver into inputsubsystem for Cypress i2c trackpad devices.

From: Dudley Du
Date: Fri Mar 04 2011 - 03:53:59 EST


Hi Linux-Input Maintainers,

This patch adds new i2c-based input mouse driver into input subsystem for
Cypress i2c trackpad devices. Through this new added driver,
Cypress i2c trackpad devices will be supported in Linux based system
which are developed based on Cypress self-designed PSOC chipset.
The function of this driver reads trackpad data using i2c interfaces
and report cursor moving data and gesture combined keys to input subsystem.

This driver adds one driver main file cypress.c in drivers/input/mouse directory,
and one header file cyapa.h in include/linux directory,
and modified Kconfig and Makefile files in drivers/input/mouse directory to include this driver.

Attached is the patch file for this driver and bellow is the content of this driver.


From ed034f67c11fa2dc93b777ba12a4770af8567ce4 Mon Sep 17 00:00:00 2001
From: Du, Dudley <dudl@xxxxxxxxxxx>
Date: Fri, 4 Mar 2011 15:03:51 -0500
Subject: [PATCH] Add new i2c-based input mouse driver for Cypress i2c-based trackpad devices into input subsystem.

Change-Id: I108ad1505f3d34ab24658921a772e937940a1b88
Signed-off-by: Du, Dudley <dudl@xxxxxxxxxxx>
---
drivers/input/mouse/Kconfig | 11 +
drivers/input/mouse/Makefile | 1 +
drivers/input/mouse/cypress_i2c.c | 3463 +++++++++++++++++++++++++++++++++++++
include/linux/cyapa.h | 60 +
4 files changed, 3535 insertions(+), 0 deletions(-)
create mode 100644 drivers/input/mouse/cypress_i2c.c
create mode 100644 include/linux/cyapa.h

diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index bf5fd7f..845d05a 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -322,4 +322,15 @@ config MOUSE_SYNAPTICS_I2C
To compile this driver as a module, choose M here: the
module will be called synaptics_i2c.

+config MOUSE_CYPRESS_I2C
+ tristate "Cypress I2C Touchpad support"
+ depends on I2C
+ help
+ This driver supports Cypress I2C touchpad on Chrome OS platform.
+
+ Say y here if you have Chrome OS that support Cypress I2C Touchpad.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cypress_i2c.
+
endif
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 570c84a..706f261 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o
+obj-$(CONFIG_MOUSE_CYPRESS_I2C) += cypress_i2c.o
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o

psmouse-objs := psmouse-base.o synaptics.o
diff --git a/drivers/input/mouse/cypress_i2c.c b/drivers/input/mouse/cypress_i2c.c
new file mode 100644
index 0000000..e794691
--- /dev/null
+++ b/drivers/input/mouse/cypress_i2c.c
@@ -0,0 +1,3463 @@
+/*
+ * Cypress APA touchpad with I2C interface
+ *
+ * Copyright (C) 2009 Compulab, Ltd.
+ * Dudley Du <dudl@xxxxxxxxxxx>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+
+#include <linux/cyapa.h>
+
+
+/* Debug macro */
+#define CYAPA_DBG 0
+#if CYAPA_DBG
+ #define DBGPRINTK(x) printk x
+ #define DBG_CYAPA_READ_BLOCK_DATA
+#else
+ #define DBGPRINTK(x)
+#endif
+
+#define CYAPA_USE_I2C_REG_WRITE_BLOCK 0
+
+/* Cypress I2C APA trackpad driver version is defined as bellow:
+** CYAPA_MAJOR_VER.CYAPA_MINOR_VER.CYAPA_REVISIOIN_VER . */
+#define CYAPA_MAJOR_VER 0
+#define CYAPA_MINOR_VER 9
+#define CYAPA_REVISIOIN_VER 4
+
+#define CYAPA_FILTER_EMPTY_DATA 0x7FFFFFFF
+
+/* macro definication for gestures. */
+/* --------------------------------------------------------------- */
+/* |- bit 7 - 5 -|- bit 4 -0 -| */
+/* |------------------------------|----------------------------- | */
+/* |- finger number -|- gesture id -| */
+/* --------------------------------------------------------------- */
+#define GESTURE_FINGERS(x) ((((x) & 0x07) << 5) & 0xE0)
+#define GESTURE_INDEX(x) (((x) & 0x1F))
+#define GESTURE_ID_CODE(finger, index) \
+ (GESTURE_FINGERS(finger) | GESTURE_INDEX(index))
+
+#define GESTURE_NONE 0x00
+/* 0-finger gestures. */
+#define GESTURE_PALM_REJECTIOIN GESTURE_ID_CODE(0, 1)
+/* 1-finger gestures. */
+#define GESTURE_SINGLE_TAP GESTURE_ID_CODE(1, 0)
+#define GESTURE_DOUBLE_TAP GESTURE_ID_CODE(1, 1)
+/*
+** one finger click and hold for more than definitioin time,
+** then to do something.
+*/
+#define GESTURE_TAP_AND_HOLD GESTURE_ID_CODE(1, 2)
+#define GESTURE_EDGE_MOTION GESTURE_ID_CODE(1, 3)
+#define GESTURE_FLICK GESTURE_ID_CODE(1, 4)
+/* GESTURE_DRAG : double click and hold, then move for drag.*/
+#define GESTURE_DRAG GESTURE_ID_CODE(1, 5)
+/* Depending on PSOC user module, it will give four different ID when scroll.*/
+#define GESTURE_SCROLL_UP GESTURE_ID_CODE(1, 6)
+#define GESTURE_SCROLL_DOWN GESTURE_ID_CODE(1, 7)
+#define GESTURE_SCROLL_LEFT GESTURE_ID_CODE(1, 8)
+#define GESTURE_SCROLL_RIGHT GESTURE_ID_CODE(1, 9)
+
+/* 2-finger gestures */
+#define GESTURE_2F_ZOOM_IN GESTURE_ID_CODE(2, 0)
+#define GESTURE_2F_ZOOM_OUT GESTURE_ID_CODE(2, 1)
+#define GESTURE_2F_SCROLL_UP GESTURE_ID_CODE(2, 2)
+#define GESTURE_2F_SCROLL_DOWN GESTURE_ID_CODE(2, 3)
+#define GESTURE_2F_SCROLL_LEFT GESTURE_ID_CODE(2, 4)
+#define GESTURE_2F_SCROLL_RIGHT GESTURE_ID_CODE(2, 5)
+#define GESTURE_2F_ROTATE GESTURE_ID_CODE(2, 6)
+#define GESTURE_2F_PINCH GESTURE_ID_CODE(2, 7)
+/* Activates the Right Click action */
+#define GESTURE_2F_TAP GESTURE_ID_CODE(2, 8)
+/* Single-Finger click and hold while a second finger is moving for dragging. */
+#define GESTURE_2F_DRAG GESTURE_ID_CODE(2, 9)
+#define GESTURE_2F_FLICK GESTURE_ID_CODE(2, 10)
+
+/* 3-finger gestures */
+#define GESTURE_3F_FLICK GESTURE_ID_CODE(3, 0)
+
+/* 4-finger gestures */
+#define GESTURE_4F_FLICK GESTURE_ID_CODE(4, 0)
+
+/* 5-finger gestures */
+#define GESTURE_5F_FLICK GESTURE_ID_CODE(5, 0)
+
+/* swith of the gesture, */
+#define GESTURE_MULTI_TOUCH_ONE_CLICK 0
+
+#define GESTURE_DECODE_FINGERS(x) (((x) >> 5) & 0x07)
+#define GESTURE_DECODE_INDEX(x) ((x) & 0x1F)
+
+/* max gesture index value for each fingers type is 31. 0~21.*/
+#define MAX_FINGERS 5
+
+
+/* parameter value for input_report_key(BTN_TOOL_WIDTH) */
+#define CYAPA_TOOL_WIDTH 50
+
+/* When in IRQ mode read the device every THREAD_IRQ_SLEEP_SECS */
+#define CYAPA_THREAD_IRQ_SLEEP_SECS 2
+#define CYAPA_THREAD_IRQ_SLEEP_MSECS \
+ (CYAPA_THREAD_IRQ_SLEEP_SECS * MSEC_PER_SEC)
+
+/*
+ * When in Polling mode and no data received for CYAPA_NO_DATA_THRES msecs
+ * reduce the polling rate to CYAPA_NO_DATA_SLEEP_MSECS
+ */
+#define CYAPA_NO_DATA_THRES (MSEC_PER_SEC)
+#define CYAPA_NO_DATA_SLEEP_MSECS (MSEC_PER_SEC / 4)
+
+/* report data start reg offset address. */
+#define DATA_REG_START_OFFSET 0x0000
+/* relative data report data size. */
+#define CYAPA_REL_REG_DATA_SIZE 5
+
+
+/* Device Sleep Modes */
+#define DEV_POWER_REG 0x0009
+#define INTERRUPT_MODE_MASK 0x01
+#define PWR_LEVEL_MASK 0x06
+#define PWR_BITS_SHITF 1
+#define GET_PWR_LEVEL(reg) \
+ ((((unsigned char)(reg))&PWR_LEVEL_MASK)>>PWR_BITS_SHITF)
+
+/* protocol V1. */
+#define REG_GESTURES 0x0B
+
+/* definition to store platfrom data. */
+static struct cyapa_platform_data cyapa_i2c_platform_data = {
+ .flag = 0,
+ .gen = CYAPA_GEN2,
+ .power_state = CYAPA_PWR_ACTIVE,
+ .use_absolute_mode = true,
+ .use_polling_mode = false,
+ .polling_interval_time_active = CYAPA_ACTIVE_POLLING_INTVAL_TIME,
+ .polling_interval_time_lowpower = CYAPA_LOWPOWER_POLLING_INTVAL_TIME,
+ .active_touch_timeout = CYAPA_ACTIVE_TOUCH_TIMEOUT,
+ .name = CYAPA_I2C_NAME,
+ .irq_gpio = -1,
+ .report_rate = CYAPA_REPORT_RATE,
+};
+
+
+/*
+** APA trackpad device states.
+** Used in register 0x00, bit1-0, DeviceStatus field.
+*/
+enum cyapa_devicestate {
+ CYAPA_DEV_NORNAL = 0x03,
+ /*
+ ** After trackpad booted, and can report data, it should set this value.
+ ** 0ther values stand for trackpad device is in abnormal state.
+ ** maybe need to do reset operation to it.
+ ** Other values are defined later if needed.
+ */
+};
+
+#define CYAPA_MAX_TOUCHS (MAX_FINGERS)
+/*
+** only 1 gesture can be reported one time right now.
+*/
+#define CYAPA_ONE_TIME_GESTURES (1)
+struct cyapa_touch_gen1 {
+ u8 rel_xy;
+ u8 rel_x;
+ u8 rel_y;
+};
+
+struct cyapa_reg_data_gen1 {
+ u8 tap_motion;
+ s8 deltax;
+ s8 deltay;
+ u8 reserved1;
+ u8 reserved2;
+
+ struct cyapa_touch_gen1 touch1;
+ u8 touch_fingers;
+ u8 feature_config;
+ u8 avg_pressure; /* average of all touched fingers. */
+ u8 gesture_status;
+ struct cyapa_touch_gen1 touchs[CYAPA_MAX_TOUCHS-1];
+};
+
+struct cyapa_touch_gen2 {
+ u8 xy;
+ u8 x;
+ u8 y;
+ u8 id;
+};
+
+struct cyapa_gesture {
+ u8 id;
+ u8 param1;
+ u8 param2;
+};
+
+struct cyapa_reg_data_gen2 {
+ u8 device_status;
+ u8 relative_flags;
+ s8 deltax;
+ s8 deltay;
+ u8 avg_pressure;
+ u8 touch_fingers;
+ u8 reserved1;
+ u8 reserved2;
+ struct cyapa_touch_gen2 touchs[CYAPA_MAX_TOUCHS];
+ u8 gesture_count;
+ struct cyapa_gesture gesture[CYAPA_ONE_TIME_GESTURES];
+};
+
+union cyapa_reg_data {
+ struct cyapa_reg_data_gen1 gen1_data;
+ struct cyapa_reg_data_gen2 gen2_data;
+};
+
+struct cyapa_touch {
+ int x;
+ int y;
+ int id;
+};
+
+struct cyapa_report_data {
+ u8 button;
+ u8 reserved1;
+ u8 reserved2;
+ u8 avg_pressure;
+ int rel_deltaX;
+ int rel_deltaY;
+
+ int touch_fingers;
+ struct cyapa_touch touchs[CYAPA_MAX_TOUCHS];
+
+ /* in gen1 and gen2, only 1 gesture one time supported. */
+ int gestures_count;
+ struct cyapa_gesture gestures[CYAPA_ONE_TIME_GESTURES];
+};
+
+struct speed_preferences {
+ int default_threshold; /* small scroll speed threshold. */
+ int middle_threshold;
+ int fast_threshold;
+};
+
+struct mouse_ballistic_params {
+ /*
+ ** platfrom display aspect ratio adjustment parameters.
+ */
+ int abs_rise_y;
+ int abs_run_y;
+
+ /*
+ ** ABS FIR filter algorithm parameters.
+ */
+ int fir_abs_enabled;
+
+ int fir_abs_depth_max;
+ int fir_abs_depth_slew_count;
+
+ /* Eccentricity Limits. */
+ int fir_gentle_curve_eccentricity_max;
+ int fir_moderate_curve_eccentricity_max;
+
+ int fir_slow_speed_limit;
+ int fir_moderate_speed_limit;
+
+ int fir_gentle_curve_depth;
+ int fir_moderate_curve_depth;
+ int fir_slow_speed_depth;
+ int fir_moderate_speed_depth;
+
+ /*
+ ** ABS IIR filter algorithm parameters.
+ */
+ int iir_abs_enabled;
+ int iir_numerator;
+ int iir_denominator;
+
+ /*
+ ** REL motion speed/acceleration control filter algorithm parameters.
+ */
+ int rel_stroke_history_depth;
+
+ int rel_medium_speed_threshold;
+ int rel_fast_speed_threshold;
+ int rel_flick_speed_threshold;
+
+ int rel_motion_numerator;
+ int rel_medium_speed_numerator;
+ int rel_motion_denominator;
+
+ int rel_acceleration_numerator;
+ int rel_acceleration_denominator;
+
+ /*
+ ** REL IIR filter algorithm parameters.
+ */
+ int rel_iir_acceleration_numerator;
+ int rel_iir_acceleration_denominator;
+
+ int rel_max_acceleration_speed;
+};
+
+struct cyapa_preferences {
+ struct speed_preferences vscroll;
+ struct speed_preferences hscroll;
+ struct speed_preferences zoom;
+
+ struct mouse_ballistic_params mouse_ballistic;
+};
+
+enum fir_curve_mode {
+
+ FIR_CURVE_SLOW_SPEED_LINE = 0,
+ FIR_CURVE_MODERATE_SPEED_LINE = 1,
+ FIR_CURVE_FAST_SPEED_LINE = 2,
+ FIR_CURVE_GENTLE_CURVE = 3,
+ FIR_CURVE_MODERATE_CURVE = 4,
+ FIR_CURVE_FULL_STOP = 5,
+ FIR_CURVE_CIRCLE = 6,
+ FIR_CURVE_UNINITIALIZED = 0xFFFFFFFF
+};
+
+enum rel_stroke_speed {
+ REL_STROKE_SPEED_SLOW,
+ REL_STROKE_SPEED_MEDIUM,
+ REL_STROKE_SPEED_FAST,
+ REL_STROKE_SPEED_WARP
+};
+
+struct point {
+ int x;
+ int y;
+};
+
+/* MAX_FIR_DEPTH:
+** A macro defines the number of entries in the FIR FIFO. */
+#define MAX_FIR_DEPTH 20
+/* STROKE_HISTORY_RECS:
+** A macro which defines the number of samples stored
+** in the stroke history buffer. */
+#define REL_STROKE_HISTORY_RECS 10
+struct cyapa_cursor_filters {
+ enum fir_curve_mode fir_curve_type;
+
+ /* The cursor FIR filter FIFO for the X finger vector. */
+ int fir_vectors_x[MAX_FIR_DEPTH];
+ /* The cursor FIR filter FIFO for the Y finger vector. */
+ int fir_vectors_y[MAX_FIR_DEPTH];
+
+ /* The remnant from the previous X FIR averaging computation.
+ ** Re-scaled when FIR depth changes. */
+ int fir_x_mod;
+ /* The remnant from the previous Y FIR averaging computation.
+ ** Re-scaled when FIR depth changes. */
+ int fir_y_mod;
+
+ /* Current depth of the cursor FIR filter's 'X' FIFO. */
+ int fir_abs_depth_x;
+ /* Current depth of the cursor FIR filter's 'Y' FIFO. */
+ int fir_abs_depth_y;
+
+ /* Previous depth of the cursor FIR filter's 'X' FIFO.
+ ** Used to rescale modular residue when FIFO depth is dynamically
+ ** changed. */
+ int fir_abs_prev_depth_x;
+
+ /* Previous depth of the cursor FIR filter's 'Y' FIFO.
+ ** Used to rescale modular residue when FIFO depth is dynamically
+ */
+ int fir_abs_prev_depth_y;
+
+ /* Counter used to implement the cursor FIR X vector filter's slew rate.
+ ** The FIR filter's slew rate determines the speed at which the FIR
+ ** depth increases over time,
+ ** limited by cursor speed and arc severity. */
+ int fir_abs_depth_slew_counter_x;
+
+ /* Counter used to implement the cursor FIR Y vector filter's slew rate.
+ ** The FIR filter's slew rate determines the speed at which the FIR
+ ** depth increases over time,
+ ** limited by cursor speed and arc severity. */
+ int fir_abs_depth_slew_counter_y;
+
+ int fir_effective_max_depth;
+
+ /* The current state for one finger IIR filtering, X position. */
+ int iir_x;
+
+ /* The current state for one finger IIR filtering, Y position. */
+ int iir_y;
+
+ /* The modular remnant of the previous IIR division for
+ ** one finger X position IIR calculations. */
+ int iir_mod_x;
+
+ /* The modular remnant of the previous IIR division for
+ ** one finger Y position IIR calculations. */
+ int iir_mod_y;
+
+ struct point rel_stroke_history[REL_STROKE_HISTORY_RECS];
+ int rel_stroke_depth;
+ int rel_stroke_speed;
+
+ /* The running modular remainder from the Velocity X vector computation
+ ** the purpose of keeping the residue between report periods is to both
+ ** enhance precision and prevent uncontrolled 'losiness' of velocity. */
+ int rel_residue_x;
+
+ /* The running modular remainder from the Velocity Y vector computation
+ ** The purpose of keeping the residue between report periods is to both
+ ** enhance precision and prevent uncontrolled 'losiness' of velocity. */
+ int rel_residue_y;
+
+ /* The running modular remainder from the acceleration X vector
+ ** computation. */
+ int rel_prev_residue_accel_x;
+
+ /* The running modular remainder from the acceleration Y vector
+ ** computation. */
+ int rel_prev_residue_accel_y;
+
+ /* The running accelerated cursor motion IIR filter's X vector. */
+ int rel_prev_accel_iir_x;
+
+ /* The running accelerated cursor motion IIR filter's X vector's modular
+ ** residue. Prevents loss (slip) which would otherwise result from
+ ** the IIR computation. */
+ int rel_prev_accel_iir_mod_x;
+
+ /* The running accelerated cursor motion IIR filter's X vector. */
+ int rel_prev_accel_iir_y;
+
+ /* The running accelerated cursor motion IIR filter's X vector's modular
+ ** residue. Prevents loss (slip) which would otherwise result from
+ ** the IIR computation. */
+ int rel_prev_accel_iir_mod_y;
+};
+
+/* The main device structure */
+struct cyapa_i2c {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct input_dev *input_wheel;
+ struct input_dev *input_kbd;
+ struct delayed_work dwork;
+ spinlock_t lock;
+ int no_data_count;
+ int scan_ms;
+ int read_pending;
+ int open_count;
+
+ int irq;
+ struct cyapa_platform_data *platform_data;
+ unsigned short data_base_offset;
+ unsigned short control_base_offset;
+ unsigned short command_base_offset;
+ unsigned short query_base_offset;
+
+ struct cyapa_preferences preferences;
+
+ int zoomin_delta;
+ int zoomout_delta;
+ int hscroll_left;
+ int hscroll_right;
+ int hscroll_canceled;
+ int delta_scroll_up;
+ int delta_scroll_down;
+ int delta_scroll_left;
+ int delta_scroll_right;
+ int zoom_trigged;
+
+ int abs_x;
+ int abs_y;
+ int prev_abs_x;
+ int prev_abs_y;
+ int rel_x;
+ int rel_y;
+ int prev_rel_x;
+ int prev_rel_y;
+ struct cyapa_cursor_filters cursor_filters;
+ unsigned char xy_touchs_included_bits;
+ unsigned char gesture_2F_drag_started;
+
+ unsigned long cur_active_gestures[MAX_FINGERS];
+ unsigned long prev_active_gestures[MAX_FINGERS];
+
+ int prev_touch_fingers;
+
+ /* read from query data region. */
+ char product_id[16];
+ unsigned char capability[14];
+ unsigned char fm_maj_ver; /* firmware major version. */
+ unsigned char fm_min_ver; /* firmware minor version. */
+ unsigned char hw_maj_ver; /* hardware major version. */
+ unsigned char hw_min_ver; /* hardware minor version. */
+ int max_absolution_x;
+ int max_absolution_y;
+ int physical_size_x;
+ int physical_size_y;
+};
+
+
+#ifdef DBG_CYAPA_READ_BLOCK_DATA
+void cyapa_print_data_block(const char *func, u8 reg, u8 length, void *data)
+{
+ char buf[512];
+ unsigned buf_len = sizeof(buf);
+ char *p = buf;
+ int i;
+ int l;
+
+ l = snprintf(p, buf_len, "reg 0x%04x: ", reg);
+ buf_len -= l;
+ p += l;
+ for (i = 0; i < length && buf_len; i++, p += l, buf_len -= l)
+ l = snprintf(p, buf_len, "%02x ", *((char *)data + i));
+ printk(KERN_INFO "%s: data block length = %d\n", func, length);
+ printk(KERN_INFO "%s: %s\n", func, buf);
+}
+
+void cyapa_print_report_data(const char *func,
+ struct cyapa_report_data *report_data)
+{
+ int i;
+
+ printk(KERN_INFO "%s: ------------------------------------\n", func);
+ printk(KERN_INFO "%s: report_data.button = 0x%02x\n",
+ func, report_data->button);
+ printk(KERN_INFO "%s: report_data.avg_pressure = %d\n",
+ func, report_data->avg_pressure);
+ printk(KERN_INFO "%s: report_data.touch_fingers = %d\n",
+ func, report_data->touch_fingers);
+ for (i = 0; i < report_data->touch_fingers; i++) {
+ printk(KERN_INFO "%s: report_data.touchs[%d].x = %d\n",
+ func, i, report_data->touchs[i].x);
+ printk(KERN_INFO "%s: report_data.touchs[%d].y = %d\n",
+ func, i, report_data->touchs[i].y);
+ printk(KERN_INFO "%s: report_data.touchs[%d].id = %d\n",
+ func, i, report_data->touchs[i].id);
+ }
+ printk(KERN_INFO "%s: report_data.gestures_count = %d\n",
+ func, report_data->gestures_count);
+ for (i = 0; i < report_data->gestures_count; i++) {
+ printk(KERN_INFO "%s: report_data.gestures[%d].id = 0x%02x\n",
+ func, i, report_data->gestures[i].id);
+ printk(KERN_INFO "%s: report_data.gestures[%d].param1 = 0x%02x\n",
+ func, i, report_data->gestures[i].param1);
+ printk(KERN_INFO "%s: report_data.gestures[%d].param2 = 0x%02x\n",
+ func, i, report_data->gestures[i].param2);
+ }
+ printk(KERN_INFO "%s: -------------------------------------\n", func);
+}
+
+void cyapa_print_paltform_data(const char *func,
+ struct cyapa_platform_data *cyapa_i2c_platform_data)
+{
+ printk(KERN_INFO "%s: -----------------------------------------\n",
+ func);
+ printk(KERN_INFO "%s: cyapa_i2c_platform_data.flag = 0x%08x\n",
+ func, cyapa_i2c_platform_data->flag);
+ printk(KERN_INFO "%s: cyapa_i2c_platform_data.gen = 0x%02x\n",
+ func, cyapa_i2c_platform_data->gen);
+ printk(KERN_INFO "%s: cyapa_i2c_platform_data.power_state = 0x%02x\n",
+ func, cyapa_i2c_platform_data->power_state);
+ printk(KERN_INFO "%s: cyapa_i2c_platform_data.use_absolute_mode = %s\n",
+ func,
+ cyapa_i2c_platform_data->use_absolute_mode ? "true" : "false");
+ printk(KERN_INFO "%s: cyapa_i2c_platform_data.use_polling_mode = %s\n",
+ func, cyapa_i2c_platform_data->use_polling_mode
+ ? "true" : "false");
+ printk(KERN_INFO "%s: cyapa_i2c_platform_data. \
+ polling_interval_time_active = %d\n",
+ func, cyapa_i2c_platform_data->polling_interval_time_active);
+ printk(KERN_INFO "%s: cyapa_i2c_platform_data \
+ .polling_interval_time_lowpower = %d\n",
+ func, cyapa_i2c_platform_data->polling_interval_time_lowpower);
+ printk(KERN_INFO "%s: cyapa_i2c_platform_data \
+ .active_touch_timeout = %d\n",
+ func, cyapa_i2c_platform_data->active_touch_timeout);
+ printk(KERN_INFO "%s: cyapa_i2c_platform_data.name = %s\n",
+ func, cyapa_i2c_platform_data->name);
+ printk(KERN_INFO "%s: cyapa_i2c_platform_data.irq_gpio = %d\n",
+ func, cyapa_i2c_platform_data->irq_gpio);
+ printk(KERN_INFO "%s: cyapa_i2c_platform_data.report_rate = %d\n",
+ func, cyapa_i2c_platform_data->report_rate);
+ printk(KERN_INFO "%s: cyapa_i2c_platform_data.init = %s%p\n",
+ func, cyapa_i2c_platform_data->init ? "0x" : "",
+ cyapa_i2c_platform_data->init);
+ printk(KERN_INFO "%s: cyapa_i2c_platform_data.wakeup = %s%p\n",
+ func, cyapa_i2c_platform_data->wakeup ? "0x" : "",
+ cyapa_i2c_platform_data->wakeup);
+ printk(KERN_INFO "%s: -----------------------------------------\n",
+ func);
+}
+#else
+void cyapa_print_data_block(const char *func, u8 reg, u8 length, void *data) {}
+void cyapa_print_report_data(const char *func,
+ struct cyapa_report_data *report_data) {}
+void cyapa_print_paltform_data(const char *func,
+ struct cyapa_platform_data *cyapa_i2c_platform_data) {}
+#endif
+
+
+/*
+ * Driver's initial design makes no race condition possible on i2c bus,
+ * so there is no need in any locking.
+ * Keep it in mind, while playing with the code.
+ */
+static s32 cyapa_i2c_reg_read_byte(struct i2c_client *client, u16 reg)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, (u8)reg & 0xff);
+
+ return ((ret < 0) ? 0 : ret);
+}
+
+static s32 cyapa_i2c_reg_write_byte(struct i2c_client *client, u16 reg, u8 val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, (u8)reg & 0xff, val);
+
+ return ((ret < 0) ? 0 : ret);
+}
+
+static s32 cyapa_i2c_reg_read_block(struct i2c_client *client, u16 reg,
+ int length, u8 *values)
+{
+ int retval;
+ u8 buf[1];
+
+ /*
+ ** depending on PSOC easy I2C read operations.
+ ** step1: set read pointer of easy I2C.
+ ** step2: read data.
+ */
+ /* step1: set read pointer of easy I2C. */
+ memset(buf, 0, 1);
+ buf[0] = (u8)(((u8)reg) & 0xff);
+ retval = i2c_master_send(client, buf, 1);
+ if (retval < 0) {
+ DBGPRINTK(("%s: i2c_master_send error, retval=%d\n",
+ __func__, retval));
+ return retval;
+ }
+
+ /* step2: read data. */
+ retval = i2c_master_recv(client, values, length);
+ if (retval < 0) {
+ DBGPRINTK(("%s: i2c_master_recv error, retval=%d\n",
+ __func__, retval));
+ return retval;
+ }
+
+ /* debug message */
+ cyapa_print_data_block(__func__, (u8)reg, retval, values);
+
+ if (retval != length) {
+ dev_warn(&client->dev,
+ "%s: warning I2C block read bytes \
+ [%d] not equal to required bytes [%d].\n",
+ __func__, retval, length);
+ }
+
+ return retval;
+}
+
+#if CYAPA_USE_I2C_REG_WRITE_BLOCK
+static s32 cyapa_i2c_reg_write_block(struct i2c_client *client, u16 reg,
+ u8 length, const u8 *values)
+
+{
+ int retval;
+ int i;
+ u8 buf[256];
+
+ if ((length+1) > 256) {
+ DBGPRINTK(("%s: invalid write data length, length=%d\n",
+ __func__, length));
+ return -EINVAL;
+ }
+
+ /*
+ ** depending on PSOC easy I2C read operations.
+ ** step1: write data to easy I2C in one command.
+ */
+ /* step1: write data to easy I2C in one command. */
+ memset(buf, 0, 256);
+ buf[0] = (u8)(((u8)reg) & 0xff);
+ /* move data shoud be write to I2C slave device. */
+ for (i = 1; i < length; i++)
+ buf[i] = values[i-1];
+
+ retval = i2c_master_send(client, buf, length+1);
+ if (retval < 0) {
+ DBGPRINTK(("%s: i2c_master_send error, retval=%d\n",
+ __func__, retval));
+ return retval;
+ }
+
+ if (retval != (length+1)) {
+ dev_warn(&client->dev,
+ "%s: warning I2C block write bytes \
+ [%d] not equal to required bytes [%d].\n",
+ __func__, retval, length);
+ }
+
+ return retval;
+}
+#endif
+
+#define REG_OFFSET_DATA_BASE 0x0000
+#define REG_OFFSET_CONTROL_BASE 0x0029
+#define REG_OFFSET_COMMAND_BASE 0x0049
+#define REG_OFFSET_QUERY_BASE 0x004B
+static void cyapa_get_reg_offset(struct cyapa_i2c *touch)
+{
+ touch->data_base_offset = REG_OFFSET_DATA_BASE;
+ touch->control_base_offset = REG_OFFSET_CONTROL_BASE;
+ touch->command_base_offset = REG_OFFSET_COMMAND_BASE;
+ touch->query_base_offset = REG_OFFSET_QUERY_BASE;
+
+ /* this function will be updated later depending firmware support. */
+}
+
+static void cyapa_get_query_data(struct cyapa_i2c *touch)
+{
+ unsigned char query_data[40];
+ int ret_read_size = 0;
+ int i;
+
+ /* query data has been supported in GEN1 protocol.*/
+ if (touch->platform_data->gen == CYAPA_GEN2) {
+ memset(query_data, 0, 40);
+ ret_read_size = cyapa_i2c_reg_read_block(touch->client,
+ touch->query_base_offset,
+ 38,
+ (u8 *)&query_data);
+
+ touch->product_id[0] = query_data[0];
+ touch->product_id[1] = query_data[1];
+ touch->product_id[2] = query_data[2];
+ touch->product_id[3] = query_data[3];
+ touch->product_id[4] = query_data[4];
+ touch->product_id[5] = '-';
+ touch->product_id[6] = query_data[5];
+ touch->product_id[7] = query_data[6];
+ touch->product_id[8] = query_data[7];
+ touch->product_id[9] = query_data[8];
+ touch->product_id[10] = query_data[9];
+ touch->product_id[11] = query_data[10];
+ touch->product_id[12] = '-';
+ touch->product_id[13] = query_data[11];
+ touch->product_id[14] = query_data[12];
+ touch->product_id[15] = '\0';
+
+ touch->fm_maj_ver = query_data[15];
+ touch->fm_min_ver = query_data[16];
+ touch->hw_maj_ver = query_data[17];
+ touch->hw_min_ver = query_data[18];
+
+ for (i = 0; i < 13; i++)
+ touch->capability[i] = query_data[19+i];
+
+ touch->max_absolution_x =
+ (((query_data[32] & 0xF0) << 4) | query_data[33]);
+ touch->max_absolution_y =
+ (((query_data[32] & 0x0F) << 8) | query_data[34]);
+ if (!touch->max_absolution_x || !touch->max_absolution_y) {
+ if (!strcmp(touch->product_id, "CYTRA-014001-00")) {
+ touch->max_absolution_x = 1600;
+ touch->max_absolution_y = 900;
+ } else {
+ touch->max_absolution_x = 1200;
+ touch->max_absolution_y = 600;
+ }
+ }
+
+ touch->physical_size_x =
+ (((query_data[35] & 0xF0) << 4) | query_data[36]);
+ touch->physical_size_y =
+ (((query_data[35] & 0x0F) << 8) | query_data[37]);
+ if (!touch->physical_size_x || !touch->physical_size_y) {
+ touch->physical_size_x = 105;
+ touch->physical_size_y = 60;
+ }
+
+ printk(KERN_INFO "Cypress Trackpad Information:\n");
+ printk(KERN_INFO "\t\t\tProduction ID: %s\n",
+ touch->product_id);
+ printk(KERN_INFO "\t\t\tFirmware version: %d.%d\n",
+ touch->fm_maj_ver, touch->fm_min_ver);
+ printk(KERN_INFO "\t\t\tHardware version: %d.%d\n",
+ touch->hw_maj_ver, touch->hw_min_ver);
+ printk(KERN_INFO "\t\t\tDriver Version: %d.%d.%d\n",
+ CYAPA_MAJOR_VER, CYAPA_MINOR_VER, CYAPA_REVISIOIN_VER);
+ printk(KERN_INFO "\t\t\tResolution X,Y: %d,%d\n",
+ touch->max_absolution_x, touch->max_absolution_y);
+ printk(KERN_INFO "\t\t\tPhysical Size X,Y: %d,%d\n",
+ touch->physical_size_x, touch->physical_size_y);
+ }
+}
+
+static int cyapa_i2c_reconfig(struct cyapa_i2c *touch)
+{
+ struct i2c_client *client = touch->client;
+ int regval = 0;
+ int retval = 0;
+
+ if (touch->platform_data->gen == CYAPA_GEN1) {
+ /* trackpad gen1 firmware. */
+ DBGPRINTK(("%s: trackpad support gen1 firmware.\n", __func__));
+
+ regval = cyapa_i2c_reg_read_byte(client, DEV_POWER_REG);
+ DBGPRINTK(("%s: read trackpad interrupt bit = 0x%02x\n",
+ __func__, regval&INTERRUPT_MODE_MASK));
+
+ if ((touch->platform_data->use_polling_mode == true)
+ && ((regval & INTERRUPT_MODE_MASK)
+ == INTERRUPT_MODE_MASK)) {
+ /* reset trackpad to polling mode. */
+ regval &= (~INTERRUPT_MODE_MASK);
+ retval = cyapa_i2c_reg_write_byte(client, DEV_POWER_REG,
+ (u8)(regval & 0xff));
+ if (retval) {
+ DBGPRINTK(("%s: set to polliing mode failed,\
+ retval=%d.\n", __func__, retval));
+ /*
+ * Though firmware has set interrupt mode bit.
+ * but since platfrom doesn't support
+ * interrupt mode, so also use polling mode here
+ * do nothing.
+ */
+ }
+ } else if ((touch->platform_data->use_polling_mode == false)
+ && ((regval & INTERRUPT_MODE_MASK)
+ != INTERRUPT_MODE_MASK)) {
+ /* reset trackpad to interrupt mode. */
+ regval |= INTERRUPT_MODE_MASK;
+ retval = cyapa_i2c_reg_write_byte(client, DEV_POWER_REG,
+ (u8)(regval & 0xff));
+ if (retval) {
+ DBGPRINTK(("%s: set to interrup mode failed, \
+ retval=%d.\n", __func__, retval));
+ touch->platform_data->use_polling_mode = true;
+ }
+ }
+
+ DBGPRINTK(("%s: trackpad interrupt bit = 0x%02x\n", __func__,
+ (u8)cyapa_i2c_reg_read_byte(client, DEV_POWER_REG)));
+ } else {
+ /* trackpad gen2 firmware. default is interrupt mode. */
+ DBGPRINTK(("%s: trackpad support gen2 firmware.\n", __func__));
+
+ cyapa_get_reg_offset(touch);
+ cyapa_get_query_data(touch);
+ }
+
+ DBGPRINTK(("%s: use %s mode.\n", __func__,
+ ((touch->platform_data->use_polling_mode == true)
+ ? "polling" : "interrupt")));
+ return retval;
+}
+
+static int cyapa_i2c_reset_config(struct cyapa_i2c *touch)
+{
+ int ret = 0;
+
+ DBGPRINTK(("%s: ...\n", __func__));
+
+ return ret;
+}
+
+static int cyapa_verify_data_device(struct cyapa_i2c *touch,
+ union cyapa_reg_data *reg_data)
+{
+ struct cyapa_reg_data_gen1 *data_gen1 = NULL;
+ struct cyapa_reg_data_gen2 *data_gen2 = NULL;
+
+ if (touch->platform_data->gen == CYAPA_GEN1) {
+ data_gen1 = &reg_data->gen1_data;
+ if ((data_gen1->tap_motion & 0x08) != 0x08) {
+ /* invalid data. */
+ DBGPRINTK(("%s: invalid data reg address 0x00, \
+ bit3 is not set.\n", __func__));
+ return -EINVAL;
+ }
+ } else {
+ data_gen2 = &reg_data->gen2_data;
+ if ((data_gen2->device_status & 0x80) != 0x80) {
+ /* invalid data. */
+ DBGPRINTK(("%s: invalid data reg address 0x00, \
+ bit7 is not set.\n", __func__));
+ return -EINVAL;
+ }
+
+ if ((data_gen2->device_status & 0x03) != CYAPA_DEV_NORNAL) {
+ DBGPRINTK(("%s: invalid device status = 0x%02x, \
+ wait for device ready.\n",
+ __func__, (data_gen2->device_status & 0x03)));
+ return -EBUSY;
+ }
+ }
+
+ return 0;
+}
+
+static inline void cyapa_calculate_abs_xy(struct cyapa_i2c *touch,
+ struct cyapa_report_data *report_data)
+{
+ int i;
+ int sum_x = 0, sum_y = 0;
+
+ /* invalid input data. */
+ if (!touch->xy_touchs_included_bits || !report_data->touch_fingers) {
+ touch->prev_abs_x = -1;
+ touch->prev_abs_y = -1;
+ return;
+ }
+
+ for (i = 0; i < CYAPA_MAX_TOUCHS; i++) {
+ if (touch->xy_touchs_included_bits & (0x01 << i)) {
+ sum_x += report_data->touchs[i].x;
+ sum_y += report_data->touchs[i].y;
+ }
+ }
+
+ touch->abs_x = sum_x / report_data->touch_fingers;
+ touch->abs_y = sum_y / report_data->touch_fingers;
+ /* x, y directory of Cypress trackpad is in negative direction of screen
+ ** for some platform it maybe different. */
+ /***
+ touch->abs_x = touch->platform_data->max_touchpad_x - touch->abs_x;
+ touch->abs_y = touch->platform_data->max_touchpad_y - touch->abs_y;
+ ***/
+
+ /* use simple filtr to make cursor move smoother. */
+ if (touch->prev_abs_x != -1) {
+ touch->abs_x = (touch->abs_x * 3 + touch->prev_abs_x) >> 2;
+ touch->abs_y = (touch->abs_y * 3 + touch->prev_abs_y) >> 2;
+ }
+
+ touch->prev_abs_x = touch->abs_x;
+ touch->prev_abs_y = touch->abs_y;
+}
+
+static inline int cyapa_sqrt(int delta_x, int delta_y)
+{
+ int Xk0 = 0;
+ int Xk1;
+ int multi;
+
+ multi = Xk1 = delta_x*delta_x + delta_y*delta_y;
+
+ while (abs(Xk0 - Xk1) > 1) {
+ Xk0 = Xk1;
+ Xk1 = (Xk0 + (multi / Xk0)) / 2;
+ }
+
+ return Xk1;
+}
+
+static void cyapa_parse_gen1_data(struct cyapa_i2c *touch,
+ struct cyapa_reg_data_gen1 *reg_data,
+ struct cyapa_report_data *report_data)
+{
+ int i;
+ int gesture_report_index = 0;
+ int gesture_fingers = 0;
+ int gesture_index = 0;
+
+ /* parse gestures and button data */
+ report_data->button = reg_data->tap_motion & 0x01;
+
+ /* get relative delta X and delta Y. */
+ report_data->rel_deltaX = reg_data->deltax;
+ /* The Y directory of trackpad is the oppsite of Screen. */
+ report_data->rel_deltaY = -reg_data->deltay;
+
+ if (reg_data->tap_motion & 0x02) {
+ report_data->gestures[gesture_report_index++].id
+ = GESTURE_SINGLE_TAP;
+
+ gesture_fingers = GESTURE_DECODE_FINGERS(GESTURE_SINGLE_TAP);
+ gesture_index = GESTURE_DECODE_INDEX(GESTURE_SINGLE_TAP);
+ touch->cur_active_gestures[gesture_fingers]
+ |= (1UL << gesture_index);
+ }
+
+ if (reg_data->tap_motion & 0x04) {
+ report_data->gestures[gesture_report_index++].id
+ = GESTURE_DOUBLE_TAP;
+
+ gesture_fingers = GESTURE_DECODE_FINGERS(GESTURE_DOUBLE_TAP);
+ gesture_index = GESTURE_DECODE_INDEX(GESTURE_DOUBLE_TAP);
+ touch->cur_active_gestures[gesture_fingers]
+ |= (1UL << gesture_index);
+ }
+
+ report_data->gestures_count = gesture_report_index;
+
+ /* pase fingers touch data */
+ report_data->touch_fingers
+ = ((reg_data->touch_fingers > CYAPA_MAX_TOUCHS) ?
+ (CYAPA_MAX_TOUCHS) : (reg_data->touch_fingers));
+ report_data->avg_pressure = reg_data->avg_pressure;
+ report_data->touchs[0].x =
+ ((reg_data->touch1.rel_xy & 0xF0) << 4)
+ | reg_data->touch1.rel_x;
+ report_data->touchs[0].y =
+ ((reg_data->touch1.rel_xy & 0x0F) << 8)
+ | reg_data->touch1.rel_y;
+ report_data->touchs[0].id = 0;
+
+ for (i = 0; i < (CYAPA_MAX_TOUCHS-1); i++) {
+ report_data->touchs[i+1].x =
+ ((reg_data->touchs[i].rel_xy & 0xF0) << 4)
+ | reg_data->touchs[i].rel_x;
+ report_data->touchs[i+1].y =
+ ((reg_data->touchs[i].rel_xy & 0x0F) << 8)
+ | reg_data->touchs[i].rel_y;
+ report_data->touchs[i+1].id = i+1;
+ }
+
+ cyapa_print_report_data(__func__, report_data);
+}
+
+static void cyapa_parse_gen2_data(struct cyapa_i2c *touch,
+ struct cyapa_reg_data_gen2 *reg_data,
+ struct cyapa_report_data *report_data)
+{
+ int i;
+ int gesture_fingers = 0;
+ int gesture_index = 0;
+
+ /* bit2-middle button; bit1-right button; bit0-left buttom. */
+ report_data->button = reg_data->relative_flags & 0x07;
+
+ /* get relative delta X and delta Y. */
+ report_data->rel_deltaX = reg_data->deltax;
+ /* The Y directory of trackpad is the oppsite of Screen. */
+ report_data->rel_deltaY = -reg_data->deltay;
+
+ /* copy fingers touch data */
+ report_data->avg_pressure = reg_data->avg_pressure;
+ report_data->touch_fingers
+ = ((reg_data->touch_fingers > CYAPA_MAX_TOUCHS) ?
+ (CYAPA_MAX_TOUCHS) : (reg_data->touch_fingers));
+ for (i = 0; i < report_data->touch_fingers; i++) {
+ report_data->touchs[i].x =
+ ((reg_data->touchs[i].xy & 0xF0) << 4)
+ | reg_data->touchs[i].x;
+ report_data->touchs[i].y =
+ ((reg_data->touchs[i].xy & 0x0F) << 8)
+ | reg_data->touchs[i].y;
+ report_data->touchs[i].id = reg_data->touchs[i].id;
+ }
+
+ /* parse gestures */
+ report_data->gestures_count =
+ (((reg_data->gesture_count) > CYAPA_ONE_TIME_GESTURES) ?
+ CYAPA_ONE_TIME_GESTURES : reg_data->gesture_count);
+ for (i = 0; i < report_data->gestures_count; i++) {
+ report_data->gestures[i].id = reg_data->gesture[i].id;
+ report_data->gestures[i].param1 = reg_data->gesture[i].param1;
+ report_data->gestures[i].param2 = reg_data->gesture[i].param2;
+
+ gesture_fingers
+ = GESTURE_DECODE_FINGERS(report_data->gestures[i].id);
+ gesture_index
+ = GESTURE_DECODE_INDEX(report_data->gestures[i].id);
+ touch->cur_active_gestures[gesture_fingers]
+ |= (1UL << gesture_index);
+ }
+
+ cyapa_print_report_data(__func__, report_data);
+}
+
+void cyapa_set_preferences(struct cyapa_i2c *touch)
+{
+ struct cyapa_preferences *preferences = &touch->preferences;
+
+ /* set default setting for hscroll. */
+ preferences->vscroll.default_threshold = 4;
+ preferences->vscroll.middle_threshold = 8;
+ preferences->vscroll.fast_threshold = 16;
+
+ /* set default setting for vscroll. */
+ preferences->hscroll.default_threshold = 4;
+ preferences->hscroll.middle_threshold = 8;
+ preferences->hscroll.fast_threshold = 16;
+
+ /* set default setting for vscroll. */
+ preferences->zoom.default_threshold = 8;
+ preferences->zoom.middle_threshold = 16;
+ preferences->zoom.fast_threshold = 32;
+
+ /* set default setting for platform display aspect ratio adjustment. */
+ preferences->mouse_ballistic.abs_rise_y = 16;
+ preferences->mouse_ballistic.abs_run_y = 14;
+
+ /* set default setting for ABS FIR filter parameters. */
+ preferences->mouse_ballistic.fir_abs_enabled = 1;
+
+ preferences->mouse_ballistic.fir_abs_depth_slew_count = 5;
+ preferences->mouse_ballistic.fir_gentle_curve_eccentricity_max = 2;
+ preferences->mouse_ballistic.fir_moderate_curve_eccentricity_max = 3;
+
+ preferences->mouse_ballistic.fir_slow_speed_limit = 4;
+ preferences->mouse_ballistic.fir_moderate_speed_limit = 7;
+
+ preferences->mouse_ballistic.fir_abs_depth_max = 7;
+ preferences->mouse_ballistic.fir_slow_speed_depth = 7;
+ preferences->mouse_ballistic.fir_moderate_speed_depth = 7;
+ preferences->mouse_ballistic.fir_gentle_curve_depth = 5;
+ preferences->mouse_ballistic.fir_moderate_curve_depth = 6;
+
+ /* set default setting for ABS IIR filter parameters. */
+ preferences->mouse_ballistic.iir_abs_enabled = 1;
+ preferences->mouse_ballistic.iir_numerator = 1;
+ preferences->mouse_ballistic.iir_denominator = 2;
+
+ /* set default setting for REL speed/acceleration filter parameters. */
+ preferences->mouse_ballistic.rel_stroke_history_depth = 4;
+ preferences->mouse_ballistic.rel_medium_speed_threshold = 7;
+ preferences->mouse_ballistic.rel_fast_speed_threshold = 10;
+ preferences->mouse_ballistic.rel_flick_speed_threshold = 80;
+ preferences->mouse_ballistic.rel_motion_numerator = 8;
+ preferences->mouse_ballistic.rel_medium_speed_numerator = 7;
+ preferences->mouse_ballistic.rel_motion_denominator = 12;
+ preferences->mouse_ballistic.rel_acceleration_numerator = 16;
+ preferences->mouse_ballistic.rel_acceleration_denominator = 100;
+
+ /* set default setting for REL IIR filter parameters. */
+ preferences->mouse_ballistic.rel_iir_acceleration_numerator = 1;
+ preferences->mouse_ballistic.rel_iir_acceleration_denominator = 8;
+ preferences->mouse_ballistic.rel_max_acceleration_speed = 900;
+}
+
+void cyapa_reset_cursor_filters_data(struct cyapa_i2c *touch)
+{
+ int i;
+ struct cyapa_cursor_filters *filter = &touch->cursor_filters;
+
+ /* reset previous motion data. */
+ touch->prev_abs_x = -1;
+ touch->prev_abs_y = -1;
+ touch->prev_rel_x = 0;
+ touch->prev_rel_y = 0;
+
+ /* reset data used in FIR filter to ABS data. */
+ filter->fir_curve_type = FIR_CURVE_UNINITIALIZED;
+ for (i = 0; i < MAX_FIR_DEPTH; i++) {
+ filter->fir_vectors_x[i] = 0;
+ filter->fir_vectors_y[i] = 0;
+ }
+ filter->fir_x_mod = 0;
+ filter->fir_y_mod = 0;
+ filter->fir_abs_depth_x = 0;
+ filter->fir_abs_depth_y = 0;
+ filter->fir_abs_prev_depth_x = -1;
+ filter->fir_abs_prev_depth_y = -1;
+ filter->fir_abs_depth_slew_counter_x = 1;
+ filter->fir_abs_depth_slew_counter_y = 1;
+
+ /* reset data used in IIR filter to ABS data. */
+ filter->iir_x = 0xFFFFFFFF;
+ filter->iir_y = 0xFFFFFFFF;
+ filter->iir_mod_x = 0;
+ filter->iir_mod_y = 0;
+
+ /* reset data used in compluting relative movement speed category. */
+ filter->rel_stroke_depth = 0;
+ filter->rel_stroke_speed = REL_STROKE_SPEED_FAST;
+
+ /* reset data used in relative speed/acceleration control. */
+ filter->rel_residue_x = 0;
+ filter->rel_residue_y = 0;
+ filter->rel_prev_residue_accel_x = 0;
+ filter->rel_prev_residue_accel_y = 0;
+
+ /* reset data used in relative IIR filter. */
+ filter->rel_prev_accel_iir_x = CYAPA_FILTER_EMPTY_DATA;
+ filter->rel_prev_accel_iir_y = CYAPA_FILTER_EMPTY_DATA;
+ filter->rel_prev_accel_iir_mod_x = 0;
+ filter->rel_prev_accel_iir_mod_y = 0;
+}
+
+/*Converts modular residues to new scale.
+** This enables residues to persist across interations where various divisors
+** which are used by data filters are modified.
+*/
+inline int cyapa_fir_rescale_modulus(int mod_in, int old_scale, int new_scale)
+{
+ int temp;
+
+ if (old_scale <= 1)
+ return mod_in;
+
+ if (new_scale <= old_scale)
+ return mod_in;
+
+ temp = mod_in * new_scale;
+ temp = temp / old_scale;
+
+ return temp;
+}
+
+/* Determines the ecentricity, a primitive measure of the degree of curvature,
+** which is used to select FIR characteristics.
+*/
+int cyapa_fir_get_curve_type(struct cyapa_i2c *touch)
+{
+ int i;
+ int curve_type;
+ int cur_speed;
+ int max_speed, min_speed;
+ int max_speed_x, max_speed_y;
+ int eccentricity_x, eccentricity_y;
+ int zero_count_x = 0;
+ int zero_count_y = 0;
+ struct cyapa_cursor_filters *filter = &touch->cursor_filters;
+ struct mouse_ballistic_params *mouse_ballistic
+ = &touch->preferences.mouse_ballistic;
+
+ if (filter->fir_curve_type == FIR_CURVE_UNINITIALIZED) {
+ if ((filter->fir_abs_depth_x < 2)
+ || (filter->fir_abs_depth_y < 2)) {
+ filter->fir_curve_type = FIR_CURVE_MODERATE_SPEED_LINE;
+ return FIR_CURVE_MODERATE_SPEED_LINE;
+ }
+
+ return filter->fir_curve_type;
+ }
+
+ /* find max speed in X direction. */
+ max_speed = -100000;
+ min_speed = 100000;
+ for (i = 0; i < (filter->fir_abs_depth_x - 1); i++) {
+ cur_speed = filter->fir_vectors_x[i]
+ - filter->fir_vectors_x[i+1];
+
+ if (cur_speed < min_speed)
+ min_speed = cur_speed;
+
+ if (cur_speed > max_speed)
+ max_speed = cur_speed;
+
+ /* find the count of previous data packages
+ ** that X direction keeps not moving. */
+ if ((min_speed == 0) && (max_speed == 0))
+ zero_count_x = i;
+ }
+
+ if (i > 0)
+ eccentricity_x = (max_speed - min_speed) / i;
+ else
+ eccentricity_x = 0;
+
+ max_speed_x = max_speed;
+
+ /* find max speed in Y direction. */
+ max_speed = -100000;
+ min_speed = 100000;
+ for (i = 0; i < (filter->fir_abs_depth_y - 1); i++) {
+ cur_speed = filter->fir_vectors_y[i]
+ - filter->fir_vectors_y[i+1];
+
+ if (cur_speed < min_speed)
+ min_speed = cur_speed;
+
+ if (cur_speed > max_speed)
+ max_speed = cur_speed;
+
+ /* find the count of previous data packages
+ ** that Y direction keeps not moving. */
+ if ((min_speed == 0) && (max_speed == 0))
+ zero_count_y = i;
+ }
+
+ if (i > 0)
+ eccentricity_y = (max_speed - min_speed) / i;
+ else
+ eccentricity_y = 0;
+
+ max_speed_y = max_speed;
+
+ /* After previous 3 or more data packages reports,
+ ** finger movement still not detected. */
+ if ((zero_count_x > 2) && (zero_count_y > 2)) {
+ filter->fir_curve_type = FIR_CURVE_FULL_STOP;
+ return FIR_CURVE_FULL_STOP;
+ }
+
+ /* calculate combined speed vectors. */
+ cur_speed = abs(max_speed_x) + abs(max_speed_y);
+
+ /* sort curve line type. */
+ if (cur_speed < mouse_ballistic->fir_slow_speed_limit)
+ curve_type = FIR_CURVE_SLOW_SPEED_LINE;
+ else if (cur_speed < mouse_ballistic->fir_moderate_speed_limit)
+ curve_type = FIR_CURVE_MODERATE_SPEED_LINE;
+ else
+ curve_type = FIR_CURVE_FAST_SPEED_LINE;
+
+ /* sort fast speed curve type much more detail
+ ** based on eccentricity x and y value. */
+ if (curve_type == FIR_CURVE_FAST_SPEED_LINE) {
+ if ((eccentricity_x == 1) && (eccentricity_y == 1)) {
+ curve_type = FIR_CURVE_GENTLE_CURVE;
+ } else if ((eccentricity_x
+ == mouse_ballistic->fir_gentle_curve_eccentricity_max)
+ && (eccentricity_y == 1)) {
+ curve_type = FIR_CURVE_GENTLE_CURVE;
+ } else if ((eccentricity_x == 1)
+ && (eccentricity_y ==
+ mouse_ballistic->fir_gentle_curve_eccentricity_max)) {
+ curve_type = FIR_CURVE_GENTLE_CURVE;
+ } else if ((eccentricity_x
+ > mouse_ballistic->fir_gentle_curve_eccentricity_max)
+ && (eccentricity_y
+ > mouse_ballistic->fir_gentle_curve_eccentricity_max)) {
+ curve_type = FIR_CURVE_MODERATE_CURVE;
+ } else if ((eccentricity_x
+ >= mouse_ballistic->fir_moderate_curve_eccentricity_max)
+ || (eccentricity_y >=
+ mouse_ballistic->fir_moderate_curve_eccentricity_max)) {
+ curve_type = FIR_CURVE_MODERATE_CURVE;
+ }
+ }
+
+ filter->fir_curve_type = curve_type;
+
+ return curve_type;
+}
+
+/* Obtain the FIR curve type and obtain the FIR filter depth.
+** Filter depth is a tuning parameter.
+*/
+int cyapa_fir_reset_fifo_depth(struct cyapa_i2c *touch)
+{
+ int curve_type;
+ int fir_depth;
+ struct cyapa_cursor_filters *filter = &touch->cursor_filters;
+ struct mouse_ballistic_params *mouse_ballistic
+ = &touch->preferences.mouse_ballistic;
+
+ curve_type = cyapa_fir_get_curve_type(touch);
+
+ switch (curve_type) {
+ case FIR_CURVE_SLOW_SPEED_LINE:
+ fir_depth = mouse_ballistic->fir_slow_speed_depth;
+ break;
+ case FIR_CURVE_MODERATE_SPEED_LINE:
+ fir_depth = mouse_ballistic->fir_moderate_speed_depth;
+ break;
+ case FIR_CURVE_FAST_SPEED_LINE:
+ fir_depth = mouse_ballistic->fir_gentle_curve_depth;
+ break;
+ case FIR_CURVE_GENTLE_CURVE:
+ fir_depth = mouse_ballistic->fir_gentle_curve_depth;
+ break;
+ case FIR_CURVE_MODERATE_CURVE:
+ fir_depth = mouse_ballistic->fir_moderate_curve_depth;
+ break;
+ case FIR_CURVE_FULL_STOP:
+ fir_depth = 2;
+ break;
+ case FIR_CURVE_CIRCLE:
+ fir_depth = 0;
+ break;
+ default:
+ fir_depth = mouse_ballistic->fir_slow_speed_depth;
+ break;
+ }
+
+ /* avoid invalid parameter setting. */
+ if (fir_depth > mouse_ballistic->fir_abs_depth_max)
+ fir_depth = mouse_ballistic->fir_abs_depth_max;
+
+ filter->fir_effective_max_depth = fir_depth;
+
+ return fir_depth;
+}
+
+static int cyapa_filter_fir_abs_x(struct cyapa_i2c *touch)
+{
+ int i;
+ int sum_x, average_x;
+ int effective_max_depth;
+ struct cyapa_cursor_filters *filter = &touch->cursor_filters;
+ struct mouse_ballistic_params *mouse_ballistic
+ = &touch->preferences.mouse_ballistic;
+
+ effective_max_depth = filter->fir_effective_max_depth;
+
+ /* set slow speed FIR depth. */
+ if ((effective_max_depth < filter->fir_abs_depth_x)
+ && (effective_max_depth < 2)) {
+ filter->fir_abs_depth_x = effective_max_depth;
+ filter->fir_abs_depth_slew_counter_x = 0;
+ }
+
+ if (effective_max_depth > filter->fir_abs_depth_x) {
+ /* count depth change slew value, if less or equal to 0,
+ ** make depth changed and reset slew value for next time. */
+ filter->fir_abs_depth_slew_counter_x--;
+ if (filter->fir_abs_depth_slew_counter_x <= 0) {
+ filter->fir_abs_depth_slew_counter_x
+ = mouse_ballistic->fir_abs_depth_slew_count;
+ filter->fir_abs_depth_x++;
+ }
+ }
+
+ if (effective_max_depth < filter->fir_abs_depth_x) {
+ /* count depth change slew value, if less or equal to 0,
+ ** make depth changed and reset slew value for next time. */
+ filter->fir_abs_depth_slew_counter_x--;
+ if (filter->fir_abs_depth_slew_counter_x <= 0) {
+ filter->fir_abs_depth_slew_counter_x
+ = mouse_ballistic->fir_abs_depth_slew_count;
+ filter->fir_abs_depth_x--;
+ }
+ }
+
+ /* insert new X data. */
+ for (i = mouse_ballistic->fir_abs_depth_max-1; i > 0; i--)
+ filter->fir_vectors_x[i] = filter->fir_vectors_x[i-1];
+ filter->fir_vectors_x[0] = touch->abs_x;
+
+ /* calculate and apply ABS FIR filter algorithm to X data. */
+ for (i = 0; i < filter->fir_abs_depth_x; i++)
+ sum_x += filter->fir_vectors_x[i];
+
+ if (filter->fir_abs_depth_x > 1) {
+ /* rescale modulus based on ABS FIR depth. */
+ filter->fir_x_mod = cyapa_fir_rescale_modulus(filter->fir_x_mod,
+ filter->fir_abs_prev_depth_x, filter->fir_abs_depth_x);
+ average_x = (sum_x + filter->fir_x_mod)
+ / filter->fir_abs_depth_x;
+ filter->fir_x_mod = (sum_x + filter->fir_x_mod)
+ % filter->fir_abs_depth_x;
+ } else {
+ average_x = touch->abs_x;
+ filter->fir_x_mod = 0;
+ }
+
+ /* store current ABS FIR depth.*/
+ filter->fir_abs_prev_depth_x = filter->fir_abs_depth_x;
+
+ return average_x;
+}
+
+static int cyapa_filter_fir_abs_y(struct cyapa_i2c *touch)
+{
+ int i;
+ int sum_y, average_y;
+ int effective_max_depth;
+ struct cyapa_cursor_filters *filter = &touch->cursor_filters;
+ struct mouse_ballistic_params *mouse_ballistic
+ = &touch->preferences.mouse_ballistic;
+
+ effective_max_depth = filter->fir_effective_max_depth;
+
+ /* set slow speed FIR depth. */
+ if ((effective_max_depth < filter->fir_abs_depth_y)
+ && (effective_max_depth < 2)) {
+ filter->fir_abs_depth_y = effective_max_depth;
+ filter->fir_abs_depth_slew_counter_y = 0;
+ }
+
+ if (effective_max_depth > filter->fir_abs_depth_y) {
+ /* count depth change slew value, if less or equal to 0,
+ ** make depth changed and reset slew value for next time. */
+ filter->fir_abs_depth_slew_counter_y--;
+ if (filter->fir_abs_depth_slew_counter_y <= 0) {
+ filter->fir_abs_depth_slew_counter_y
+ = mouse_ballistic->fir_abs_depth_slew_count;
+ filter->fir_abs_depth_y++;
+ }
+ }
+
+ if (effective_max_depth < filter->fir_abs_depth_y) {
+ /* count depth change slew value, if less or equal to 0,
+ ** make depth changed and reset slew value for next time. */
+ filter->fir_abs_depth_slew_counter_y--;
+ if (filter->fir_abs_depth_slew_counter_y <= 0) {
+ filter->fir_abs_depth_slew_counter_y
+ = mouse_ballistic->fir_abs_depth_slew_count;
+ filter->fir_abs_depth_y--;
+ }
+ }
+
+ /* insert new Y data. */
+ for (i = mouse_ballistic->fir_abs_depth_max-1; i > 0; i--)
+ filter->fir_vectors_y[i] = filter->fir_vectors_y[i-1];
+ filter->fir_vectors_y[0] = touch->abs_y;
+
+ /* calculate and apply ABS FIR filter algorithm to Y data. */
+ for (i = 0; i < filter->fir_abs_depth_y; i++)
+ sum_y += filter->fir_vectors_y[i];
+
+ if (filter->fir_abs_depth_y > 1) {
+ /* rescale modulus based on ABS FIR depth. */
+ filter->fir_y_mod = cyapa_fir_rescale_modulus(filter->fir_y_mod,
+ filter->fir_abs_prev_depth_y, filter->fir_abs_depth_y);
+ average_y = (sum_y + filter->fir_y_mod)
+ / filter->fir_abs_depth_y;
+ filter->fir_y_mod = (sum_y + filter->fir_y_mod)
+ % filter->fir_abs_depth_y;
+ } else {
+ average_y = touch->abs_y;
+ filter->fir_y_mod = 0;
+ }
+
+ /* store current ABS FIR depth.*/
+ filter->fir_abs_prev_depth_y = filter->fir_abs_depth_y;
+
+ return average_y;
+}
+
+static int cyapa_filter_iir_abs(struct cyapa_i2c *touch,
+ int *prev_iir,
+ int *prev_iir_mod,
+ int pos,
+ int numerator,
+ int denominator)
+{
+ int old_iir;
+ int new_iir;
+ int iir_mod_prev;
+ int iir_mod_next;
+
+ if (*prev_iir == 0xFFFFFFFF) {
+ *prev_iir = pos;
+ *prev_iir_mod = 0;
+ return pos;
+ }
+
+ if ((numerator == 1) && (denominator == 1)) {
+ *prev_iir = pos;
+ *prev_iir_mod = 0;
+ return pos;
+ }
+
+ if ((numerator > denominator) || (denominator <= 0)) {
+ *prev_iir = pos;
+ *prev_iir_mod = 0;
+ return pos;
+ }
+
+ old_iir = *prev_iir;
+ iir_mod_prev = *prev_iir_mod;
+
+ /* calculate and apply IIR filter to ABS postion value 'pos'. */
+ new_iir = old_iir * numerator;
+ pos = pos * (denominator - numerator);
+ new_iir = new_iir + pos + iir_mod_prev;
+ iir_mod_next = new_iir % denominator;
+ new_iir = new_iir / denominator;
+
+ /* store current IIR value and IIR mode value
+ ** for next time calculating.*/
+ *prev_iir = new_iir;
+ *prev_iir_mod = iir_mod_next;
+
+ return new_iir;
+}
+
+inline int cyapa_compute_rel_motion(int abs_pos, int prev_abs_pos)
+{
+ if (prev_abs_pos == -1)
+ return 0;
+
+ return abs_pos - prev_abs_pos;
+}
+
+static int cyapa_rel_get_accel_stoke_type(struct cyapa_i2c *touch,
+ int delta_x,
+ int delta_y)
+{
+ int i;
+ int rel_stroke_speed;
+ int speed_x, speed_y;
+ int max_speed_x, max_speed_y;
+ struct cyapa_cursor_filters *filter = &touch->cursor_filters;
+ struct mouse_ballistic_params *mouse_ballistic
+ = &touch->preferences.mouse_ballistic;
+
+ /* verify mouse ballistic setting for stroke history depth,
+ ** update it if invliad. */
+ if (mouse_ballistic->rel_stroke_history_depth
+ > (REL_STROKE_HISTORY_RECS-1)) {
+ mouse_ballistic->rel_stroke_history_depth
+ = REL_STROKE_HISTORY_RECS - 1;
+ }
+
+ for (i = mouse_ballistic->rel_stroke_history_depth-1; i > 0; i--) {
+ filter->rel_stroke_history[i].x
+ = filter->rel_stroke_history[i-1].x;
+ filter->rel_stroke_history[i].y
+ = filter->rel_stroke_history[i-1].y;
+ }
+ filter->rel_stroke_history[0].x = abs(delta_x);
+ filter->rel_stroke_history[0].y = abs(delta_y);
+
+ /* upate storke depth. */
+ if (filter->rel_stroke_depth
+ < mouse_ballistic->rel_stroke_history_depth) {
+ filter->rel_stroke_depth++;
+ } else {
+ filter->rel_stroke_depth
+ = mouse_ballistic->rel_stroke_history_depth;
+ }
+
+ /* find max speed in stroke history. */
+ max_speed_x = max_speed_y = -100000;
+ for (i = 0; i < filter->rel_stroke_depth; i++) {
+ speed_x = filter->rel_stroke_history[i].x;
+ speed_y = filter->rel_stroke_history[i].y;
+
+ if (speed_x > max_speed_x)
+ max_speed_x = speed_x;
+
+ if (speed_y > max_speed_y)
+ max_speed_y = speed_y;
+ }
+
+ if (max_speed_y > max_speed_x)
+ max_speed_x = max_speed_y;
+
+ /* sort storke speed range based on max stroke history speed. */
+ if (max_speed_x < mouse_ballistic->rel_medium_speed_threshold) {
+ rel_stroke_speed = REL_STROKE_SPEED_SLOW;
+ } else if (max_speed_x > mouse_ballistic->rel_fast_speed_threshold) {
+ if (max_speed_x < mouse_ballistic->rel_flick_speed_threshold)
+ rel_stroke_speed = REL_STROKE_SPEED_FAST;
+ else
+ rel_stroke_speed = REL_STROKE_SPEED_WARP;
+ } else {
+ rel_stroke_speed = REL_STROKE_SPEED_MEDIUM;
+ }
+
+ return rel_stroke_speed;
+}
+
+int cyapa_filter_iir_rel(struct cyapa_i2c *touch,
+ int rel_accel_pos,
+ int *rel_prev_accel_pos,
+ int *rel_prev_accel_iir_mod)
+{
+ int old_iir;
+ int new_iir;
+ int iir_mod_prev;
+ int iir_mod_next;
+ int iir_numerator, iir_denominator;
+ struct mouse_ballistic_params *mouse_ballistic
+ = &touch->preferences.mouse_ballistic;
+
+ iir_numerator = mouse_ballistic->rel_iir_acceleration_numerator;
+ iir_denominator = mouse_ballistic->rel_iir_acceleration_denominator;
+
+ old_iir = *rel_prev_accel_pos;
+ iir_mod_prev = *rel_prev_accel_iir_mod;
+
+ if ((iir_denominator > 0) && (iir_denominator != iir_numerator)) {
+ if (old_iir == CYAPA_FILTER_EMPTY_DATA) {
+ old_iir = rel_accel_pos;
+ iir_mod_prev = 0;
+ }
+
+ old_iir *= iir_numerator;
+ new_iir = rel_accel_pos * (iir_denominator - iir_numerator);
+ new_iir = new_iir + old_iir + iir_mod_prev;
+ iir_mod_next = new_iir % iir_denominator;
+ new_iir = new_iir / iir_denominator;
+
+ *rel_prev_accel_pos = new_iir;
+ *rel_prev_accel_iir_mod = iir_mod_next;
+ } else {
+ *rel_prev_accel_pos = new_iir = rel_accel_pos;
+ *rel_prev_accel_iir_mod = 0;
+ }
+
+ return new_iir;
+}
+
+/* Adjusts the Y data as reported by the trackpad FW to meet the shape
+** of the user's screen. This is useful to meet some customer test
+** requirements, where a diagonal stroke on the TP is supposed to
+** cause a like-angled diaginal stroke on the PC display device.
+*/
+inline int cyapa_filter_platform_display_aspect_ratio_adjust(
+ struct cyapa_i2c *touch,
+ int abs_y)
+{
+ struct mouse_ballistic_params *mouse_ballistic
+ = &touch->preferences.mouse_ballistic;
+
+ abs_y *= mouse_ballistic->abs_rise_y;
+ if ((mouse_ballistic->abs_run_y > 1)
+ && (mouse_ballistic->abs_run_y < 10000)) {
+ abs_y = abs_y / mouse_ballistic->abs_run_y;
+ }
+
+ return abs_y;
+}
+
+static void cyapa_filter_cursor_movement(struct cyapa_i2c *touch,
+ struct cyapa_report_data *report_data)
+{
+ int abs_x, abs_y;
+ int delta_x, delta_y;
+ int speed_numerator, speed_denominator;
+ int acceleration_vector_amplitude = 1;
+ int acceleration_denominator;
+ int delta_acceleration_x = 0;
+ int delta_acceleration_y = 0;
+ struct cyapa_cursor_filters *filter = &touch->cursor_filters;
+ struct mouse_ballistic_params *mouse_ballistic
+ = &touch->preferences.mouse_ballistic;
+
+ /*
+ ** 1. Input ABS raw data.
+ */
+ touch->abs_x = abs_x = report_data->touchs[0].x;
+ touch->abs_y = abs_y = report_data->touchs[0].y;
+
+ /* adjust Y data as reported by firware to meet the
+ ** shape of the usre's screen firstly. */
+ touch->abs_y = cyapa_filter_platform_display_aspect_ratio_adjust(touch,
+ touch->abs_y);
+
+ /*
+ ** 2. Apply FIR filter to ABS raw data.
+ */
+ if (mouse_ballistic->fir_abs_enabled) {
+ cyapa_fir_reset_fifo_depth(touch);
+ abs_x = cyapa_filter_fir_abs_x(touch);
+ abs_y = cyapa_filter_fir_abs_y(touch);
+ }
+
+ /*
+ ** 3. Apply IIR filter to FIR filtered ABS data.
+ */
+ if (mouse_ballistic->iir_abs_enabled) {
+ abs_x = cyapa_filter_iir_abs(touch,
+ &filter->iir_x,
+ &filter->iir_mod_x,
+ abs_x,
+ mouse_ballistic->iir_numerator,
+ mouse_ballistic->iir_denominator);
+ abs_y = cyapa_filter_iir_abs(touch,
+ &filter->iir_y,
+ &filter->iir_mod_y,
+ abs_y,
+ mouse_ballistic->iir_numerator,
+ mouse_ballistic->iir_denominator);
+ touch->abs_x = abs_x;
+ touch->abs_y = abs_y;
+ }
+
+ /*
+ ** 4. Calculate delta movement value.
+ */
+ delta_x = cyapa_compute_rel_motion(touch->abs_x, touch->prev_abs_x);
+ delta_y = cyapa_compute_rel_motion(touch->abs_y, touch->prev_abs_y);
+ touch->prev_abs_x = touch->abs_x;
+ touch->prev_abs_y = touch->abs_y;
+
+ /*
+ ** 5. Apply speed/acceleration control.
+ */
+ /* Obtain relative movement speed category
+ ** for use in computing speed coefficients. */
+ filter->rel_stroke_speed
+ = cyapa_rel_get_accel_stoke_type(touch, delta_x, delta_y);
+
+ /* Compute Acceleration Component. */
+ if ((touch->prev_rel_x == CYAPA_FILTER_EMPTY_DATA)
+ || (touch->prev_rel_y == CYAPA_FILTER_EMPTY_DATA)) {
+ /* debounce before start filter.*/
+ touch->prev_rel_x = delta_x;
+ touch->prev_rel_y = delta_y;
+ filter->rel_residue_x = 0;
+ filter->rel_residue_y = 0;
+ filter->rel_prev_accel_iir_x = CYAPA_FILTER_EMPTY_DATA;
+ filter->rel_prev_accel_iir_y = CYAPA_FILTER_EMPTY_DATA;
+ filter->rel_prev_accel_iir_mod_x = 0;
+ filter->rel_prev_accel_iir_mod_y = 0;
+ filter->rel_prev_residue_accel_x = 0;
+ filter->rel_prev_residue_accel_y = 0;
+
+ touch->rel_x = delta_x;
+ touch->rel_y = delta_y;
+ report_data->rel_deltaX = delta_x;
+ report_data->rel_deltaY = delta_y;
+
+ return;
+ }
+
+ /* sort speed numerator and denominator. */
+ if (filter->rel_stroke_speed == REL_STROKE_SPEED_SLOW) {
+ speed_numerator = mouse_ballistic->rel_motion_numerator;
+ speed_denominator = mouse_ballistic->rel_motion_denominator;
+ } else {
+ speed_numerator = mouse_ballistic->rel_medium_speed_numerator;
+ speed_denominator = mouse_ballistic->rel_motion_denominator;
+ }
+
+ /* apply speed/acceleration control. */
+ delta_x *= speed_numerator;
+ delta_y *= speed_numerator;
+
+ if (speed_denominator > 1) {
+ delta_x += filter->rel_residue_x;
+ filter->rel_residue_x = delta_x % speed_denominator;
+ delta_x /= speed_denominator;
+
+ delta_y += filter->rel_residue_y;
+ filter->rel_residue_y = delta_y % speed_denominator;
+ delta_y /= speed_denominator;
+ }
+
+ if (filter->rel_stroke_speed >= REL_STROKE_SPEED_FAST) {
+ acceleration_vector_amplitude = 10;
+ acceleration_vector_amplitude
+ *= mouse_ballistic->rel_acceleration_numerator;
+ acceleration_denominator
+ = mouse_ballistic->rel_acceleration_denominator;
+
+ touch->prev_rel_x = delta_x;
+ touch->prev_rel_y = delta_y;
+
+ delta_acceleration_x = delta_x * acceleration_vector_amplitude;
+ delta_acceleration_y = delta_y * acceleration_vector_amplitude;
+
+ if (acceleration_denominator > 1) {
+ delta_acceleration_x
+ += filter->rel_prev_residue_accel_x;
+ filter->rel_prev_residue_accel_x = delta_acceleration_x
+ % acceleration_denominator;
+ delta_acceleration_x /= acceleration_denominator;
+
+ delta_acceleration_y
+ += filter->rel_prev_residue_accel_y;
+ filter->rel_prev_residue_accel_y = delta_acceleration_y
+ % acceleration_denominator;
+ delta_acceleration_y /= acceleration_denominator;
+ }
+
+ delta_x = delta_acceleration_x;
+ delta_y = delta_acceleration_y;
+ }
+
+ /*
+ ** 6. Apply IIR filter to relative data.
+ */
+ delta_x = cyapa_filter_iir_rel(touch, delta_x,
+ &filter->rel_prev_accel_iir_x,
+ &filter->rel_prev_accel_iir_mod_x);
+ delta_y = cyapa_filter_iir_rel(touch, delta_y,
+ &filter->rel_prev_accel_iir_y,
+ &filter->rel_prev_accel_iir_mod_y);
+
+ if (delta_x > mouse_ballistic->rel_max_acceleration_speed)
+ delta_x = mouse_ballistic->rel_max_acceleration_speed;
+ else if (delta_x < -mouse_ballistic->rel_max_acceleration_speed)
+ delta_x = -mouse_ballistic->rel_max_acceleration_speed;
+
+ if (delta_y > mouse_ballistic->rel_max_acceleration_speed)
+ delta_y = mouse_ballistic->rel_max_acceleration_speed;
+ else if (delta_y < -mouse_ballistic->rel_max_acceleration_speed)
+ delta_y = -mouse_ballistic->rel_max_acceleration_speed;
+
+ touch->rel_x = delta_x;
+ touch->rel_y = delta_y;
+ report_data->rel_deltaX = delta_x;
+ report_data->rel_deltaY = delta_y;
+
+ return;
+}
+
+static inline void cyapa_report_fingers(struct input_dev *input, int fingers)
+{
+ if (fingers) {
+ input_report_key(input, BTN_TOOL_FINGER, (fingers == 1));
+ input_report_key(input, BTN_TOOL_DOUBLETAP, (fingers == 2));
+ input_report_key(input, BTN_TOOL_TRIPLETAP, (fingers == 3));
+ input_report_key(input, BTN_TOOL_QUADTAP, (fingers > 3));
+ } else {
+ input_report_key(input, BTN_TOOL_FINGER, 0);
+ input_report_key(input, BTN_TOOL_DOUBLETAP, 0);
+ input_report_key(input, BTN_TOOL_TRIPLETAP, 0);
+ input_report_key(input, BTN_TOOL_QUADTAP, 0);
+ }
+}
+
+static void cyapa_process_prev_gesture_report(struct cyapa_i2c *touch,
+ struct cyapa_report_data *report_data)
+{
+ int i, j;
+ unsigned long gesture_diff;
+ struct input_dev *input = touch->input;
+ struct input_dev *input_kbd = touch->input_kbd;
+
+ for (i = 0; i < MAX_FINGERS; i++) {
+ /* get all diffenent gestures in prev and cur. */
+ gesture_diff
+ = touch->prev_active_gestures[i]
+ ^ touch->cur_active_gestures[i];
+ /* get all prev gestures that has been canceled in cur. */
+ gesture_diff = gesture_diff & touch->prev_active_gestures[i];
+ if (gesture_diff) {
+ for (j = 0; j < (sizeof(unsigned long)*8); j++) {
+ /* cancel previous exists gesture. */
+ if ((gesture_diff >> j) & 1UL) {
+ switch (GESTURE_ID_CODE(i, j)) {
+ case GESTURE_PALM_REJECTIOIN:
+ break;
+ case GESTURE_SINGLE_TAP:
+ break;
+ case GESTURE_DOUBLE_TAP:
+ break;
+ case GESTURE_TAP_AND_HOLD:
+ break;
+ case GESTURE_EDGE_MOTION:
+ break;
+ case GESTURE_DRAG:
+ touch->prev_abs_x = -1;
+ touch->prev_abs_y = -1;
+
+ if (touch->platform_data
+ ->use_absolute_mode) {
+ input_report_key(input,
+ BTN_TOUCH, 0);
+ input_report_abs(input,
+ ABS_PRESSURE,
+ 0);
+ cyapa_report_fingers(
+ input, 0);
+ input_report_key(input,
+ BTN_LEFT, 0);
+ input_sync(input);
+ }
+
+ cyapa_reset_cursor_filters_data(
+ touch);
+
+ break;
+ case GESTURE_2F_ZOOM_IN:
+ touch->zoomin_delta = 0;
+ touch->zoom_trigged = 0;
+ input_report_key(input_kbd,
+ KEY_LEFTCTRL, 1);
+ input_sync(input_kbd);
+ input_report_key(input_kbd,
+ KEY_LEFTCTRL, 0);
+ input_sync(input_kbd);
+ break;
+ case GESTURE_2F_ZOOM_OUT:
+ touch->zoomout_delta = 0;
+ touch->zoom_trigged = 0;
+ input_report_key(input_kbd,
+ KEY_LEFTCTRL, 1);
+ input_sync(input_kbd);
+ input_report_key(input_kbd,
+ KEY_LEFTCTRL, 0);
+ input_sync(input_kbd);
+ break;
+ case GESTURE_SCROLL_UP:
+ case GESTURE_2F_SCROLL_UP:
+ touch->delta_scroll_up = 0;
+ break;
+ case GESTURE_SCROLL_DOWN:
+ case GESTURE_2F_SCROLL_DOWN:
+ touch->delta_scroll_down = 0;
+ break;
+ case GESTURE_SCROLL_LEFT:
+ case GESTURE_2F_SCROLL_LEFT:
+ input_report_key(input_kbd,
+ KEY_LEFTSHIFT, 1);
+ input_sync(input_kbd);
+ input_report_key(input_kbd,
+ KEY_LEFTSHIFT, 0);
+ input_sync(input_kbd);
+ touch->hscroll_canceled = 1;
+ touch->hscroll_left = 0;
+ touch->delta_scroll_left = 0;
+ break;
+ case GESTURE_SCROLL_RIGHT:
+ case GESTURE_2F_SCROLL_RIGHT:
+ input_report_key(input_kbd,
+ KEY_LEFTSHIFT, 1);
+ input_sync(input_kbd);
+ input_report_key(input_kbd,
+ KEY_LEFTSHIFT, 0);
+ input_sync(input_kbd);
+ touch->hscroll_canceled = 1;
+ touch->hscroll_right = 0;
+ touch->delta_scroll_right = 0;
+ break;
+ case GESTURE_2F_ROTATE:
+ break;
+ case GESTURE_2F_PINCH:
+ break;
+ case GESTURE_2F_TAP:
+ break;
+ case GESTURE_2F_DRAG:
+ if (touch->platform_data
+ ->use_absolute_mode) {
+ input_report_key(input,
+ BTN_TOUCH, 0);
+ input_report_abs(input,
+ ABS_PRESSURE,
+ 0);
+ input_report_key(input,
+ BTN_LEFT, 0);
+ cyapa_report_fingers(
+ input, 0);
+ input_sync(input);
+ }
+
+ touch->gesture_2F_drag_started
+ = 0;
+ touch->prev_abs_x = -1;
+ touch->prev_abs_y = -1;
+ break;
+ case GESTURE_FLICK:
+ case GESTURE_2F_FLICK:
+ case GESTURE_3F_FLICK:
+ case GESTURE_4F_FLICK:
+ case GESTURE_5F_FLICK:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void cyapa_gesture_report(struct cyapa_i2c *touch,
+ struct cyapa_report_data *report_data,
+ struct cyapa_gesture *gesture)
+{
+ struct input_dev *input = touch->input;
+ struct input_dev *input_wheel = touch->input_wheel;
+ struct input_dev *input_kbd = touch->input_kbd;
+ int delta = 0;
+ struct cyapa_preferences *preferences = &touch->preferences;
+ int threshold = 0;
+ int value = 0;
+
+ switch (gesture->id) {
+ case GESTURE_PALM_REJECTIOIN:
+ /* when palm rejection gesture is trigged, do not move cursor
+ ** any more, just operation as no finger touched on trackpad.
+ */
+ if (touch->platform_data->use_absolute_mode) {
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_report_abs(input, ABS_TOOL_WIDTH, 0);
+ cyapa_report_fingers(input, 0);
+ }
+
+ touch->prev_abs_x = -1;
+ touch->prev_abs_y = -1;
+
+ input_report_key(input, BTN_LEFT, report_data->button & 0x01);
+ input_report_key(input, BTN_RIGHT, report_data->button & 0x02);
+ input_report_key(input, BTN_MIDDLE, report_data->button & 0x04);
+
+ input_sync(input);
+
+ DBGPRINTK(("%s: report palm rejection\n", __func__));
+ break;
+ case GESTURE_SINGLE_TAP:
+ if (touch->platform_data->use_absolute_mode) {
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_report_key(input, BTN_LEFT, 0);
+ input_sync(input);
+
+ /* in absolute mode use cyapa_report_fingers(input, N)
+ ** to trigger click.
+ ** when N change from small to large,
+ ** clieck with be trigged.*/
+ break;
+ }
+
+ input_report_key(input, BTN_LEFT, 1);
+ input_sync(input);
+
+ input_report_key(input, BTN_LEFT, 0);
+ input_sync(input);
+
+ DBGPRINTK(("%s: report single tap\n", __func__));
+ break;
+ case GESTURE_DOUBLE_TAP:
+ if (touch->platform_data->use_absolute_mode) {
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_report_key(input, BTN_LEFT, 0);
+ input_report_key(input, BTN_RIGHT, 0);
+ input_sync(input);
+ }
+
+ input_report_key(input, BTN_LEFT, 1);
+ input_sync(input);
+
+ input_report_key(input, BTN_LEFT, 0);
+ input_sync(input);
+
+ input_report_key(input, BTN_LEFT, 1);
+ input_sync(input);
+
+ input_report_key(input, BTN_LEFT, 0);
+ input_sync(input);
+
+ DBGPRINTK(("%s: report double tap\n", __func__));
+ break;
+ case GESTURE_TAP_AND_HOLD:
+ /* one finger click and hold for more than definitioin time,
+ ** then to do something. */
+ DBGPRINTK(("%s: no gesture for Tap and hold yet.\n", __func__));
+ break;
+ case GESTURE_EDGE_MOTION:
+ DBGPRINTK(("%s: no gesture for edge motion yet.\n", __func__));
+ break;
+ case GESTURE_DRAG:
+ /* 1-finger drag. 1-finger double click and hold,
+ ** then move the finger. */
+ if (touch->platform_data->use_absolute_mode) {
+ touch->xy_touchs_included_bits = 0x01;
+ cyapa_calculate_abs_xy(touch, report_data);
+
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_X, touch->abs_x);
+ input_report_abs(input, ABS_Y, touch->abs_y);
+ input_report_abs(input, ABS_PRESSURE,
+ report_data->avg_pressure);
+ cyapa_report_fingers(input, 1);
+ input_report_key(input, BTN_LEFT, 1);
+ input_sync(input);
+ } else {
+ cyapa_filter_cursor_movement(touch, report_data);
+
+ input_report_rel(input, REL_X, report_data->rel_deltaX);
+ input_report_rel(input, REL_Y, report_data->rel_deltaY);
+ input_report_key(input, BTN_LEFT, 1);
+ input_sync(input);
+ }
+
+ DBGPRINTK(("%s: 1 finger drag.\n", __func__));
+ break;
+ case GESTURE_2F_ZOOM_IN:
+ delta = gesture->param2;
+ /* no zoom delta. */
+ if (delta <= 0)
+ break;
+ touch->zoomin_delta += delta;
+
+ if (touch->zoom_trigged == 0) {
+ input_report_key(input_kbd, KEY_LEFTCTRL, 0);
+ input_sync(input_kbd);
+ input_report_key(input_kbd, KEY_LEFTCTRL, 1);
+ input_sync(input_kbd);
+ touch->zoom_trigged = 1;
+ } else {
+ if (touch->zoomin_delta
+ <= preferences->zoom.default_threshold) {
+ threshold = 0;
+ value = 1;
+ } else if (touch->zoomin_delta
+ > preferences->zoom.fast_threshold) {
+ threshold = 2;
+ value = touch->zoomin_delta
+ / preferences->zoom.fast_threshold;
+ touch->zoomin_delta
+ %= preferences->zoom.fast_threshold;
+ } else {
+ threshold = 1;
+ value = 1;
+ touch->zoomin_delta = 0;
+ }
+
+ while (threshold > 0) {
+ input_report_rel(input_wheel, REL_WHEEL, value);
+ input_sync(input_wheel);
+ threshold--;
+ }
+ }
+
+ DBGPRINTK(("%s: 2F zoom in.\n", __func__));
+ break;
+ case GESTURE_2F_ZOOM_OUT:
+ delta = gesture->param2;
+ /* no zoom delta. */
+ if (delta <= 0)
+ break;
+ touch->zoomout_delta += delta;
+
+ if (touch->zoom_trigged == 0) {
+ input_report_key(input_kbd, KEY_LEFTCTRL, 0);
+ input_sync(input_kbd);
+ input_report_key(input_kbd, KEY_LEFTCTRL, 1);
+ input_sync(input_kbd);
+ touch->zoom_trigged = 1;
+ } else {
+ value = 1;
+ if (touch->zoomout_delta
+ <= preferences->zoom.default_threshold) {
+ threshold = 0;
+ value = 1;
+ } else if (touch->zoomout_delta
+ > preferences->zoom.fast_threshold) {
+ threshold = 2;
+ value = touch->zoomout_delta
+ / preferences->zoom.fast_threshold;
+ touch->zoomout_delta
+ %= preferences->zoom.fast_threshold;
+ } else {
+ threshold = 1;
+ value = 1;
+ touch->zoomout_delta = 0;
+ }
+
+ while (threshold > 0) {
+ input_report_rel(input_wheel,
+ REL_WHEEL,
+ -value);
+ input_sync(input_wheel);
+ threshold--;
+ }
+ }
+
+ DBGPRINTK(("%s: 2F zoom out.\n", __func__));
+ break;
+ case GESTURE_SCROLL_UP:
+ case GESTURE_2F_SCROLL_UP:
+ if (touch->hscroll_canceled) {
+ /* avoid VScroll miss trigged as HScroll. */
+ touch->hscroll_canceled = 0;
+ input_report_key(input_kbd, KEY_LEFTSHIFT, 0);
+ input_sync(input_kbd);
+
+ break;
+ }
+
+ delta = gesture->param2;
+ threshold = preferences->vscroll.default_threshold;
+ value = 1;
+ touch->delta_scroll_up += delta;
+
+ if (touch->delta_scroll_up <= 0) {
+ /* no scroll move, it's not need to report. */
+ touch->delta_scroll_up = 0;
+ break;
+ }
+
+ if (touch->delta_scroll_up < threshold) {
+ /* keep small movement also can work. */
+ input_report_rel(input_wheel, REL_WHEEL, value);
+ input_sync(input_wheel);
+
+ touch->delta_scroll_up = 0;
+ break;
+ }
+
+ if (touch->delta_scroll_up
+ > preferences->vscroll.fast_threshold) {
+ /* fast scroll, reset threshold value. */
+ threshold = 1;
+ value = 16;
+ } else {
+ /* middle scroll speed. */
+ threshold = 2;
+ value = 2;
+ }
+
+ while (touch->delta_scroll_up >= threshold) {
+ input_report_rel(input_wheel,
+ REL_WHEEL,
+ value*2/threshold);
+ input_sync(input_wheel);
+
+ touch->delta_scroll_up -= threshold*value;
+ }
+
+ DBGPRINTK(("%s: scroll up, fingers=%d\n",
+ __func__, report_data->touch_fingers));
+ break;
+ case GESTURE_SCROLL_DOWN:
+ case GESTURE_2F_SCROLL_DOWN:
+ if (touch->hscroll_canceled) {
+ /* avoid VScroll miss trigged as HScroll. */
+ touch->hscroll_canceled = 0;
+ input_report_key(input_kbd, KEY_LEFTSHIFT, 0);
+ input_sync(input_kbd);
+
+ break;
+ }
+
+ delta = gesture->param2;
+ threshold = preferences->vscroll.default_threshold;
+ value = 1;
+ touch->delta_scroll_down += delta;
+
+ if (touch->delta_scroll_down <= 0) {
+ /* no scroll move, it's not need to report. */
+ touch->delta_scroll_down = 0;
+ break;
+ }
+
+ if (touch->delta_scroll_down < threshold) {
+ /* keep small movement also can work. */
+ input_report_rel(input_wheel, REL_WHEEL, -value);
+ input_sync(input_wheel);
+
+ touch->delta_scroll_down = 0;
+ break;
+ }
+
+ if (touch->delta_scroll_down
+ > preferences->vscroll.fast_threshold) {
+ /* fast scroll, reset threshold value. */
+ threshold = 1;
+ value = 16;
+ } else {
+ /* middle scroll speed. */
+ threshold = 2;
+ value = 2;
+ }
+
+ while (touch->delta_scroll_down >= threshold) {
+ input_report_rel(input_wheel,
+ REL_WHEEL,
+ -value*2/threshold);
+ input_sync(input_wheel);
+
+ touch->delta_scroll_down -= threshold*value;
+ }
+
+ DBGPRINTK(("%s: scroll down, finger=%d\n",
+ __func__, report_data->touch_fingers));
+ break;
+ case GESTURE_SCROLL_LEFT:
+ case GESTURE_2F_SCROLL_LEFT:
+ delta = gesture->param2;
+ if (0 == touch->hscroll_left) {
+ /* to report left shift firstly inorder to avoid miss
+ ** trig vscroll. because, for 0.9.101 chromium os,
+ ** it seems that when left shift is pressed,
+ ** then scroll soon, the combined gesture won't
+ ** take effect soon, it will have some delay.
+ ** So add a debounce to avoid this issue.
+ */
+ input_report_key(input_kbd, KEY_LEFTSHIFT, 0);
+ input_sync(input_kbd);
+ input_report_key(input_kbd, KEY_LEFTSHIFT, 1);
+ input_sync(input_kbd);
+ touch->hscroll_left = delta;
+ } else {
+ threshold = preferences->hscroll.default_threshold;
+ value = 1;
+ touch->delta_scroll_left += delta;
+
+ if (touch->delta_scroll_left <= 0) {
+ /* no scroll move, it's not need to report. */
+ touch->delta_scroll_left = 0;
+ break;
+ }
+
+ if (touch->delta_scroll_left < threshold) {
+ /* keep small movement also can work. */
+ input_report_rel(input_wheel, REL_WHEEL, value);
+ input_sync(input_wheel);
+
+ touch->delta_scroll_left = 0;
+ break;
+ }
+
+ if (touch->delta_scroll_left
+ > preferences->hscroll.fast_threshold) {
+ /* fast scroll, reset threshold value. */
+ threshold = 1;
+ value = 16;
+ } else {
+ /* middle scroll speed. */
+ threshold = 2;
+ value = 2;
+ }
+
+ while (touch->delta_scroll_left >= threshold) {
+ input_report_rel(input_wheel,
+ REL_WHEEL,
+ value*2/threshold);
+ input_sync(input_wheel);
+
+ touch->delta_scroll_left -= threshold*value;
+ }
+ }
+
+ DBGPRINTK(("%s: scroll left, finger=%d\n",
+ __func__, report_data->touch_fingers));
+ break;
+ case GESTURE_SCROLL_RIGHT:
+ case GESTURE_2F_SCROLL_RIGHT:
+ delta = gesture->param2;
+ if (0 == touch->hscroll_right) {
+ input_report_key(input_kbd, KEY_LEFTSHIFT, 0);
+ input_sync(input_kbd);
+ input_report_key(input_kbd, KEY_LEFTSHIFT, 1);
+ input_sync(input_kbd);
+ touch->hscroll_right = delta;
+ } else {
+ threshold = preferences->hscroll.default_threshold;
+ value = 1;
+ touch->delta_scroll_right += delta;
+
+ if (touch->delta_scroll_right <= 0) {
+ /* no scroll move, it's not need to report. */
+ touch->delta_scroll_right = 0;
+ break;
+ }
+
+ if (touch->delta_scroll_right < threshold) {
+ /* keep small movement also can work. */
+ input_report_rel(input_wheel,
+ REL_WHEEL,
+ -value);
+ input_sync(input_wheel);
+
+ touch->delta_scroll_right = 0;
+ break;
+ }
+
+ if (touch->delta_scroll_right
+ > preferences->hscroll.fast_threshold) {
+ /* fast scroll, reset threshold value. */
+ threshold = 1;
+ value = 16;
+ } else {
+ /* middle scroll speed. */
+ threshold = 2;
+ value = 2;
+ }
+
+ while (touch->delta_scroll_right >= threshold) {
+ input_report_rel(input_wheel,
+ REL_WHEEL,
+ -value*2/threshold);
+ input_sync(input_wheel);
+
+ touch->delta_scroll_right -= threshold*value;
+ }
+ }
+
+ DBGPRINTK(("%s: scroll right, finger=%d\n",
+ __func__, report_data->touch_fingers));
+ break;
+ case GESTURE_2F_ROTATE:
+ DBGPRINTK(("%s: 2 finger rotate.\n", __func__));
+ break;
+ case GESTURE_2F_PINCH:
+ DBGPRINTK(("%s: 2 finger pinch.\n", __func__));
+ break;
+ case GESTURE_2F_TAP:
+ /* 2-finger tap, active like right button press and relase. */
+ if (touch->platform_data->use_absolute_mode) {
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_report_key(input, BTN_LEFT, 0);
+ input_report_key(input, BTN_RIGHT, 0);
+ input_sync(input);
+ }
+
+ input_report_key(input, BTN_RIGHT, 1);
+ input_sync(input);
+
+ input_report_key(input, BTN_RIGHT, 0);
+ input_sync(input);
+
+ DBGPRINTK(("%s: report 2 fingers tap, \
+ active like right button.\n", __func__));
+ break;
+ case GESTURE_2F_DRAG:
+ /* first finger click and hold,
+ ** and second finger moving for dragging. */
+ if (touch->gesture_2F_drag_started == 0) {
+ touch->xy_touchs_included_bits = 0x01;
+ touch->prev_abs_x = -1;
+ touch->prev_abs_y = -1;
+ cyapa_calculate_abs_xy(touch, report_data);
+
+ /* firstly, move move cursor to the target for drag. */
+ input_report_key(input, BTN_TOUCH, 1);
+ if (touch->platform_data->use_absolute_mode) {
+ input_report_abs(input, ABS_X, touch->abs_x);
+ input_report_abs(input, ABS_Y, touch->abs_y);
+ input_report_abs(input,
+ ABS_PRESSURE,
+ report_data->avg_pressure);
+ cyapa_report_fingers(input, 1);
+ }
+ input_report_key(input, BTN_LEFT, 0);
+ input_report_key(input, BTN_RIGHT, 0);
+ input_sync(input);
+
+ /* second, stop cursor on the target for drag. */
+ touch->prev_abs_x = -1;
+ touch->prev_abs_y = -1;
+ if (touch->platform_data->use_absolute_mode) {
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_sync(input);
+ }
+
+ /* third, select the target for drag. */
+ input_report_key(input, BTN_LEFT, 1);
+ input_sync(input);
+
+ /* go to step four. */
+ touch->gesture_2F_drag_started = 1;
+ }
+
+ /* fourth, move cursor for dragging. */
+ touch->xy_touchs_included_bits = 0x02;
+ cyapa_calculate_abs_xy(touch, report_data);
+
+ if (touch->platform_data->use_absolute_mode) {
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_X, touch->abs_x);
+ input_report_abs(input, ABS_Y, touch->abs_y);
+ input_report_abs(input,
+ ABS_PRESSURE,
+ report_data->avg_pressure);
+ cyapa_report_fingers(input, 1);
+ } else {
+ input_report_rel(input, REL_X, report_data->rel_deltaX);
+ input_report_rel(input, REL_Y, report_data->rel_deltaY);
+ input_sync(input);
+ }
+ input_report_key(input, BTN_LEFT, 1);
+ input_sync(input);
+
+ DBGPRINTK(("%s: report 2 fingers drag\n", __func__));
+ break;
+ case GESTURE_FLICK:
+ case GESTURE_2F_FLICK:
+ case GESTURE_3F_FLICK:
+ case GESTURE_4F_FLICK:
+ case GESTURE_5F_FLICK:
+ touch->xy_touchs_included_bits = report_data->touch_fingers;
+ DBGPRINTK(("%s: no flick gesture supported yet, , finger=%d\n",
+ __func__, report_data->touch_fingers));
+ break;
+ default:
+ DBGPRINTK(("%s: default, unknown gesture for reporting.\n",
+ __func__));
+ break;
+ }
+}
+
+static int cyapa_rel_input_report_data(struct cyapa_i2c *touch,
+ struct cyapa_report_data *report_data)
+{
+ int i;
+ struct input_dev *input = touch->input;
+
+ /* step 1: process gestures firstly if trigged. */
+ cyapa_process_prev_gesture_report(touch, report_data);
+ if (report_data->gestures_count > 0) {
+ DBGPRINTK(("%s: do gesture report, gestures_count = %d\n",
+ __func__, report_data->gestures_count));
+ /* gesture trigged */
+ for (i = 0; i < report_data->gestures_count; i++) {
+ cyapa_gesture_report(touch,
+ report_data,
+ &report_data->gestures[i]);
+ }
+
+ /* when gestures are trigged, cursor should not move. */
+ } else {
+ /* when multi-fingers touched, cursour should also not move. */
+ if (report_data->touch_fingers == 1) {
+ /* apply cursor movement filters to
+ ** improve cursor performance. */
+ cyapa_filter_cursor_movement(touch, report_data);
+
+ /* Report the deltas */
+ input_report_rel(input, REL_X, report_data->rel_deltaX);
+ input_report_rel(input, REL_Y, report_data->rel_deltaY);
+ } else {
+ cyapa_reset_cursor_filters_data(touch);
+ }
+
+ /* Report the button event */
+ input_report_key(input, BTN_LEFT,
+ (report_data->button & 0x01));
+ input_report_key(input, BTN_RIGHT,
+ (report_data->button & 0x02));
+ input_report_key(input, BTN_MIDDLE,
+ (report_data->button & 0x04));
+ input_sync(input);
+ }
+
+ DBGPRINTK(("%s: deltax = %d\n", __func__, report_data->rel_deltaX));
+ DBGPRINTK(("%s: deltay = %d\n", __func__, report_data->rel_deltaY));
+ DBGPRINTK(("%s: left_btn = %d\n",
+ __func__, report_data->button & 0x01));
+ DBGPRINTK(("%s: right_btn = %d\n",
+ __func__, report_data->button & 0x02));
+ DBGPRINTK(("%s: middle_btn = %d\n",
+ __func__, report_data->button & 0x04));
+
+ /* store current active gestures array into
+ ** prev active gesture array. */
+ for (i = 0; i < MAX_FINGERS; i++)
+ touch->prev_active_gestures[i] = touch->cur_active_gestures[i];
+ touch->prev_touch_fingers = report_data->touch_fingers;
+
+ return report_data->gestures_count | report_data->rel_deltaX
+ |report_data->rel_deltaY | report_data->button;
+}
+
+static int cyapa_abs_input_report_data(struct cyapa_i2c *touch,
+ struct cyapa_report_data *report_data)
+{
+ int i;
+ int have_data = 0;
+ struct input_dev *input = touch->input;
+
+ DBGPRINTK(("%s: ...\n", __func__));
+
+ cyapa_process_prev_gesture_report(touch, report_data);
+ if (report_data->gestures_count > 0) {
+ DBGPRINTK(("%s: do gesture report, gestures_count = %d\n",
+ __func__, report_data->gestures_count));
+ /* gesture trigged */
+ for (i = 0; i < report_data->gestures_count; i++) {
+ cyapa_gesture_report(touch, report_data,
+ &report_data->gestures[i]);
+ }
+ } else if (report_data->touch_fingers) {
+ /* no gesture trigged, report touchs move data. */
+ if (report_data->touch_fingers > 1) {
+ DBGPRINTK(("%s: more then 1 finger touch, \
+ touch_fingers = %d\n",
+ __func__, report_data->touch_fingers));
+ /*
+ ** two and much more finger on trackpad are used for
+ ** gesture only, so even no gesture are trigged,
+ ** do not make cursor move also.
+ ** Here, must keep on report finger touched, otherwise,
+ ** when multi-finger touch not in same time will
+ ** triiged clikc.
+ */
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_PRESSURE,
+ report_data->avg_pressure);
+ input_report_abs(input, ABS_TOOL_WIDTH,
+ CYAPA_TOOL_WIDTH);
+ #if GESTURE_MULTI_TOUCH_ONE_CLICK
+ cyapa_report_fingers(input, report_data->touch_fingers);
+ #else
+ cyapa_report_fingers(input, 1);
+ #endif
+
+ touch->prev_abs_x = -1;
+ touch->prev_abs_y = -1;
+
+ input_report_key(input, BTN_LEFT,
+ report_data->button & 0x01);
+ input_report_key(input, BTN_RIGHT,
+ report_data->button & 0x02);
+ input_report_key(input, BTN_MIDDLE,
+ report_data->button & 0x04);
+
+ input_sync(input);
+ } else {
+ DBGPRINTK(("%s: 1 finger touch, make cursor move\n",
+ __func__));
+ /* avoid cursor jump, when touched finger changed
+ ** from multi-touch to one finger touch. */
+ if (touch->prev_touch_fingers > 1) {
+ /* cheat system or application that no finger
+ ** has touched to may them
+ ** lock the cursor when later only one finger
+ ** touched on trackpad. */
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_report_abs(input, ABS_TOOL_WIDTH, 0);
+ cyapa_report_fingers(input, 0);
+ touch->prev_abs_x = -1;
+ touch->prev_abs_y = -1;
+ input_report_key(input, BTN_LEFT,
+ report_data->button & 0x01);
+ input_report_key(input, BTN_RIGHT,
+ report_data->button & 0x02);
+ input_report_key(input, BTN_MIDDLE,
+ report_data->button & 0x04);
+ input_sync(input);
+ } else {
+ /* only 1 finger can make cursor move. */
+ touch->xy_touchs_included_bits = 0x01;
+ cyapa_calculate_abs_xy(touch, report_data);
+
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_X, touch->abs_x);
+ input_report_abs(input, ABS_Y, touch->abs_y);
+ input_report_abs(input,
+ ABS_PRESSURE,
+ report_data->avg_pressure);
+ input_report_abs(input, ABS_TOOL_WIDTH,
+ CYAPA_TOOL_WIDTH);
+
+ cyapa_report_fingers(input,
+ report_data->touch_fingers);
+
+ input_report_key(input, BTN_LEFT,
+ report_data->button & 0x01);
+ input_report_key(input, BTN_RIGHT,
+ report_data->button & 0x02);
+ input_report_key(input, BTN_MIDDLE,
+ report_data->button & 0x04);
+
+ input_sync(input);
+ }
+ }
+ } else {
+ /*
+ ** 1. two or more fingers on trackpad are used for gesture only,
+ ** so even no gesture are trigged, do not make cursor move also.
+ ** 2. no gesture and no touch on trackpad.
+ */
+ DBGPRINTK(("%s: no finger touch.\n", __func__));
+
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_report_abs(input, ABS_TOOL_WIDTH, 0);
+ cyapa_report_fingers(input, 0);
+
+ touch->prev_abs_x = -1;
+ touch->prev_abs_y = -1;
+
+ input_report_key(input, BTN_LEFT, report_data->button & 0x01);
+ input_report_key(input, BTN_RIGHT, report_data->button & 0x02);
+ input_report_key(input, BTN_MIDDLE, report_data->button & 0x04);
+
+ input_sync(input);
+ }
+
+ /* store current active gestures array into
+ ** prev active gesture array. */
+ for (i = 0; i < MAX_FINGERS; i++)
+ touch->prev_active_gestures[i] = touch->cur_active_gestures[i];
+ touch->prev_touch_fingers = report_data->touch_fingers;
+
+ have_data = (report_data->gestures_count +
+ report_data->touch_fingers + report_data->button);
+
+ DBGPRINTK(("%s: gesture count = %d, touch finger =%d, \
+ button = 0x%02x\n", __func__, report_data->gestures_count,
+ report_data->touch_fingers, report_data->button));
+ return have_data;
+}
+
+static bool cyapa_i2c_get_input(struct cyapa_i2c *touch)
+{
+ int i;
+ int ret_read_size = -1;
+ int read_length = 0;
+ union cyapa_reg_data reg_data;
+ struct cyapa_reg_data_gen1 *gen1_data;
+ struct cyapa_reg_data_gen2 *gen2_data;
+ struct cyapa_report_data report_data;
+
+ DBGPRINTK(("%s: start ...\n", __func__));
+
+ memset(&reg_data, 0, sizeof(union cyapa_reg_data));
+
+ /* read register data from trackpad. */
+ gen1_data = &reg_data.gen1_data;
+ gen2_data = &reg_data.gen2_data;
+ read_length = CYAPA_REL_REG_DATA_SIZE;
+ if (touch->platform_data->gen == CYAPA_GEN1)
+ read_length = (int)sizeof(struct cyapa_reg_data_gen1);
+ else
+ read_length = (int)sizeof(struct cyapa_reg_data_gen2);
+ DBGPRINTK(("%s: read gen%d data, read length=%d\n", __func__,
+ ((touch->platform_data->gen == CYAPA_GEN1) ? 1 : 2),
+ read_length));
+ ret_read_size = cyapa_i2c_reg_read_block(touch->client,
+ DATA_REG_START_OFFSET,
+ read_length,
+ (u8 *)&reg_data);
+ if (ret_read_size < 0) {
+ DBGPRINTK(("%s: I2C read data from trackpad error = %d\n",
+ __func__, ret_read_size));
+ return 0;
+ }
+
+ if (cyapa_verify_data_device(touch, &reg_data)) {
+ DBGPRINTK(("%s: verify data device failed, \
+ invalid data, skip.\n", __func__));
+ return 0;
+ }
+
+ /* process and parse raw data that read from Trackpad. */
+ memset(&report_data, 0, sizeof(struct cyapa_report_data));
+ touch->xy_touchs_included_bits = 0;
+ /* initialize current active gestures array. */
+ for (i = 0; i < MAX_FINGERS; i++)
+ touch->cur_active_gestures[i] = 0;
+
+ if (touch->platform_data->gen == CYAPA_GEN1)
+ cyapa_parse_gen1_data(touch, gen1_data, &report_data);
+ else
+ cyapa_parse_gen2_data(touch, gen2_data, &report_data);
+
+ /* report data to input subsystem. */
+ if (touch->platform_data->use_absolute_mode == false)
+ return cyapa_rel_input_report_data(touch, &report_data);
+ else
+ return cyapa_abs_input_report_data(touch, &report_data);
+}
+
+static void cyapa_i2c_reschedule_work(struct cyapa_i2c *touch,
+ unsigned long delay)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&touch->lock, flags);
+
+ /*
+ * If work is already scheduled then subsequent schedules will not
+ * change the scheduled time that's why we have to cancel it first.
+ */
+ __cancel_delayed_work(&touch->dwork);
+ schedule_delayed_work(&touch->dwork, delay);
+
+ spin_unlock_irqrestore(&touch->lock, flags);
+}
+
+static irqreturn_t cyapa_i2c_irq(int irq, void *dev_id)
+{
+ struct cyapa_i2c *touch = dev_id;
+
+ DBGPRINTK(("%s: trackpad interrupt captured. \
+ report_rate=%d; read_pending=%d\n",
+ __func__, touch->platform_data->report_rate,
+ touch->read_pending));
+
+ if (touch->platform_data->report_rate == 0) {
+ /*
+ ** no limitatioin for data reporting.
+ ** the report rate depending on trackpad max report rate.
+ ** this is the default report mode.
+ */
+ cyapa_i2c_reschedule_work(touch, 0);
+ } else {
+ /*
+ ** when use limited report rate, some important data packages
+ ** may be lost. Such as a tap or double tap gesture may be lost.
+ ** So firmware need to keep this data until there data is read.
+ */
+ if (!touch->read_pending) {
+ touch->read_pending = 1;
+ cyapa_i2c_reschedule_work(touch, touch->scan_ms);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* Control the Device polling rate / Work Handler sleep time */
+static unsigned long cyapa_i2c_adjust_delay(struct cyapa_i2c *touch,
+ bool have_data)
+{
+ unsigned long delay, nodata_count_thres;
+
+ if (touch->platform_data->use_polling_mode) {
+ delay = touch->platform_data->polling_interval_time_active;
+ if (have_data) {
+ touch->no_data_count = 0;
+ } else {
+ nodata_count_thres
+ = CYAPA_NO_DATA_THRES / touch->scan_ms;
+ if (touch->no_data_count < nodata_count_thres)
+ touch->no_data_count++;
+ else
+ delay = CYAPA_NO_DATA_SLEEP_MSECS;
+ }
+ return msecs_to_jiffies(delay);
+ } else {
+ delay = msecs_to_jiffies(CYAPA_THREAD_IRQ_SLEEP_MSECS);
+ return round_jiffies_relative(delay);
+ }
+}
+
+/* Work Handler */
+static void cyapa_i2c_work_handler(struct work_struct *work)
+{
+ bool have_data;
+ struct cyapa_i2c *touch
+ = container_of(work, struct cyapa_i2c, dwork.work);
+ unsigned long delay;
+
+ DBGPRINTK(("%s: start ...\n", __func__));
+
+ have_data = cyapa_i2c_get_input(touch);
+
+ /*
+ * While interrupt driven, there is no real need to poll the device.
+ * But touchpads are very sensitive, so there could be errors
+ * related to physical environment and the attention line isn't
+ * neccesarily asserted. In such case we can lose the touchpad.
+ * We poll the device once in CYAPA_THREAD_IRQ_SLEEP_SECS and
+ * if error is detected, we try to reset and reconfigure the touchpad.
+ */
+ delay = cyapa_i2c_adjust_delay(touch, have_data);
+ /* if needs fixed interval time trackpad scan, open it.
+ cyapa_i2c_reschedule_work(touch, delay);
+ */
+
+ touch->read_pending = 0;
+
+ DBGPRINTK(("%s: done ...\n", __func__));
+}
+
+static int cyapa_i2c_open(struct input_dev *input)
+{
+ struct cyapa_i2c *touch = input_get_drvdata(input);
+ int retval;
+
+ if (0 == touch->open_count) {
+ /* Since input_dev mouse, wheel, and kbd will all use same open
+ ** and close routines. But indeed, reset config to trackpad
+ ** once is enought,So when trackpad is open for the first time,
+ ** reset it. for other time not do it.
+ */
+ retval = cyapa_i2c_reset_config(touch);
+ if (retval) {
+ DBGPRINTK(("%s: failed to reset i2c trackpad. \
+ error = %d\n", __func__, retval));
+ return retval;
+ }
+ }
+ touch->open_count++;
+
+ if (touch->platform_data->use_polling_mode) {
+ /*
+ ** for the firstly time, it is set to CYAPA_NO_DATA_SLEEP_MSECS,
+ ** when data is read from trackpad, the read speed will
+ ** be pull up.
+ */
+ cyapa_i2c_reschedule_work(touch,
+ msecs_to_jiffies(CYAPA_NO_DATA_SLEEP_MSECS));
+ }
+
+ DBGPRINTK(("%s: touch->open_count = %d ...\n",
+ __func__, touch->open_count));
+
+ return 0;
+}
+
+static void cyapa_i2c_close(struct input_dev *input)
+{
+ struct cyapa_i2c *touch = input_get_drvdata(input);
+
+ touch->open_count--;
+
+ if (0 == touch->open_count) {
+ /* Since input_dev mouse, wheel, and kbd will all use same open
+ ** and close routines.
+ ** so when all mouse, wheel and kbd input_dev is closed,
+ ** then cancel the delayed work routine.
+ */
+ cancel_delayed_work_sync(&touch->dwork);
+ }
+
+ DBGPRINTK(("%s: touch->open_count=%d\n", __func__, touch->open_count));
+}
+
+static struct cyapa_i2c *cyapa_i2c_touch_create(struct i2c_client *client)
+{
+ struct cyapa_i2c *touch;
+
+ touch = kzalloc(sizeof(struct cyapa_i2c), GFP_KERNEL);
+ if (!touch)
+ return NULL;
+
+ DBGPRINTK(("%s: client=0x%p, allocate memory for touch successfully.\n",
+ __func__, client));
+
+ touch->platform_data = &cyapa_i2c_platform_data;
+ if (client->dev.platform_data) {
+ DBGPRINTK(("%s: client->dev.platform_data is set, copy it.\n",
+ __func__));
+ *touch->platform_data
+ = *(struct cyapa_platform_data *)
+ client->dev.platform_data;
+ }
+
+ cyapa_print_paltform_data(__func__, touch->platform_data);
+
+ if (touch->platform_data->use_polling_mode &&
+ (touch->platform_data->report_rate == 0)) {
+ /* when user miss setting platform data,
+ ** ensure that system is robust.
+ ** no divid zero error. */
+ touch->platform_data->report_rate
+ = CYAPA_POLLING_REPORTRATE_DEFAULT;
+ }
+ touch->scan_ms = touch->platform_data->report_rate
+ ? (1000 / touch->platform_data->report_rate) : 0;
+ touch->open_count = 0;
+ touch->prev_abs_x = -1;
+ touch->prev_abs_y = -1;
+ touch->client = client;
+ touch->zoomin_delta = 0;
+ touch->zoomout_delta = 0;
+ touch->hscroll_left = 0;
+ touch->hscroll_right = 0;
+ touch->prev_touch_fingers = 0;
+
+ cyapa_set_preferences(touch);
+ cyapa_reset_cursor_filters_data(touch);
+
+ INIT_DELAYED_WORK(&touch->dwork, cyapa_i2c_work_handler);
+ spin_lock_init(&touch->lock);
+
+ return touch;
+}
+
+static int cyapa_create_input_dev_mouse(struct cyapa_i2c *touch)
+{
+ int retval = 0;
+ struct input_dev *input = NULL;
+
+ input = touch->input = input_allocate_device();
+ if (!touch->input) {
+ dev_err(&touch->client->dev,
+ "%s: Allocate memory for Input device failed: %d\n",
+ __func__, retval);
+ return -ENOMEM;
+ }
+
+ input->name = "cyapa_i2c_trackpad";
+ input->phys = touch->client->adapter->name;
+ input->id.bustype = BUS_I2C;
+ input->id.version = 1;
+ input->dev.parent = &touch->client->dev;
+
+ input->open = cyapa_i2c_open;
+ input->close = cyapa_i2c_close;
+ input_set_drvdata(input, touch);
+
+ if (touch->platform_data->use_absolute_mode) {
+ /* absolution data report mode. */
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(EV_KEY, input->evbit);
+
+ input_set_abs_params(input, ABS_X, 0,
+ touch->max_absolution_x, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0,
+ touch->max_absolution_y, 0, 0);
+ input_set_abs_params(input, ABS_PRESSURE, 0, 255, 0, 0);
+ input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0);
+
+ __set_bit(BTN_TOUCH, input->keybit);
+ __set_bit(BTN_TOOL_FINGER, input->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
+ __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
+ __set_bit(BTN_TOOL_QUADTAP, input->keybit);
+
+ __set_bit(BTN_LEFT, input->keybit);
+ __set_bit(BTN_RIGHT, input->keybit);
+ __set_bit(BTN_MIDDLE, input->keybit);
+
+ __clear_bit(EV_REL, input->evbit);
+ __clear_bit(REL_X, input->relbit);
+ __clear_bit(REL_Y, input->relbit);
+ __clear_bit(BTN_TRIGGER, input->keybit);
+
+ input_abs_set_res(input,
+ ABS_X,
+ touch->max_absolution_x/touch->physical_size_x);
+ input_abs_set_res(input,
+ ABS_Y,
+ touch->max_absolution_y/touch->physical_size_y);
+
+ DBGPRINTK(("%s: Use absolute data reporting mode.\n",
+ __func__));
+ } else {
+ /* relative data reporting mode. */
+ __set_bit(EV_REL, input->evbit);
+ __set_bit(REL_X, input->relbit);
+ __set_bit(REL_Y, input->relbit);
+
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(BTN_LEFT, input->keybit);
+ __set_bit(BTN_RIGHT, input->keybit);
+ __set_bit(BTN_MIDDLE, input->keybit);
+
+ __clear_bit(EV_ABS, input->evbit);
+
+ DBGPRINTK(("%s: Use relative data reporting mode.\n",
+ __func__));
+ }
+
+ /* Register the device in input subsystem */
+ retval = input_register_device(touch->input);
+ if (retval) {
+ dev_err(&touch->client->dev,
+ "%s: Input device register failed: %d\n",
+ __func__, retval);
+
+ input_free_device(input);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int cyapa_create_input_dev_wheel(struct cyapa_i2c *touch)
+{
+ int retval = 0;
+ struct input_dev *input_wheel = NULL;
+
+ input_wheel = touch->input_wheel = input_allocate_device();
+ if (!touch->input_wheel) {
+ dev_err(&touch->client->dev,
+ "%s: Allocate memory for Input device failed: %d\n",
+ __func__, retval);
+ return -ENOMEM;
+ }
+
+ input_wheel->name = "cyapa_i2c_wheel";
+ input_wheel->phys = touch->client->adapter->name;
+ input_wheel->id.bustype = BUS_I2C;
+ input_wheel->id.version = 1;
+ input_wheel->dev.parent = &touch->client->dev;
+ input_wheel->open = cyapa_i2c_open;
+ input_wheel->close = cyapa_i2c_close;
+ input_set_drvdata(input_wheel, touch);
+
+ __set_bit(EV_KEY, input_wheel->evbit);
+ __set_bit(EV_REL, input_wheel->evbit);
+ __set_bit(REL_WHEEL, input_wheel->relbit);
+
+ retval = input_register_device(touch->input_wheel);
+ if (retval) {
+ dev_err(&touch->client->dev,
+ "%s: Input device register failed: %d\n",
+ __func__, retval);
+
+ input_free_device(input_wheel);
+ return retval;
+ }
+
+ return 0;
+}
+
+#define MAX_NR_SCANCODES 128
+
+static unsigned char cyapa_virtual_keycode[MAX_NR_SCANCODES] = {
+/* Bellow keys are supported.
+KEY_ENTER 28
+KEY_LEFTCTRL 29
+KEY_LEFTSHIFT 42
+KEY_RIGHTSHIFT 54
+KEY_LEFTALT 56
+KEY_KPMINUS 74
+KEY_KPPLUS 78
+KEY_RIGHTCTRL 97
+KEY_RIGHTALT 100
+KEY_HOME 102
+KEY_UP 103
+KEY_PAGEUP 104
+KEY_LEFT 105
+KEY_RIGHT 106
+KEY_END 107
+KEY_DOWN 108
+KEY_PAGEDOWN 109
+*/
+ 28, 29, 42, 54, 56, 74, 78, 97, 100,
+ 102, 103, 104, 105, 106, 107, 108, 109
+};
+
+static int cyapa_create_input_dev_kbd(struct cyapa_i2c *touch)
+{
+ int retval = 0;
+ int i;
+ struct input_dev *input_kbd = NULL;
+
+ input_kbd = touch->input_kbd = input_allocate_device();
+ if (!touch->input_kbd) {
+ dev_err(&touch->client->dev,
+ "%s: Allocate memory for Input device failed: %d\n",
+ __func__, retval);
+ return -ENOMEM;
+ }
+
+ input_kbd->name = "cyapa_i2c_virtual_kbd";
+ input_kbd->phys = touch->client->adapter->name;
+ input_kbd->id.bustype = BUS_I2C;
+ input_kbd->id.version = 1;
+ input_kbd->dev.parent = &touch->client->dev;
+ input_kbd->open = cyapa_i2c_open;
+ input_kbd->close = cyapa_i2c_close;
+ input_set_drvdata(input_kbd, touch);
+
+ input_kbd->keycode = &cyapa_virtual_keycode;
+ input_kbd->keycodesize = sizeof(unsigned char);
+ input_kbd->keycodemax = ARRAY_SIZE(cyapa_virtual_keycode);
+
+ __set_bit(EV_KEY, input_kbd->evbit);
+ __set_bit(EV_REP, input_kbd->evbit);
+
+ for (i = 0; i < ARRAY_SIZE(cyapa_virtual_keycode); i++)
+ __set_bit(cyapa_virtual_keycode[i], input_kbd->keybit);
+ __clear_bit(KEY_RESERVED, input_kbd->keybit);
+
+ retval = input_register_device(touch->input_kbd);
+ if (retval) {
+ dev_err(&touch->client->dev,
+ "%s: Input device register failed: %d\n",
+ __func__, retval);
+
+ input_free_device(input_kbd);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int __devinit cyapa_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ int retval = 0;
+ struct cyapa_i2c *touch;
+
+ DBGPRINTK(("%s: start ...\n", __func__));
+ touch = cyapa_i2c_touch_create(client);
+ if (!touch)
+ return -ENOMEM;
+
+ /* do platfrom initialize firstly. */
+ if (touch->platform_data->init)
+ retval = touch->platform_data->init();
+ if (retval)
+ goto err_mem_free;
+
+ /* set irq number if not using polling mode. */
+ if (touch->platform_data->use_polling_mode == true) {
+ touch->irq = -1;
+ } else {
+ if (touch->platform_data->irq_gpio == -1) {
+ if (client->irq) {
+ touch->irq = client->irq;
+ } else {
+ /* irq mode is not supported by system. */
+ touch->platform_data->use_polling_mode = true;
+ touch->irq = -1;
+ }
+ } else {
+ touch->irq
+ = gpio_to_irq(touch->platform_data->irq_gpio);
+ }
+ }
+ DBGPRINTK(("%s: irq=%d, client->irq=%d\n",
+ __func__, touch->irq, client->irq));
+
+ if (touch->platform_data->use_polling_mode == false) {
+ DBGPRINTK(("%s: request interrupt riq.\n", __func__));
+
+ set_irq_type(touch->irq, IRQF_TRIGGER_FALLING);
+ retval = request_irq(touch->irq,
+ cyapa_i2c_irq,
+ 0,
+ CYAPA_I2C_NAME,
+ touch);
+ if (retval) {
+ dev_warn(&touch->client->dev,
+ "%s: IRQ request failed: \
+ %d, falling back to polling mode.\n",
+ __func__, retval);
+
+ touch->platform_data->use_polling_mode = true;
+ }
+ }
+
+ /* reconfig trackpad depending on platfrom setting. */
+ /* Should disable interrupt to protect this polling read operation.
+ ** Ohterwise, this I2C read will be interrupt by other reading,
+ ** and failed. */
+ disable_irq(touch->irq);
+ cyapa_i2c_reconfig(touch);
+ enable_irq(touch->irq);
+
+ /* create an input_dev instance for virtual mouse trackpad. */
+ retval = cyapa_create_input_dev_mouse(touch);
+ if (retval) {
+ DBGPRINTK(("%s: create mouse input_dev instance filed.\n",
+ __func__));
+ goto err_mem_free;
+ }
+
+ /* create an input_dev instances for virtual wheel device
+ ** and virtual keyboard device. */
+ retval = cyapa_create_input_dev_wheel(touch);
+ if (retval) {
+ DBGPRINTK(("%s: create input_dev instance for wheel filed.\n",
+ __func__));
+ goto err_mem_free;
+ }
+
+ retval = cyapa_create_input_dev_kbd(touch);
+ if (retval) {
+ DBGPRINTK(("%s: create keyboad input_dev instance filed.\n",
+ __func__));
+ goto err_mem_free;
+ }
+
+ i2c_set_clientdata(client, touch);
+
+ DBGPRINTK(("%s: Done successfully.\n", __func__));
+
+ return 0;
+
+err_mem_free:
+ /* release previous allocated input_dev instances. */
+ if (touch->input) {
+ input_free_device(touch->input);
+ touch->input = NULL;
+ }
+
+ if (touch->input_wheel) {
+ input_free_device(touch->input_wheel);
+ touch->input_wheel = NULL;
+ }
+
+ if (touch->input_kbd) {
+ input_free_device(touch->input_kbd);
+ touch->input_kbd = NULL;
+ }
+
+ kfree(touch);
+
+ DBGPRINTK(("%s: exist with error %d.\n", __func__, retval));
+ return retval;
+}
+
+static int __devexit cyapa_i2c_remove(struct i2c_client *client)
+{
+ struct cyapa_i2c *touch = i2c_get_clientdata(client);
+
+ if (!touch->platform_data->use_polling_mode)
+ free_irq(client->irq, touch);
+
+ if (touch->input)
+ input_unregister_device(touch->input);
+ if (touch->input_wheel)
+ input_unregister_device(touch->input);
+ if (touch->input_kbd)
+ input_unregister_device(touch->input);
+ kfree(touch);
+
+ DBGPRINTK(("%s: ...\n", __func__));
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cyapa_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct cyapa_i2c *touch = i2c_get_clientdata(client);
+
+ DBGPRINTK(("%s: ...\n", __func__));
+ cancel_delayed_work_sync(&touch->dwork);
+
+ return 0;
+}
+
+static int cyapa_i2c_resume(struct i2c_client *client)
+{
+ int ret;
+ struct cyapa_i2c *touch = i2c_get_clientdata(client);
+
+ ret = cyapa_i2c_reset_config(touch);
+ DBGPRINTK(("%s: ...\n", __func__));
+ if (ret)
+ return ret;
+
+ cyapa_i2c_reschedule_work(touch,
+ msecs_to_jiffies(CYAPA_NO_DATA_SLEEP_MSECS));
+
+ return 0;
+}
+#else
+#define cyapa_i2c_suspend NULL
+#define cyapa_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id cypress_i2c_id_table[] = {
+ { CYAPA_I2C_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, cypress_i2c_id_table);
+
+static struct i2c_driver cypress_i2c_driver = {
+ .driver = {
+ .name = CYAPA_I2C_NAME,
+ .owner = THIS_MODULE,
+ },
+
+ .probe = cyapa_i2c_probe,
+ .remove = __devexit_p(cyapa_i2c_remove),
+
+ .suspend = cyapa_i2c_suspend,
+ .resume = cyapa_i2c_resume,
+ .id_table = cypress_i2c_id_table,
+};
+
+static int __init cyapa_i2c_init(void)
+{
+ DBGPRINTK(("%s: start ...\n", __func__));
+ return i2c_add_driver(&cypress_i2c_driver);
+}
+
+static void __exit cyapa_i2c_exit(void)
+{
+ DBGPRINTK(("%s: exit ...\n", __func__));
+ i2c_del_driver(&cypress_i2c_driver);
+}
+
+module_init(cyapa_i2c_init);
+module_exit(cyapa_i2c_exit);
+
+MODULE_DESCRIPTION("Cypress I2C Trackpad Driver");
+MODULE_AUTHOR("Dudley Du <dudl@xxxxxxxxxxx>");
+MODULE_LICENSE("GPL");
+
diff --git a/include/linux/cyapa.h b/include/linux/cyapa.h
new file mode 100644
index 0000000..888fab0
--- /dev/null
+++ b/include/linux/cyapa.h
@@ -0,0 +1,60 @@
+#ifndef CYAPA_H
+#define CYAPA_H
+
+
+#define CYAPA_I2C_NAME "cypress_i2c_apa"
+
+/* Active power state scanning/processing refresh interval time. unit: ms. */
+#define CYAPA_ACTIVE_POLLING_INTVAL_TIME 0x00
+/* Low power state scanning/processing refresh interval time. unit: ms. */
+#define CYAPA_LOWPOWER_POLLING_INTVAL_TIME 0x10
+/* Touch timeout for active power state. unit: ms. */
+#define CYAPA_ACTIVE_TOUCH_TIMEOUT 0xFF
+
+/* Max report rate limited for Cypress Trackpad. */
+#define CYAPA_NO_LIMITED_REPORT_RATE 0
+#define CYAPA_REPORT_RATE (CYAPA_NO_LIMITED_REPORT_RATE)
+#define CYAPA_POLLING_REPORTRATE_DEFAULT 125
+
+
+/* APA trackpad firmware generation */
+enum cyapa_gen {
+ CYAPA_GEN1 = 0x01,
+ CYAPA_GEN2 = 0x02,
+};
+
+/*
+** APA trackpad power states.
+** Used in register 0x00, bit3-2, PowerMode field.
+*/
+enum cyapa_powerstate {
+ CYAPA_PWR_ACTIVE = 0x01,
+ CYAPA_PWR_LIGHT_SLEEP = 0x02,
+ CYAPA_PWR_MEDIUM_SLEEP = 0x03,
+ CYAPA_PWR_DEEP_SLEEP = 0x04,
+};
+
+struct cyapa_platform_data {
+ u32 flag; /* reserved for future use. */
+ enum cyapa_gen gen; /* trackpad firmware generation. */
+ enum cyapa_powerstate power_state;
+
+ /* use absolute data report or relative data report. */
+ unsigned use_absolute_mode:1;
+ /* use polling mode or interrupt mode. */
+ unsigned use_polling_mode:1;
+ /* active mode, polling refresh interval; ms */
+ u8 polling_interval_time_active;
+ /* low power mode, polling refresh interval; ms */
+ u8 polling_interval_time_lowpower;
+ u8 active_touch_timeout; /* active touch timeout; ms */
+ char *name; /* device name of Cypress I2C trackpad. */
+ /* the gpio id used for interrupt to notify host data is ready. */
+ s16 irq_gpio;
+ u32 report_rate; /* max limitation of data report rate. */
+
+ int (*wakeup)(void);
+ int (*init)(void);
+};
+
+#endif /* #ifndef CYAPA_H */
--
1.7.0.4

Best Wishes,
Dudley Du
86-21-61648950


Attachment: 0001-Add-new-i2c-based-input-mouse-driver-for-Cypress-i2c.patch
Description: 0001-Add-new-i2c-based-input-mouse-driver-for-Cypress-i2c.patch