Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(973)

Unified Diff: drivers/input/mouse/cypress_i2c.c

Issue 6626009: Add new i2c-based input mouse driver into input subsystem for Cypress trackpad devices. (Closed) Base URL: http://git.chromium.org/git/kernel-next.git@chromeos-2.6.37
Patch Set: Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « drivers/input/mouse/Makefile ('k') | include/linux/cyapa.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: drivers/input/mouse/cypress_i2c.c
diff --git a/drivers/input/mouse/cypress_i2c.c b/drivers/input/mouse/cypress_i2c.c
new file mode 100644
index 0000000000000000000000000000000000000000..e79469177ad8460891610288612a46cfd55e3efe
--- /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@cypress.com>
+ *
+ * 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@cypress.com>");
+MODULE_LICENSE("GPL");
+
« no previous file with comments | « drivers/input/mouse/Makefile ('k') | include/linux/cyapa.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698