Hi Dmitry, Any updates on this? I'd be really happy if it can go to 2.6.31... Mike Rapoport wrote: > Hi Dmitry, > > Dmitry Torokhov wrote: >> Hi Mike, >> >> On Wednesday 20 May 2009 05:36:17 Mike Rapoport wrote: >>> This patch adds support for Synaptics i2c touchpad controller found on >>> eXeda machine. >>> >>> Signed-off-by: Igor Grinberg <grinberg@xxxxxxxxxxxxxx> >>> Signed-off-by: Mike Rapoport <mike@xxxxxxxxxxxxxx> >>> CC: Jean Delvare <khali@xxxxxxxxxxxx> >>> >>> --- >>> v2 changelog: >>> - fix bugs and issues pointed out by Jean and Dmitry >>> - use workqueue instead of kthread >>> - reorganize methods order to something more logical >>> >>> --- >>> drivers/input/mouse/Kconfig | 18 + >>> drivers/input/mouse/Makefile | 1 + >>> drivers/input/mouse/synaptics_i2c.c | 700 >>> +++++++++++++++++++++++++++++++++++ 3 files changed, 719 insertions(+), 0 >>> deletions(-) >>> create mode 100644 drivers/input/mouse/synaptics_i2c.c >>> >>> diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig >>> index c66cc3d..8a2c5b1 100644 >>> --- a/drivers/input/mouse/Kconfig >>> +++ b/drivers/input/mouse/Kconfig >>> @@ -303,4 +303,22 @@ config MOUSE_MAPLE >>> To compile this driver as a module choose M here: the module will be >>> called maplemouse. >>> >>> +config MOUSE_SYNAPTICS_I2C >>> + tristate "Synaptics I2C Touchpad support" >>> + depends on I2C >>> + help >>> + This driver supports Synaptics I2C touchpad controller on eXeda >>> + mobile device. >>> + The device will not work the synaptics X11 driver because >>> + (i) it reports only relative coordinates and has no capabilities >>> + to report absolute coordinates >>> + (ii) the eXeda device itself uses Xfbdev as X Server and it does >>> + not allow using xf86-input-* drivers. >>> + >>> + Say y here if you have eXeda device and want to use a Synaptics >>> + I2C Touchpad. >>> + >>> + To compile this driver as a module, choose M here: the >>> + module will be called synaptics_i2c. >>> + >>> endif >>> diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile >>> index 4721894..010f265 100644 >>> --- a/drivers/input/mouse/Makefile >>> +++ b/drivers/input/mouse/Makefile >>> @@ -18,6 +18,7 @@ obj-$(CONFIG_MOUSE_PS2) += psmouse.o >>> obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o >>> obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o >>> obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o >>> +obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o >>> obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o >>> >>> psmouse-objs := psmouse-base.o synaptics.o >>> diff --git a/drivers/input/mouse/synaptics_i2c.c >>> b/drivers/input/mouse/synaptics_i2c.c new file mode 100644 >>> index 0000000..82d90f3 >>> --- /dev/null >>> +++ b/drivers/input/mouse/synaptics_i2c.c >>> @@ -0,0 +1,700 @@ >>> +/* >>> + * Synaptics touchpad with I2C interface >>> + * >>> + * Copyright (C) 2009 Compulab, Ltd. >>> + * Mike Rapoport <mike@xxxxxxxxxxxxxx> >>> + * Igor Grinberg <grinberg@xxxxxxxxxxxxxx> >>> + * >>> + * 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> >>> + >>> +#define DRIVER_NAME "synaptics_i2c" >>> +/* maximum product id is 15 characters */ >>> +#define PRODUCT_ID_LENGTH 15 >>> +#define REGISTER_LENGTH 8 >>> + >>> +/* >>> + * after soft reset, we should wait for 1 ms >>> + * before the device becomes operational >>> + */ >>> +#define SOFT_RESET_DELAY_MS 3 >>> +/* and after hard reset, we should wait for max 500ms */ >>> +#define HARD_RESET_DELAY_MS 500 >>> + >>> +/* Registers by SMBus address */ >>> +#define PAGE_SEL_REG 0xff >>> +#define DEVICE_STATUS_REG 0x09 >>> + >>> +/* Registers by RMI address */ >>> +#define DEV_CONTROL_REG 0x0000 >>> +#define INTERRUPT_EN_REG 0x0001 >>> +#define ERR_STAT_REG 0x0002 >>> +#define INT_REQ_STAT_REG 0x0003 >>> +#define DEV_COMMAND_REG 0x0004 >>> + >>> +#define RMI_PROT_VER_REG 0x0200 >>> +#define MANUFACT_ID_REG 0x0201 >>> +#define PHYS_INT_VER_REG 0x0202 >>> +#define PROD_PROPERTY_REG 0x0203 >>> +#define INFO_QUERY_REG0 0x0204 >>> +#define INFO_QUERY_REG1 (INFO_QUERY_REG0 + 1) >>> +#define INFO_QUERY_REG2 (INFO_QUERY_REG0 + 2) >>> +#define INFO_QUERY_REG3 (INFO_QUERY_REG0 + 3) >>> + >>> +#define PRODUCT_ID_REG0 0x0210 >>> +#define PRODUCT_ID_REG1 (PRODUCT_ID_REG0 + 1) >>> +#define PRODUCT_ID_REG2 (PRODUCT_ID_REG0 + 2) >>> +#define PRODUCT_ID_REG3 (PRODUCT_ID_REG0 + 3) >>> +#define PRODUCT_ID_REG4 (PRODUCT_ID_REG0 + 4) >>> +#define PRODUCT_ID_REG5 (PRODUCT_ID_REG0 + 5) >>> +#define PRODUCT_ID_REG6 (PRODUCT_ID_REG0 + 6) >>> +#define PRODUCT_ID_REG7 (PRODUCT_ID_REG0 + 7) >>> +#define PRODUCT_ID_REG8 (PRODUCT_ID_REG0 + 8) >>> +#define PRODUCT_ID_REG9 (PRODUCT_ID_REG0 + 9) >>> +#define PRODUCT_ID_REG10 (PRODUCT_ID_REG0 + 10) >>> +#define PRODUCT_ID_REG11 (PRODUCT_ID_REG0 + 11) >>> +#define PRODUCT_ID_REG12 (PRODUCT_ID_REG0 + 12) >>> +#define PRODUCT_ID_REG13 (PRODUCT_ID_REG0 + 13) >>> +#define PRODUCT_ID_REG14 (PRODUCT_ID_REG0 + 14) >>> +#define PRODUCT_ID_REG15 (PRODUCT_ID_REG0 + 15) >>> + >>> +#define DATA_REG0 0x0400 >>> +#define ABS_PRESSURE_REG 0x0401 >>> +#define ABS_MSB_X_REG 0x0402 >>> +#define ABS_LSB_X_REG (ABS_MSB_X_REG + 1) >>> +#define ABS_MSB_Y_REG 0x0404 >>> +#define ABS_LSB_Y_REG (ABS_MSB_Y_REG + 1) >>> +#define REL_X_REG 0x0406 >>> +#define REL_Y_REG 0x0407 >>> + >>> +#define DEV_QUERY_REG0 0x1000 >>> +#define DEV_QUERY_REG1 (DEV_QUERY_REG0 + 1) >>> +#define DEV_QUERY_REG2 (DEV_QUERY_REG0 + 2) >>> +#define DEV_QUERY_REG3 (DEV_QUERY_REG0 + 3) >>> +#define DEV_QUERY_REG4 (DEV_QUERY_REG0 + 4) >>> +#define DEV_QUERY_REG5 (DEV_QUERY_REG0 + 5) >>> +#define DEV_QUERY_REG6 (DEV_QUERY_REG0 + 6) >>> +#define DEV_QUERY_REG7 (DEV_QUERY_REG0 + 7) >>> +#define DEV_QUERY_REG8 (DEV_QUERY_REG0 + 8) >>> + >>> +#define GENERAL_2D_CONTROL_REG 0x1041 >>> +#define SENSOR_SENSITIVITY_REG 0x1044 >>> +#define SENS_MAX_POS_MSB_REG 0x1046 >>> +#define SENS_MAX_POS_LSB_REG (SENS_MAX_POS_UPPER_REG + 1) >>> + >>> +/* Register bits */ >>> +/* Device Control Register Bits */ >>> +#define REPORT_RATE_1ST_BIT 6 >>> + >>> +/* Interrupt Enable Register Bits (INTERRUPT_EN_REG) */ >>> +#define F10_ABS_INT_ENA 0 >>> +#define F10_REL_INT_ENA 1 >>> +#define F20_INT_ENA 2 >>> + >>> +/* Interrupt Request Register Bits (INT_REQ_STAT_REG | DEVICE_STATUS_REG) >>> */ +#define F10_ABS_INT_REQ 0 >>> +#define F10_REL_INT_REQ 1 >>> +#define F20_INT_REQ 2 >>> +/* Device Status Register Bits (DEVICE_STATUS_REG) */ >>> +#define STAT_CONFIGURED 6 >>> +#define STAT_ERROR 7 >>> + >>> +/* Device Command Register Bits (DEV_COMMAND_REG) */ >>> +#define RESET_COMMAND 0x01 >>> +#define REZERO_COMMAND 0x02 >>> + >>> +/* Data Register 0 Bits (DATA_REG0) */ >>> +#define GESTURE 3 >>> + >>> +/* Device Query Registers Bits */ >>> +/* DEV_QUERY_REG3 */ >>> +#define HAS_PALM_DETECT 1 >>> +#define HAS_MULTI_FING 2 >>> +#define HAS_SCROLLER 4 >>> +#define HAS_2D_SCROLL 5 >>> + >>> +/* General 2D Control Register Bits (GENERAL_2D_CONTROL_REG) */ >>> +#define NO_DECELERATION 1 >>> +#define REDUCE_REPORTING 3 >>> +#define NO_FILTER 5 >>> + >>> +#define GET_BIT(bit, val) (((val) >> (bit)) & 0x01) >> We have test_bit() if you don't want to open-code this. > > Fixed > >>> + >>> +/* Function Masks */ >>> +/* Device Control Register Masks (DEV_CONTROL_REG) */ >>> +#define REPORT_RATE_MSK 0xc0 >>> +#define SLEEP_MODE_MSK 0x07 >>> + >>> +/* Device Sleep Modes */ >>> +#define FULL_AWAKE 0x0 >>> +#define NORMAL_OP 0x1 >>> +#define LOW_PWR_OP 0x2 >>> +#define VERY_LOW_PWR_OP 0x3 >>> +#define SENS_SLEEP 0x4 >>> +#define SLEEP_MOD 0x5 >>> +#define DEEP_SLEEP 0x6 >>> +#define HIBERNATE 0x7 >>> + >>> +/* Interrupt Register Mask */ >>> +/* (INT_REQ_STAT_REG | DEVICE_STATUS_REG | INTERRUPT_EN_REG) */ >>> +#define INT_ENA_REQ_MSK 0x07 >>> +#define INT_ENA_ABS_MSK 0x01 >>> +#define INT_ENA_REL_MSK 0x02 >>> +#define INT_ENA_F20_MSK 0x04 >>> + >>> +/* Device Status Register Masks (DEVICE_STATUS_REG) */ >>> +#define CONFIGURED_MSK 0x40 >>> +#define ERROR_MSK 0x80 >>> + >>> +/* Data Register 0 Masks */ >>> +#define FINGER_WIDTH_MSK 0xf0 >>> +#define GESTURE_MSK 0x08 >>> +#define SENSOR_STATUS_MSK 0x07 >>> + >>> +/* >>> + * MSB Position Register Masks >>> + * ABS_MSB_X_REG | ABS_MSB_Y_REG | SENS_MAX_POS_MSB_REG | >>> + * DEV_QUERY_REG3 | DEV_QUERY_REG5 >>> + */ >>> +#define MSB_POSITION_MSK 0x1f >>> + >>> +/* Device Query Registers Masks */ >>> + >>> +/* DEV_QUERY_REG2 */ >>> +#define NUM_EXTRA_POS_MSK 0x07 >>> + >>> +/* When in IRQ mode read the device every THREAD_IRQ_SLEEP_SECS */ >>> +#define THREAD_IRQ_SLEEP_SECS 2 >>> +#define THREAD_IRQ_SLEEP_MSECS (THREAD_IRQ_SLEEP_SECS * MSEC_PER_SEC) >>> + >>> +/* >>> + * When in Polling mode and no data received for NO_DATA_THRES msecs >>> + * reduce the polling rate to NO_DATA_SLEEP_MSECS >>> + */ >>> +#define NO_DATA_THRES (MSEC_PER_SEC) >>> +#define NO_DATA_SLEEP_MSECS (MSEC_PER_SEC / 4) >>> + >>> +/* >>> + * The Acceleration Factor: >>> + * from 0.5 (1) to 4 (8) in dozes of 0.5 >>> + * the transformation is done in order to get 0.5 resolution. >>> + * values above 8 will affect poor performance without proper smoothing. >>> + */ >>> +#define ACCEL_DIVIDER 10 >>> +#define ACCEL_DOZE 2 >>> +static int accel = 4; >>> +module_param(accel, int, 0644); >>> +MODULE_PARM_DESC(accel, "Set Acceleration parameter 1..8. Default = 4"); >>> + >>> +/* Control touchpad's No Deceleration option */ >>> +static int no_decel = 1; >>> +module_param(no_decel, bool, 0644); >>> +MODULE_PARM_DESC(no_decel, "No Deceleration. Default = 1 (on)"); >>> + >> The same objection as before - does not belong in the kernel. > > Acceleration handling is removed from the driver. > As for the 'no_decel' parameter, it does not define a policy in the driver but > rather the hardware operation mode, so we've keep it along with others. The > parameters are directly translated into the device control register bits. > >>> +/* Control touchpad's Reduced Reporting option */ >>> +static int reduce_report; >>> +module_param(reduce_report, bool, 0644); >>> +MODULE_PARM_DESC(reduce_report, "Reduced Reporting. Default = 0 (off)"); >>> + >>> +/* Control touchpad's No Filter option */ >>> +static int no_filter; >>> +module_param(no_filter, bool, 0644); >>> +MODULE_PARM_DESC(no_filter, "No Filter. Default = 0 (off)"); >>> + >>> +/* >>> + * touchpad Attention line is Active Low and Open Drain, >>> + * therefore should be connected to pulled up line >>> + * and the irq configuration should be set to Falling Edge Trigger >>> + */ >>> +/* Control IRQ / Polling option */ >>> +static int polling_req; >>> +module_param(polling_req, bool, 0444); >>> +MODULE_PARM_DESC(polling_req, "Request Polling. Default = 0 (use irq)"); >>> + >>> +/* Control Polling Rate */ >>> +static int scan_rate = 80; >>> +module_param(scan_rate, int, 0644); >>> +MODULE_PARM_DESC(scan_rate, "Polling rate in times/sec. Default = 80"); >>> + >>> +/* The main device structure */ >>> +struct synaptics_i2c { >>> + struct i2c_client *client; >>> + struct input_dev *input; >>> + struct mutex mutex; >>> + struct delayed_work dwork; >>> + int no_data_count; >>> + int no_decel_param; >>> + int reduce_report_param; >>> + int no_filter_param; >>> + int scan_rate_param; >>> + int accel_param; >>> + int accel; /* Transformed acceleration */ >>> + int scan_ms; >>> +}; >>> + >>> +static inline void set_acceleration(struct synaptics_i2c *touch, int >>> accel) +{ >>> + int acc = (accel < 1) ? 1 : ((accel > 8) ? 8 : accel); >>> + touch->accel = (acc * ACCEL_DIVIDER) / ACCEL_DOZE; >>> + touch->accel_param = accel; >>> +} >>> + >>> +static inline void set_scan_rate(struct synaptics_i2c *touch, int >>> scan_rate) +{ >>> + touch->scan_ms = MSEC_PER_SEC / scan_rate; >>> + touch->scan_rate_param = scan_rate; >>> +} >>> + >>> +static s32 synaptics_i2c_reg_get(struct i2c_client *client, u16 reg) >>> +{ >>> + struct synaptics_i2c *touch = i2c_get_clientdata(client); >>> + int ret; >>> + >>> + mutex_lock(&touch->mutex); >> Why do you need to take this mutex? I don't see you doing simultaneous >> access to the device, but if you do then I'd think you need to protect >> larger blocks, like the read-modify-write chunks, not individual read/ >> write operations. > > The driver indeed has no concurrent accesses to the device, so we've removed the > locks and added appropriate comment. > >>> + >>> + ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); >>> + if (!ret) >>> + ret = i2c_smbus_read_byte_data(client, reg & 0xff); >>> + >>> + mutex_unlock(&touch->mutex); >>> + >>> + return ret; >>> +} >>> + >>> +static s32 synaptics_i2c_reg_set(struct i2c_client *client, u16 reg, u8 >>> val) +{ >>> + struct synaptics_i2c *touch = i2c_get_clientdata(client); >>> + int ret; >>> + >>> + mutex_lock(&touch->mutex); >>> + >>> + ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); >>> + if (!ret) >>> + ret = i2c_smbus_write_byte_data(client, reg & 0xff, val); >>> + >>> + mutex_unlock(&touch->mutex); >>> + >>> + return ret; >>> +} >>> + >>> +static s32 synaptics_i2c_word_get(struct i2c_client *client, u16 reg) >>> +{ >>> + struct synaptics_i2c *touch = i2c_get_clientdata(client); >>> + int ret; >>> + >>> + mutex_lock(&touch->mutex); >>> + >>> + ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); >>> + if (!ret) >>> + ret = i2c_smbus_read_word_data(client, reg & 0xff); >>> + >>> + mutex_unlock(&touch->mutex); >>> + >>> + return ret; >>> +} >>> + >>> +static int synaptics_i2c_config(struct i2c_client *client) >>> +{ >>> + int ret, control; >>> + u8 int_en; >>> + >>> + /* set Report Rate to Device Highest (>=80) and Sleep to normal */ >>> + ret = synaptics_i2c_reg_set(client, DEV_CONTROL_REG, 0xc1); >>> + if (ret) >>> + return ret; >>> + >>> + /* set Interrupt Disable to Func20 / Enable to Func10) */ >>> + int_en = (polling_req) ? 0 : INT_ENA_ABS_MSK | INT_ENA_REL_MSK; >>> + ret = synaptics_i2c_reg_set(client, INTERRUPT_EN_REG, int_en); >>> + if (ret) >>> + return ret; >>> + >>> + control = synaptics_i2c_reg_get(client, GENERAL_2D_CONTROL_REG); >>> + /* No Deceleration */ >>> + control |= (no_decel) ? 1 << NO_DECELERATION : 0; >>> + /* Reduced Reporting */ >>> + control |= (reduce_report) ? 1 << REDUCE_REPORTING : 0; >>> + /* No Filter */ >>> + control |= (no_filter) ? 1 << NO_FILTER : 0; >>> + ret = synaptics_i2c_reg_set(client, GENERAL_2D_CONTROL_REG, control); >>> + if (ret) >>> + return ret; >>> + >>> + return 0; >>> +} >>> + >>> +static int synaptics_i2c_reset_config(struct i2c_client *client) >>> +{ >>> + int ret; >>> + /* Reset the Touchpad */ >>> + ret = synaptics_i2c_reg_set(client, DEV_COMMAND_REG, RESET_COMMAND); >>> + if (ret) { >>> + dev_err(&client->dev, "Unable to reset device\n"); >>> + } else { >>> + msleep(SOFT_RESET_DELAY_MS); >>> + ret = synaptics_i2c_config(client); >>> + if (ret) >>> + dev_err(&client->dev, "Unable to config device\n"); >>> + } >>> + >>> + return ret; >>> +} >>> + >>> +static int synaptics_i2c_check_error(struct i2c_client *client) >>> +{ >>> + int status, ret = 0; >>> + struct synaptics_i2c *touch = i2c_get_clientdata(client); >>> + >>> + mutex_lock(&touch->mutex); >>> + >>> + status = i2c_smbus_read_byte_data(client, DEVICE_STATUS_REG) & >>> + (CONFIGURED_MSK | ERROR_MSK); >>> + >>> + mutex_unlock(&touch->mutex); >>> + >>> + if (status != CONFIGURED_MSK) >>> + ret = synaptics_i2c_reset_config(client); >>> + >>> + return ret; >>> +} >>> + >>> +static int synaptics_i2c_get_input(struct synaptics_i2c *touch) >>> +{ >>> + struct input_dev *input = touch->input; >>> + int xy_delta, gesture; >>> + s8 x_delta, y_delta; >>> + >>> + /* Deal with spontanious resets and errors */ >>> + if (synaptics_i2c_check_error(touch->client)) >>> + return 0; >>> + >>> + /* Get Gesture Bit */ >>> + gesture = GET_BIT(GESTURE, >>> + synaptics_i2c_reg_get(touch->client, DATA_REG0)); >>> + >>> + /* >>> + * Get Relative axes. we have to get them in one shot, >>> + * so we get 2 bytes starting from REL_X_REG. >>> + */ >>> + xy_delta = synaptics_i2c_word_get(touch->client, REL_X_REG) & 0xffff; >>> + >>> + /* Separate X from Y */ >>> + x_delta = xy_delta & 0xff; >>> + y_delta = (xy_delta >> REGISTER_LENGTH) & 0xff; >>> + >>> + /* Report the button event */ >>> + input_report_key(input, BTN_LEFT, gesture); >>> + >>> + /* Report the deltas multiplied by acceleration factor */ >>> + input_report_rel(input, REL_X, >>> + (touch->accel * (s16) x_delta) / ACCEL_DIVIDER); >>> + input_report_rel(input, REL_Y, >>> + -((touch->accel * (s16) y_delta) / ACCEL_DIVIDER)); >>> + input_sync(input); >>> + >>> + return (xy_delta || gesture) ? 1 : 0; >>> +} >>> + >>> +static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id) >>> +{ >>> + struct synaptics_i2c *touch = dev_id; >>> + >>> + cancel_delayed_work(&touch->dwork); >> Why do we need to cancel? > > An explanation is below. > >>> + schedule_work(&touch->dwork.work); >> Do not poke inside delayed work structure, just do >> >> schedule_delayed_work(&touch->dwork, 0); >> >> to schedule it immediately. > > Fixed. > >>> + >>> + return IRQ_HANDLED; >>> +} >>> + >>> +static void synaptics_i2c_check_params(struct synaptics_i2c *touch) >>> +{ >>> + int reset = 0; >>> + if (accel != touch->accel_param) >>> + set_acceleration(touch, accel); >>> + >>> + if (scan_rate != touch->scan_rate_param) >>> + set_scan_rate(touch, scan_rate); >>> + >>> + if (no_decel != touch->no_decel_param) { >>> + touch->no_decel_param = no_decel; >>> + reset = 1; >>> + } >>> + if (no_filter != touch->no_filter_param) { >>> + touch->no_filter_param = no_filter; >>> + reset = 1; >>> + } >>> + if (reduce_report != touch->reduce_report_param) { >>> + touch->reduce_report_param = reduce_report; >>> + reset = 1; >>> + } >>> + if (reset) >>> + synaptics_i2c_reset_config(touch->client); >>> +} >>> + >>> +/* Control the Device polling rate / Work Handler sleep time */ >>> +unsigned long synaptics_i2c_fix_delay(struct synaptics_i2c *touch, int >>> data) +{ >>> + int delay, nodata_count_thres; >>> + >>> + if (polling_req) { >>> + delay = touch->scan_ms; >>> + if (data) { >>> + touch->no_data_count = 0; >>> + } else { >>> + nodata_count_thres = NO_DATA_THRES / touch->scan_ms; >>> + if (touch->no_data_count < nodata_count_thres) >>> + touch->no_data_count++; >>> + else >>> + delay = NO_DATA_SLEEP_MSECS; >>> + } >>> + } else >>> + delay = THREAD_IRQ_SLEEP_MSECS; >>> + >>> + return msecs_to_jiffies(delay); >>> +} >>> + >>> +/* Work Handler */ >>> +static void synaptics_i2c_work_handler(struct work_struct *work) >>> +{ >>> + int data = 1; >>> + struct synaptics_i2c *touch = >>> + container_of(work, struct synaptics_i2c, dwork.work); >>> + unsigned long delay; >>> + >>> + synaptics_i2c_check_params(touch); >>> + >>> + do { >>> + data = synaptics_i2c_get_input(touch); >>> + delay = synaptics_i2c_fix_delay(touch, data); >>> + } while (data); >>> + >>> + schedule_delayed_work(&touch->dwork, delay); >> Do we need to re-schedule the work even if we are in interrupt-driven >> mode? > > We've added the following comment explaining why we re-schedule the work even if > we are in interrupt-driven mode: > /* > 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 THREAD_IRQ_SLEEP_SECS and > if error is detected, we try to reset and reconfigure the touchpad. > */ > >>> +} >>> + >>> +static int synaptics_i2c_open(struct input_dev *input) >>> +{ >>> + int ret = 0; >>> + struct synaptics_i2c *touch = input_get_drvdata(input); >>> + >>> + ret = synaptics_i2c_reset_config(touch->client); >>> + if (ret) >>> + return ret; >>> + >>> + if (polling_req) >>> + schedule_delayed_work(&touch->dwork, >>> + msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); >>> + else >>> + enable_irq(touch->client->irq); >>> + >>> + return ret; >>> +} >>> + >>> +static void synaptics_i2c_close(struct input_dev *input) >>> +{ >>> + struct synaptics_i2c *touch = input_get_drvdata(input); >>> + >>> + if (!polling_req) >>> + disable_irq(touch->client->irq); >> Just disabling IRQ is pretty heavy-handed. Can't it ever be shared with >> another device? > > Fixed. > >>> + >>> + cancel_delayed_work_sync(&touch->dwork); >>> + >>> + /* Save some power */ >>> + synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP); >>> +} >>> + >>> +static void synaptics_i2c_set_input_params(struct synaptics_i2c *touch) >>> +{ >>> + struct input_dev *input = touch->input; >>> + >>> + input->name = touch->client->name; >>> + input->phys = touch->client->adapter->name; >>> + input->id.bustype = BUS_I2C; >>> + input->id.version = synaptics_i2c_word_get(touch->client, >>> + INFO_QUERY_REG0); >>> + input->dev.parent = &touch->client->dev; >>> + input->open = synaptics_i2c_open; >>> + input->close = synaptics_i2c_close; >>> + input_set_drvdata(input, touch); >>> + >>> + /* Register the device as mouse */ >>> + set_bit(EV_REL, input->evbit); >>> + set_bit(REL_X, input->relbit); >>> + set_bit(REL_Y, input->relbit); >>> + >>> + /* Register device's buttons and keys */ >>> + set_bit(EV_KEY, input->evbit); >>> + set_bit(BTN_LEFT, input->keybit); >>> +} >>> + >>> +struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client >>> *client) +{ >>> + struct synaptics_i2c *touch; >>> + >>> + touch = kzalloc(sizeof(struct synaptics_i2c), GFP_KERNEL); >>> + if (!touch) >>> + return NULL; >>> + >>> + touch->client = client; >>> + touch->no_decel_param = no_decel; >>> + touch->scan_rate_param = scan_rate; >>> + set_scan_rate(touch, scan_rate); >>> + touch->accel_param = accel; >>> + set_acceleration(touch, accel); >>> + INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler); >>> + mutex_init(&touch->mutex); >>> + >>> + return touch; >>> +} >>> + >>> +static int __devinit synaptics_i2c_probe(struct i2c_client *client, >>> + const struct i2c_device_id *dev_id) >>> +{ >>> + int ret; >>> + struct synaptics_i2c *touch; >>> + >>> + touch = synaptics_i2c_touch_create(client); >>> + if (!touch) >>> + return -ENOMEM; >>> + >>> + i2c_set_clientdata(client, touch); >>> + >>> + ret = synaptics_i2c_reset_config(client); >>> + if (ret) >>> + goto err_mem_free; >>> + >>> + if (client->irq < 1) >>> + polling_req = 1; >>> + >>> + touch->input = input_allocate_device(); >>> + if (!touch->input) { >>> + ret = -ENOMEM; >>> + goto err_mem_free; >>> + } >>> + >>> + synaptics_i2c_set_input_params(touch); >>> + >>> + if (!polling_req) { >>> + dev_dbg(&touch->client->dev, >>> + "Requesting IRQ: %d\n", touch->client->irq); >>> + >>> + ret = request_irq(touch->client->irq, synaptics_i2c_irq, >>> + IRQF_DISABLED|IRQ_TYPE_EDGE_FALLING, >>> + DRIVER_NAME, touch); >>> + if (ret) { >>> + dev_warn(&touch->client->dev, >>> + "IRQ request failed: %d, " >>> + "falling back to polling\n", ret); >>> + polling_req = 1; >>> + } else >>> + disable_irq(touch->client->irq); >>> + } >>> + >>> + if (polling_req) >>> + dev_dbg(&touch->client->dev, >>> + "Using polling at rate: %d times/sec\n", scan_rate); >>> + >>> + /* Register the device in input subsystem */ >>> + ret = input_register_device(touch->input); >>> + if (ret) { >>> + dev_err(&client->dev, >>> + "Input device register failed: %d\n", ret); >>> + goto err_input_free; >>> + } >>> + return 0; >>> + >>> +err_input_free: >>> + input_free_device(touch->input); >>> +err_mem_free: >>> + i2c_set_clientdata(client, NULL); >>> + kfree(touch); >>> + >>> + return ret; >>> +} >>> + >>> +static int __devexit synaptics_i2c_remove(struct i2c_client *client) >>> +{ >>> + struct synaptics_i2c *touch = i2c_get_clientdata(client); >>> + >>> + if (!polling_req) >>> + free_irq(touch->client->irq, touch); >>> + >>> + input_unregister_device(touch->input); >>> + i2c_set_clientdata(client, NULL); >>> + kfree(touch); >>> + >>> + return 0; >>> +} >>> + >>> +static int synaptics_i2c_suspend(struct i2c_client *client, pm_message_t >>> mesg) +{ >>> + struct synaptics_i2c *touch = i2c_get_clientdata(client); >>> + >>> + cancel_delayed_work_sync(&touch->dwork); >>> + >>> + /* Save some power */ >>> + synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP); >>> + >>> + return 0; >>> +} >>> + >>> +static int synaptics_i2c_resume(struct i2c_client *client) >>> +{ >>> + int ret; >>> + struct synaptics_i2c *touch = i2c_get_clientdata(client); >>> + >>> + ret = synaptics_i2c_reset_config(client); >>> + if (ret) >>> + return ret; >>> + >>> + schedule_delayed_work(&touch->dwork, >>> + msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); >>> + >>> + return 0; >>> +} >>> + >>> +static const struct i2c_device_id synaptics_i2c_id_table[] = { >>> + { "synaptics_i2c", 0 }, >>> + { }, >>> +}; >>> +MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table); >>> + >>> +static struct i2c_driver synaptics_i2c_driver = { >>> + .driver = { >>> + .name = DRIVER_NAME, >>> + .owner = THIS_MODULE, >>> + }, >>> + >>> + .probe = synaptics_i2c_probe, >>> + .remove = __devexit_p(synaptics_i2c_remove), >>> + >>> + .suspend = synaptics_i2c_suspend, >>> + .resume = synaptics_i2c_resume, >>> + .id_table = synaptics_i2c_id_table, >>> +}; >>> + >>> +static int __init synaptics_i2c_init(void) >>> +{ >>> + return i2c_add_driver(&synaptics_i2c_driver); >>> +} >>> + >>> +static void __exit synaptics_i2c_exit(void) >>> +{ >>> + i2c_del_driver(&synaptics_i2c_driver); >>> +} >>> + >>> +module_init(synaptics_i2c_init); >>> +module_exit(synaptics_i2c_exit); >>> + >>> +MODULE_DESCRIPTION("Synaptics I2C touchpad driver"); >>> +MODULE_AUTHOR("Mike Rapoport, Igor Grinberg, Compulab"); >>> +MODULE_LICENSE("GPL"); >>> + >> Thanks. >> > > This driver supports Synaptics I2C touchpad controller on eXeda > mobile device. > > Signed-off-by: Igor Grinberg <grinberg@xxxxxxxxxxxxxx> > Signed-off-by: Mike Rapoport <mike@xxxxxxxxxxxxxx> > --- > drivers/input/mouse/Kconfig | 18 + > drivers/input/mouse/Makefile | 1 + > drivers/input/mouse/synaptics_i2c.c | 664 +++++++++++++++++++++++++++++++++++ > 3 files changed, 683 insertions(+), 0 deletions(-) > create mode 100644 drivers/input/mouse/synaptics_i2c.c > > diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig > index c66cc3d..8a2c5b1 100644 > --- a/drivers/input/mouse/Kconfig > +++ b/drivers/input/mouse/Kconfig > @@ -303,4 +303,22 @@ config MOUSE_MAPLE > To compile this driver as a module choose M here: the module will be > called maplemouse. > > +config MOUSE_SYNAPTICS_I2C > + tristate "Synaptics I2C Touchpad support" > + depends on I2C > + help > + This driver supports Synaptics I2C touchpad controller on eXeda > + mobile device. > + The device will not work the synaptics X11 driver because > + (i) it reports only relative coordinates and has no capabilities > + to report absolute coordinates > + (ii) the eXeda device itself uses Xfbdev as X Server and it does > + not allow using xf86-input-* drivers. > + > + Say y here if you have eXeda device and want to use a Synaptics > + I2C Touchpad. > + > + To compile this driver as a module, choose M here: the > + module will be called synaptics_i2c. > + > endif > diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile > index 4721894..010f265 100644 > --- a/drivers/input/mouse/Makefile > +++ b/drivers/input/mouse/Makefile > @@ -18,6 +18,7 @@ obj-$(CONFIG_MOUSE_PS2) += psmouse.o > obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o > obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o > obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o > +obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o > obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o > > psmouse-objs := psmouse-base.o synaptics.o > diff --git a/drivers/input/mouse/synaptics_i2c.c > b/drivers/input/mouse/synaptics_i2c.c > new file mode 100644 > index 0000000..32a5d87 > --- /dev/null > +++ b/drivers/input/mouse/synaptics_i2c.c > @@ -0,0 +1,664 @@ > +/* > + * Synaptics touchpad with I2C interface > + * > + * Copyright (C) 2009 Compulab, Ltd. > + * Mike Rapoport <mike@xxxxxxxxxxxxxx> > + * Igor Grinberg <grinberg@xxxxxxxxxxxxxx> > + * > + * 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> > + > +#define DRIVER_NAME "synaptics_i2c" > +/* maximum product id is 15 characters */ > +#define PRODUCT_ID_LENGTH 15 > +#define REGISTER_LENGTH 8 > + > +/* > + * after soft reset, we should wait for 1 ms > + * before the device becomes operational > + */ > +#define SOFT_RESET_DELAY_MS 3 > +/* and after hard reset, we should wait for max 500ms */ > +#define HARD_RESET_DELAY_MS 500 > + > +/* Registers by SMBus address */ > +#define PAGE_SEL_REG 0xff > +#define DEVICE_STATUS_REG 0x09 > + > +/* Registers by RMI address */ > +#define DEV_CONTROL_REG 0x0000 > +#define INTERRUPT_EN_REG 0x0001 > +#define ERR_STAT_REG 0x0002 > +#define INT_REQ_STAT_REG 0x0003 > +#define DEV_COMMAND_REG 0x0004 > + > +#define RMI_PROT_VER_REG 0x0200 > +#define MANUFACT_ID_REG 0x0201 > +#define PHYS_INT_VER_REG 0x0202 > +#define PROD_PROPERTY_REG 0x0203 > +#define INFO_QUERY_REG0 0x0204 > +#define INFO_QUERY_REG1 (INFO_QUERY_REG0 + 1) > +#define INFO_QUERY_REG2 (INFO_QUERY_REG0 + 2) > +#define INFO_QUERY_REG3 (INFO_QUERY_REG0 + 3) > + > +#define PRODUCT_ID_REG0 0x0210 > +#define PRODUCT_ID_REG1 (PRODUCT_ID_REG0 + 1) > +#define PRODUCT_ID_REG2 (PRODUCT_ID_REG0 + 2) > +#define PRODUCT_ID_REG3 (PRODUCT_ID_REG0 + 3) > +#define PRODUCT_ID_REG4 (PRODUCT_ID_REG0 + 4) > +#define PRODUCT_ID_REG5 (PRODUCT_ID_REG0 + 5) > +#define PRODUCT_ID_REG6 (PRODUCT_ID_REG0 + 6) > +#define PRODUCT_ID_REG7 (PRODUCT_ID_REG0 + 7) > +#define PRODUCT_ID_REG8 (PRODUCT_ID_REG0 + 8) > +#define PRODUCT_ID_REG9 (PRODUCT_ID_REG0 + 9) > +#define PRODUCT_ID_REG10 (PRODUCT_ID_REG0 + 10) > +#define PRODUCT_ID_REG11 (PRODUCT_ID_REG0 + 11) > +#define PRODUCT_ID_REG12 (PRODUCT_ID_REG0 + 12) > +#define PRODUCT_ID_REG13 (PRODUCT_ID_REG0 + 13) > +#define PRODUCT_ID_REG14 (PRODUCT_ID_REG0 + 14) > +#define PRODUCT_ID_REG15 (PRODUCT_ID_REG0 + 15) > + > +#define DATA_REG0 0x0400 > +#define ABS_PRESSURE_REG 0x0401 > +#define ABS_MSB_X_REG 0x0402 > +#define ABS_LSB_X_REG (ABS_MSB_X_REG + 1) > +#define ABS_MSB_Y_REG 0x0404 > +#define ABS_LSB_Y_REG (ABS_MSB_Y_REG + 1) > +#define REL_X_REG 0x0406 > +#define REL_Y_REG 0x0407 > + > +#define DEV_QUERY_REG0 0x1000 > +#define DEV_QUERY_REG1 (DEV_QUERY_REG0 + 1) > +#define DEV_QUERY_REG2 (DEV_QUERY_REG0 + 2) > +#define DEV_QUERY_REG3 (DEV_QUERY_REG0 + 3) > +#define DEV_QUERY_REG4 (DEV_QUERY_REG0 + 4) > +#define DEV_QUERY_REG5 (DEV_QUERY_REG0 + 5) > +#define DEV_QUERY_REG6 (DEV_QUERY_REG0 + 6) > +#define DEV_QUERY_REG7 (DEV_QUERY_REG0 + 7) > +#define DEV_QUERY_REG8 (DEV_QUERY_REG0 + 8) > + > +#define GENERAL_2D_CONTROL_REG 0x1041 > +#define SENSOR_SENSITIVITY_REG 0x1044 > +#define SENS_MAX_POS_MSB_REG 0x1046 > +#define SENS_MAX_POS_LSB_REG (SENS_MAX_POS_UPPER_REG + 1) > + > +/* Register bits */ > +/* Device Control Register Bits */ > +#define REPORT_RATE_1ST_BIT 6 > + > +/* Interrupt Enable Register Bits (INTERRUPT_EN_REG) */ > +#define F10_ABS_INT_ENA 0 > +#define F10_REL_INT_ENA 1 > +#define F20_INT_ENA 2 > + > +/* Interrupt Request Register Bits (INT_REQ_STAT_REG | DEVICE_STATUS_REG) */ > +#define F10_ABS_INT_REQ 0 > +#define F10_REL_INT_REQ 1 > +#define F20_INT_REQ 2 > +/* Device Status Register Bits (DEVICE_STATUS_REG) */ > +#define STAT_CONFIGURED 6 > +#define STAT_ERROR 7 > + > +/* Device Command Register Bits (DEV_COMMAND_REG) */ > +#define RESET_COMMAND 0x01 > +#define REZERO_COMMAND 0x02 > + > +/* Data Register 0 Bits (DATA_REG0) */ > +#define GESTURE 3 > + > +/* Device Query Registers Bits */ > +/* DEV_QUERY_REG3 */ > +#define HAS_PALM_DETECT 1 > +#define HAS_MULTI_FING 2 > +#define HAS_SCROLLER 4 > +#define HAS_2D_SCROLL 5 > + > +/* General 2D Control Register Bits (GENERAL_2D_CONTROL_REG) */ > +#define NO_DECELERATION 1 > +#define REDUCE_REPORTING 3 > +#define NO_FILTER 5 > + > +/* Function Masks */ > +/* Device Control Register Masks (DEV_CONTROL_REG) */ > +#define REPORT_RATE_MSK 0xc0 > +#define SLEEP_MODE_MSK 0x07 > + > +/* Device Sleep Modes */ > +#define FULL_AWAKE 0x0 > +#define NORMAL_OP 0x1 > +#define LOW_PWR_OP 0x2 > +#define VERY_LOW_PWR_OP 0x3 > +#define SENS_SLEEP 0x4 > +#define SLEEP_MOD 0x5 > +#define DEEP_SLEEP 0x6 > +#define HIBERNATE 0x7 > + > +/* Interrupt Register Mask */ > +/* (INT_REQ_STAT_REG | DEVICE_STATUS_REG | INTERRUPT_EN_REG) */ > +#define INT_ENA_REQ_MSK 0x07 > +#define INT_ENA_ABS_MSK 0x01 > +#define INT_ENA_REL_MSK 0x02 > +#define INT_ENA_F20_MSK 0x04 > + > +/* Device Status Register Masks (DEVICE_STATUS_REG) */ > +#define CONFIGURED_MSK 0x40 > +#define ERROR_MSK 0x80 > + > +/* Data Register 0 Masks */ > +#define FINGER_WIDTH_MSK 0xf0 > +#define GESTURE_MSK 0x08 > +#define SENSOR_STATUS_MSK 0x07 > + > +/* > + * MSB Position Register Masks > + * ABS_MSB_X_REG | ABS_MSB_Y_REG | SENS_MAX_POS_MSB_REG | > + * DEV_QUERY_REG3 | DEV_QUERY_REG5 > + */ > +#define MSB_POSITION_MSK 0x1f > + > +/* Device Query Registers Masks */ > + > +/* DEV_QUERY_REG2 */ > +#define NUM_EXTRA_POS_MSK 0x07 > + > +/* When in IRQ mode read the device every THREAD_IRQ_SLEEP_SECS */ > +#define THREAD_IRQ_SLEEP_SECS 2 > +#define THREAD_IRQ_SLEEP_MSECS (THREAD_IRQ_SLEEP_SECS * MSEC_PER_SEC) > + > +/* > + * When in Polling mode and no data received for NO_DATA_THRES msecs > + * reduce the polling rate to NO_DATA_SLEEP_MSECS > + */ > +#define NO_DATA_THRES (MSEC_PER_SEC) > +#define NO_DATA_SLEEP_MSECS (MSEC_PER_SEC / 4) > + > +/* Control touchpad's No Deceleration option */ > +static int no_decel = 1; > +module_param(no_decel, bool, 0644); > +MODULE_PARM_DESC(no_decel, "No Deceleration. Default = 1 (on)"); > + > +/* Control touchpad's Reduced Reporting option */ > +static int reduce_report; > +module_param(reduce_report, bool, 0644); > +MODULE_PARM_DESC(reduce_report, "Reduced Reporting. Default = 0 (off)"); > + > +/* Control touchpad's No Filter option */ > +static int no_filter; > +module_param(no_filter, bool, 0644); > +MODULE_PARM_DESC(no_filter, "No Filter. Default = 0 (off)"); > + > +/* > + * touchpad Attention line is Active Low and Open Drain, > + * therefore should be connected to pulled up line > + * and the irq configuration should be set to Falling Edge Trigger > + */ > +/* Control IRQ / Polling option */ > +static int polling_req; > +module_param(polling_req, bool, 0444); > +MODULE_PARM_DESC(polling_req, "Request Polling. Default = 0 (use irq)"); > + > +/* Control Polling Rate */ > +static int scan_rate = 80; > +module_param(scan_rate, int, 0644); > +MODULE_PARM_DESC(scan_rate, "Polling rate in times/sec. Default = 80"); > + > +/* The main device structure */ > +struct synaptics_i2c { > + struct i2c_client *client; > + struct input_dev *input; > + struct delayed_work dwork; > + int no_data_count; > + int no_decel_param; > + int reduce_report_param; > + int no_filter_param; > + int scan_rate_param; > + int scan_ms; > +}; > + > +static inline void set_scan_rate(struct synaptics_i2c *touch, int scan_rate) > +{ > + touch->scan_ms = MSEC_PER_SEC / scan_rate; > + touch->scan_rate_param = scan_rate; > +} > + > +/* > + * 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 synaptics_i2c_reg_get(struct i2c_client *client, u16 reg) > +{ > + int ret; > + > + ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); > + if (ret) > + return ret; > + > + return i2c_smbus_read_byte_data(client, reg & 0xff); > +} > + > +static s32 synaptics_i2c_reg_set(struct i2c_client *client, u16 reg, u8 val) > +{ > + int ret; > + > + ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); > + if (ret) > + return ret; > + > + return i2c_smbus_write_byte_data(client, reg & 0xff, val); > +} > + > +static s32 synaptics_i2c_word_get(struct i2c_client *client, u16 reg) > +{ > + int ret; > + > + ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); > + if (ret) > + return ret; > + > + return i2c_smbus_read_word_data(client, reg & 0xff); > +} > + > +static int synaptics_i2c_config(struct i2c_client *client) > +{ > + int ret, control; > + u8 int_en; > + > + /* set Report Rate to Device Highest (>=80) and Sleep to normal */ > + ret = synaptics_i2c_reg_set(client, DEV_CONTROL_REG, 0xc1); > + if (ret) > + return ret; > + > + /* set Interrupt Disable to Func20 / Enable to Func10) */ > + int_en = (polling_req) ? 0 : INT_ENA_ABS_MSK | INT_ENA_REL_MSK; > + ret = synaptics_i2c_reg_set(client, INTERRUPT_EN_REG, int_en); > + if (ret) > + return ret; > + > + control = synaptics_i2c_reg_get(client, GENERAL_2D_CONTROL_REG); > + /* No Deceleration */ > + control |= (no_decel) ? 1 << NO_DECELERATION : 0; > + /* Reduced Reporting */ > + control |= (reduce_report) ? 1 << REDUCE_REPORTING : 0; > + /* No Filter */ > + control |= (no_filter) ? 1 << NO_FILTER : 0; > + ret = synaptics_i2c_reg_set(client, GENERAL_2D_CONTROL_REG, control); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static int synaptics_i2c_reset_config(struct i2c_client *client) > +{ > + int ret; > + /* Reset the Touchpad */ > + ret = synaptics_i2c_reg_set(client, DEV_COMMAND_REG, RESET_COMMAND); > + if (ret) { > + dev_err(&client->dev, "Unable to reset device\n"); > + } else { > + msleep(SOFT_RESET_DELAY_MS); > + ret = synaptics_i2c_config(client); > + if (ret) > + dev_err(&client->dev, "Unable to config device\n"); > + } > + > + return ret; > +} > + > +static int synaptics_i2c_check_error(struct i2c_client *client) > +{ > + int status, ret = 0; > + > + status = i2c_smbus_read_byte_data(client, DEVICE_STATUS_REG) & > + (CONFIGURED_MSK | ERROR_MSK); > + > + if (status != CONFIGURED_MSK) > + ret = synaptics_i2c_reset_config(client); > + > + return ret; > +} > + > +static int synaptics_i2c_get_input(struct synaptics_i2c *touch) > +{ > + struct input_dev *input = touch->input; > + int xy_delta, gesture; > + s8 x_delta, y_delta; > + > + /* Deal with spontanious resets and errors */ > + if (synaptics_i2c_check_error(touch->client)) > + return 0; > + > + /* Get Gesture Bit */ > + gesture = (synaptics_i2c_reg_get(touch->client, DATA_REG0) > + >> GESTURE) & 0x1; > + > + /* > + * Get Relative axes. we have to get them in one shot, > + * so we get 2 bytes starting from REL_X_REG. > + */ > + xy_delta = synaptics_i2c_word_get(touch->client, REL_X_REG) & 0xffff; > + > + /* Separate X from Y */ > + x_delta = xy_delta & 0xff; > + y_delta = (xy_delta >> REGISTER_LENGTH) & 0xff; > + > + /* Report the button event */ > + input_report_key(input, BTN_LEFT, gesture); > + > + /* Report the deltas */ > + input_report_rel(input, REL_X, x_delta); > + input_report_rel(input, REL_Y, -y_delta); > + input_sync(input); > + > + return (xy_delta || gesture) ? 1 : 0; > +} > + > +static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id) > +{ > + struct synaptics_i2c *touch = dev_id; > + > + /* > + * This cancels the work scheduled in work handler. > + * See explanation on this in work handler func. > + */ > + cancel_delayed_work(&touch->dwork); > + schedule_delayed_work(&touch->dwork, 0); > + > + return IRQ_HANDLED; > +} > + > +static void synaptics_i2c_check_params(struct synaptics_i2c *touch) > +{ > + int reset = 0; > + if (scan_rate != touch->scan_rate_param) > + set_scan_rate(touch, scan_rate); > + > + if (no_decel != touch->no_decel_param) { > + touch->no_decel_param = no_decel; > + reset = 1; > + } > + if (no_filter != touch->no_filter_param) { > + touch->no_filter_param = no_filter; > + reset = 1; > + } > + if (reduce_report != touch->reduce_report_param) { > + touch->reduce_report_param = reduce_report; > + reset = 1; > + } > + if (reset) > + synaptics_i2c_reset_config(touch->client); > +} > + > +/* Control the Device polling rate / Work Handler sleep time */ > +unsigned long synaptics_i2c_fix_delay(struct synaptics_i2c *touch, int data) > +{ > + int delay, nodata_count_thres; > + > + if (polling_req) { > + delay = touch->scan_ms; > + if (data) { > + touch->no_data_count = 0; > + } else { > + nodata_count_thres = NO_DATA_THRES / touch->scan_ms; > + if (touch->no_data_count < nodata_count_thres) > + touch->no_data_count++; > + else > + delay = NO_DATA_SLEEP_MSECS; > + } > + } else > + delay = THREAD_IRQ_SLEEP_MSECS; > + > + return msecs_to_jiffies(delay); > +} > + > +/* Work Handler */ > +static void synaptics_i2c_work_handler(struct work_struct *work) > +{ > + int data = 1; > + struct synaptics_i2c *touch = > + container_of(work, struct synaptics_i2c, dwork.work); > + unsigned long delay; > + > + synaptics_i2c_check_params(touch); > + > + do { > + data = synaptics_i2c_get_input(touch); > + delay = synaptics_i2c_fix_delay(touch, data); > + } while (data); > + > + /* > + * 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 THREAD_IRQ_SLEEP_SECS and > + * if error is detected, we try to reset and reconfigure the touchpad. > + */ > + schedule_delayed_work(&touch->dwork, delay); > +} > + > +static int synaptics_i2c_open(struct input_dev *input) > +{ > + int ret = 0; > + struct synaptics_i2c *touch = input_get_drvdata(input); > + > + ret = synaptics_i2c_reset_config(touch->client); > + if (ret) > + return ret; > + > + if (polling_req) > + schedule_delayed_work(&touch->dwork, > + msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); > + > + return ret; > +} > + > +static void synaptics_i2c_close(struct input_dev *input) > +{ > + struct synaptics_i2c *touch = input_get_drvdata(input); > + > + if (!polling_req) > + synaptics_i2c_reg_set(touch->client, INTERRUPT_EN_REG, 0); > + > + cancel_delayed_work_sync(&touch->dwork); > + > + /* Save some power */ > + synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP); > +} > + > +static void synaptics_i2c_set_input_params(struct synaptics_i2c *touch) > +{ > + struct input_dev *input = touch->input; > + > + input->name = touch->client->name; > + input->phys = touch->client->adapter->name; > + input->id.bustype = BUS_I2C; > + input->id.version = synaptics_i2c_word_get(touch->client, > + INFO_QUERY_REG0); > + input->dev.parent = &touch->client->dev; > + input->open = synaptics_i2c_open; > + input->close = synaptics_i2c_close; > + input_set_drvdata(input, touch); > + > + /* Register the device as mouse */ > + set_bit(EV_REL, input->evbit); > + set_bit(REL_X, input->relbit); > + set_bit(REL_Y, input->relbit); > + > + /* Register device's buttons and keys */ > + set_bit(EV_KEY, input->evbit); > + set_bit(BTN_LEFT, input->keybit); > +} > + > +struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client) > +{ > + struct synaptics_i2c *touch; > + > + touch = kzalloc(sizeof(struct synaptics_i2c), GFP_KERNEL); > + if (!touch) > + return NULL; > + > + touch->client = client; > + touch->no_decel_param = no_decel; > + touch->scan_rate_param = scan_rate; > + set_scan_rate(touch, scan_rate); > + INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler); > + > + return touch; > +} > + > +static int __devinit synaptics_i2c_probe(struct i2c_client *client, > + const struct i2c_device_id *dev_id) > +{ > + int ret; > + struct synaptics_i2c *touch; > + > + touch = synaptics_i2c_touch_create(client); > + if (!touch) > + return -ENOMEM; > + > + i2c_set_clientdata(client, touch); > + > + ret = synaptics_i2c_reset_config(client); > + if (ret) > + goto err_mem_free; > + > + if (client->irq < 1) > + polling_req = 1; > + > + touch->input = input_allocate_device(); > + if (!touch->input) { > + ret = -ENOMEM; > + goto err_mem_free; > + } > + > + synaptics_i2c_set_input_params(touch); > + > + if (!polling_req) { > + dev_dbg(&touch->client->dev, > + "Requesting IRQ: %d\n", touch->client->irq); > + > + ret = request_irq(touch->client->irq, synaptics_i2c_irq, > + IRQF_DISABLED|IRQ_TYPE_EDGE_FALLING, > + DRIVER_NAME, touch); > + if (ret) { > + dev_warn(&touch->client->dev, > + "IRQ request failed: %d, " > + "falling back to polling\n", ret); > + polling_req = 1; > + synaptics_i2c_reg_set(touch->client, > + INTERRUPT_EN_REG, 0); > + } > + } > + > + if (polling_req) > + dev_dbg(&touch->client->dev, > + "Using polling at rate: %d times/sec\n", scan_rate); > + > + /* Register the device in input subsystem */ > + ret = input_register_device(touch->input); > + if (ret) { > + dev_err(&client->dev, > + "Input device register failed: %d\n", ret); > + goto err_input_free; > + } > + return 0; > + > +err_input_free: > + input_free_device(touch->input); > +err_mem_free: > + i2c_set_clientdata(client, NULL); > + kfree(touch); > + > + return ret; > +} > + > +static int __devexit synaptics_i2c_remove(struct i2c_client *client) > +{ > + struct synaptics_i2c *touch = i2c_get_clientdata(client); > + > + if (!polling_req) > + free_irq(touch->client->irq, touch); > + > + input_unregister_device(touch->input); > + i2c_set_clientdata(client, NULL); > + kfree(touch); > + > + return 0; > +} > + > +static int synaptics_i2c_suspend(struct i2c_client *client, pm_message_t mesg) > +{ > + struct synaptics_i2c *touch = i2c_get_clientdata(client); > + > + cancel_delayed_work_sync(&touch->dwork); > + > + /* Save some power */ > + synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP); > + > + return 0; > +} > + > +static int synaptics_i2c_resume(struct i2c_client *client) > +{ > + int ret; > + struct synaptics_i2c *touch = i2c_get_clientdata(client); > + > + ret = synaptics_i2c_reset_config(client); > + if (ret) > + return ret; > + > + schedule_delayed_work(&touch->dwork, > + msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); > + > + return 0; > +} > + > +static const struct i2c_device_id synaptics_i2c_id_table[] = { > + { "synaptics_i2c", 0 }, > + { }, > +}; > +MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table); > + > +static struct i2c_driver synaptics_i2c_driver = { > + .driver = { > + .name = DRIVER_NAME, > + .owner = THIS_MODULE, > + }, > + > + .probe = synaptics_i2c_probe, > + .remove = __devexit_p(synaptics_i2c_remove), > + > + .suspend = synaptics_i2c_suspend, > + .resume = synaptics_i2c_resume, > + .id_table = synaptics_i2c_id_table, > +}; > + > +static int __init synaptics_i2c_init(void) > +{ > + return i2c_add_driver(&synaptics_i2c_driver); > +} > + > +static void __exit synaptics_i2c_exit(void) > +{ > + i2c_del_driver(&synaptics_i2c_driver); > +} > + > +module_init(synaptics_i2c_init); > +module_exit(synaptics_i2c_exit); > + > +MODULE_DESCRIPTION("Synaptics I2C touchpad driver"); > +MODULE_AUTHOR("Mike Rapoport, Igor Grinberg, Compulab"); > +MODULE_LICENSE("GPL"); > + -- Sincerely yours, Mike. -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html