Re: [PATCH v2] input: add synaptics_i2c driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux