Hi, On Wed, May 13, 2009 at 05:30:03PM +0300, Mike Rapoport wrote: > From: Igor Grinberg <grinberg@xxxxxxxxxxxxxx> > > This patch adds support for Synaptics i2c touchpad controller found on > eXeda machine. > I am CCing Jean Delvare for review of I2C bits. > Signed-off-by: Igor Grinberg <grinberg@xxxxxxxxxxxxxx> > Signed-off-by: Mike Rapoport <mike@xxxxxxxxxxxxxx> > --- > drivers/input/mouse/Kconfig | 9 + > drivers/input/mouse/Makefile | 1 + > drivers/input/mouse/synaptics_i2c.c | 775 +++++++++++++++++++++++++++++++++++ > 3 files changed, 785 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..89192f5 100644 > --- a/drivers/input/mouse/Kconfig > +++ b/drivers/input/mouse/Kconfig > @@ -303,4 +303,13 @@ 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 > + Say y here if you want to use a Synaptics I2C Touchpad. Say Y here ... Also please add long long explanantion that the driver will _not_ work with Synaptics X driver. There is no chance to enable absolute mode, is there? > + > + 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..de2c363 > --- /dev/null > +++ b/drivers/input/mouse/synaptics_i2c.c > @@ -0,0 +1,775 @@ > +/* > + * 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/ctype.h> > +#include <linux/uaccess.h> > +#include <linux/i2c.h> > +#include <linux/irq.h> > +#include <linux/interrupt.h> > +#include <linux/input.h> > +#include <linux/delay.h> > +#include <linux/kthread.h> > +#include <linux/completion.h> > +#include <linux/freezer.h> > + > +#define DRIVER_NAME "synaptics_i2c" > +/* According to RMI manual, maximum product id is 15 characters */ > +#define PRODUCT_ID_LENGTH 15 > +#define REGISTER_LENGTH 8 > + > +/* According to RMI manual, 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 > + > +/* Define Registers by SMBus address */ > +#define PAGE_SEL_REG 0xff > +#define DEVICE_STATUS_REG 0x09 > + > +/* Define 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) > + > +/* Define 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) > + > +/* Define Function Masks */ > +/* Device Control Register Masks (DEV_CONTROL_REG) */ > +#define REPORT_RATE_MSK 0xc0 > +#define SLEEP_MODE_MSK 0x07 > + > +/* 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)"); > + Accelkeration and deceleration handling should be done in userspace. X already does this, does it not? > +/* 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)"); > + > +/* According to Synaptics manual the 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"); > + > +/* Print Device Info */ > +static int query; > +module_param(query, bool, 0444); > +MODULE_PARM_DESC(query, "Print Device Query. Default = 0"); I don't think its an inetersting attribute, just set mode to 0. > + > +/* The main device structure */ > +struct synaptics_i2c { > + struct i2c_client *client; > + struct input_dev *input; > + struct task_struct *thread; > + struct completion touch_completion; > + struct completion thread_completion; > + int thread_stop; > + int suspend_link; > + int users_count; > + 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) > +{ > + 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 s32 synaptics_i2c_block_read(struct i2c_client *client, > + u16 reg, u8 length, u8 *values) > +{ > + int ret; > + > + ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); > + if (ret) > + return ret; > + > + return i2c_smbus_read_i2c_block_data(client, reg & 0xff, > + length, values); > +} > + > +static int synaptics_i2c_query(struct i2c_client *client) > +{ > + u8 data[7]; > + char id[PRODUCT_ID_LENGTH + 1]; > + int ret, retry_count, control, status, sens; > + > + /* DEV_QUERY_REG0 is the Function Query area for 2D devices. */ > + /* We're only interested in entries DEV_QUERY_REG2 */ > + /* and following registers right now. */ > + for (retry_count = 0; retry_count < 3; retry_count++) { > + ret = synaptics_i2c_block_read(client, DEV_QUERY_REG2, > + sizeof(data), data); > + if (ret != sizeof(data)) > + continue; > + > + dev_info(&client->dev, "Number of extra positions: %d\n", > + data[0] & NUM_EXTRA_POS_MSK); > + dev_info(&client->dev, "Has 2D Scroll: %d\n", > + GET_BIT(HAS_2D_SCROLL, data[1])); > + dev_info(&client->dev, "Has Scroller: %d\n", > + GET_BIT(HAS_SCROLLER, data[1])); > + dev_info(&client->dev, "Has Multifing: %d\n", > + GET_BIT(HAS_MULTI_FING, data[1])); > + dev_info(&client->dev, "Has Palm Det: %d\n", > + GET_BIT(HAS_PALM_DETECT, data[1])); > + dev_info(&client->dev, "Sensor Max X: %d\n", > + ((data[2] & MSB_POSITION_MSK) << REGISTER_LENGTH) > + | data[3]); > + dev_info(&client->dev, "Sensor Max Y: %d\n", > + ((data[4] & MSB_POSITION_MSK) << REGISTER_LENGTH) > + | data[5]); > + dev_info(&client->dev, "Sensor Resolution: %d\n", data[6]); > + break; > + } > + if (retry_count >= 5) > + dev_warn(&client->dev, > + "Query command failed: block read failed\n"); > + > + control = synaptics_i2c_reg_get(client, DEV_CONTROL_REG); > + dev_info(&client->dev, "Default Report Rate: 0x%x\n", > + (control & REPORT_RATE_MSK) >> REPORT_RATE_1ST_BIT); > + dev_info(&client->dev, "Sleep Mode: 0x%x\n", > + control & SLEEP_MODE_MSK); > + > + /* The Touchpad's manual doesn't document the RMI address of the */ > + /* Device Status Register and niether does RMI manual, */ > + /* so we use the SMBus address */ > + status = i2c_smbus_read_byte_data(client, DEVICE_STATUS_REG); > + dev_info(&client->dev, "Device Status Register: 0x%x\n", status); > + > + sens = synaptics_i2c_reg_get(client, SENSOR_SENSITIVITY_REG); > + dev_info(&client->dev, "Sensor Sensitivity: 0x%x\n", sens); > + > + for (retry_count = 0; retry_count < 3; retry_count++) { > + ret = synaptics_i2c_block_read(client, PRODUCT_ID_REG0, > + sizeof(id), id); > + if (ret != sizeof(id)) > + continue; > + > + dev_info(&client->dev, "Product ID: %s\n", id); > + break; > + } > + > + return 0; > +} > + > +static int synaptics_i2c_config(struct i2c_client *client) > +{ > + int ret, control; > + u8 int_en; > + > + /* set Report Rate to 40 and Sleep to normal */ > + ret = synaptics_i2c_reg_set(client, DEV_CONTROL_REG, 0x41); > + 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 { > + mdelay(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 = GET_BIT(GESTURE, > + synaptics_i2c_reg_get(touch->client, DATA_REG0)); > + > + /* Get Relative axes. According to RMI Manual, 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 */ > + if (gesture) > + input_report_key(input, BTN_LEFT, 1); > + else > + input_report_key(input, BTN_LEFT, 0); > + > + /* 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 *_touch) > +{ > + struct synaptics_i2c *touch = _touch; > + > + complete(&touch->touch_completion); > + > + 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 Thread 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); > +} > + > +/* The Thread */ > +static int synaptics_i2c_touchd(void *_touch) > +{ > + struct synaptics_i2c *touch = _touch; > + unsigned long sleep_delay; > + int data = 1; > + > + sleep_delay = synaptics_i2c_fix_delay(touch, data); > + > + daemonize("synaptics_i2cd"); > + while (!kthread_should_stop()) { > + if (touch->thread_stop) > + break; > + synaptics_i2c_check_params(touch); > + > + wait_for_completion_interruptible_timeout(&touch->touch_completion, sleep_delay); > + > + if (touch->thread_stop) > + break; > + > + try_to_freeze(); > + if (!touch->suspend_link) { > + do { > + data = synaptics_i2c_get_input(touch); > + sleep_delay = synaptics_i2c_fix_delay(touch, data); > + } while (data); > + } > + } > + complete_and_exit(&touch->thread_completion, 0); Why don't you just use workqueue (keventd)? > +} > + > +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; > + > + memset(touch, 0, sizeof(struct synaptics_i2c)); > + 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_completion(&touch->touch_completion); > + init_completion(&touch->thread_completion); > + > + return touch; > +} > + > +static int synaptics_i2c_suspend(struct i2c_client *client, pm_message_t mesg) > +{ > + struct synaptics_i2c *touch = i2c_get_clientdata(client); > + > + /* We don't want timeouts on i2c bus */ > + touch->suspend_link = 1; > + > + 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); > + touch->suspend_link = 0; > + > + return ret; > +} > + > +static int synaptics_i2c_open(struct input_dev *input) > +{ > + int ret = 0; > + struct synaptics_i2c *touch = input_get_drvdata(input); > + > + if (touch->users_count++) > + return 0; No need to count, input core will only call you when needed. > + > + ret = synaptics_i2c_reset_config(touch->client); > + if (ret) > + return ret; > + > + if (!polling_req) { > + 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\n" > + "Trying to use polling...\n", ret); > + polling_req = 1; > + } > + } > + touch->thread_stop = 0; > + touch->thread = kthread_run(&synaptics_i2c_touchd, > + touch, "synaptics_i2cd"); > + if (IS_ERR(touch->thread)) { > + ret = PTR_ERR(touch->thread); > + touch->thread = NULL; > + } > + /* FIXME: Should we do something with the device here? */ > + > + return ret; > +} > + > +static void synaptics_i2c_close(struct input_dev *input) > +{ > + struct synaptics_i2c *touch = input_get_drvdata(input); > + > + if (--touch->users_count) > + return; Same here. > + > + if (!touch->users_count) { > + touch->thread_stop = 1; > + complete(&touch->touch_completion); /* Awake the thread */ > + wait_for_completion(&touch->thread_completion); > + touch->thread = NULL; > + > + if (!polling_req) > + free_irq(touch->client->irq, touch); > + > + /* FIXME: Should we do something with the device here? */ > + } > +} > + > +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); > +} > + > +static const struct i2c_device_id synaptics_i2c_id_table[] = { > + { DRIVER_NAME, 0 }, > + { }, > +}; > +MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table); > + > +static int synaptics_i2c_probe(struct i2c_client *client, > + const struct i2c_device_id *dev_id) > +{ > + int ret; > + struct synaptics_i2c *touch = NULL; > + > + ret = synaptics_i2c_reset_config(client); > + if (ret) > + return ret; > + > + if (query) { > + ret = synaptics_i2c_query(client); > + if (ret) { > + dev_err(&client->dev, "Query failed: %d\n", ret); > + return ret; > + } > + } > + > + touch = synaptics_i2c_touch_create(client); > + if (!touch) > + goto err_mem; > + > + i2c_set_clientdata(client, touch); > + > + if (client->irq < 1) > + polling_req = 1; > + > + touch->input = input_allocate_device(); > + if (!touch->input) > + goto err_mem; > + > + synaptics_i2c_set_input_params(touch); > + > + if (!polling_req) > + dev_info(&touch->client->dev, > + "IRQ will be used: %d\n", touch->client->irq); > + else > + dev_info(&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_free; > + } > + return 0; > + > +err_mem: > + dev_err(&client->dev, "Insufficient memory\n"); > + ret = -ENOMEM; > + > +err_free: > + input_free_device(touch->input); > + 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); > + > + input_unregister_device(touch->input); > + i2c_set_clientdata(client, NULL); > + kfree(touch); > + > + return 0; > +} > + > +static struct i2c_driver synaptics_i2c_driver = { > + .driver = { > + .name = DRIVER_NAME, > + .owner = THIS_MODULE, > + }, > + > + .probe = synaptics_i2c_probe, > + .remove = synaptics_i2c_remove, __devexit_p() > + > + .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. -- Dmitry -- 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