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 = ®_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 = ®_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(®_data, 0, sizeof(union cyapa_reg_data)); |
+ |
+ /* read register data from trackpad. */ |
+ gen1_data = ®_data.gen1_data; |
+ gen2_data = ®_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 *)®_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, ®_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"); |
+ |