| 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");
|
| +
|
|
|