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. > + > +/* 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. > +/* 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. > + > + 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? > + schedule_work(&touch->dwork.work); Do not poke inside delayed work structure, just do schedule_delayed_work(&touch->dwork, 0); to schedule it immediately. > + > + 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? > +} > + > +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? > + > + 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. -- 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