Re: [PATCH v2] input: add synaptics_i2c driver

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

 



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

[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