From: Mike Rapoport <mike@compulab.co.il>
To: dmitry.torokhov@gmail.com
Cc: grinberg@compulab.co.il, linux-input@vger.kernel.org,
Mike Rapoport <mike@compulab.co.il>
Subject: [PATCH] input: add synaptics_i2c driver
Date: Wed, 13 May 2009 17:30:03 +0300 [thread overview]
Message-ID: <1242225003-8049-1-git-send-email-mike@compulab.co.il> (raw)
From: Igor Grinberg <grinberg@compulab.co.il>
This patch adds support for Synaptics i2c touchpad controller found on
eXeda machine.
Signed-off-by: Igor Grinberg <grinberg@compulab.co.il>
Signed-off-by: Mike Rapoport <mike@compulab.co.il>
---
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.
+
+ 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@compulab.co.il>
+ * Igor Grinberg <grinberg@compulab.co.il>
+ *
+ * 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)");
+
+/* 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");
+
+/* 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);
+}
+
+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;
+
+ 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;
+
+ 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,
+
+ .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");
--
1.6.0.6
next reply other threads:[~2009-05-13 15:04 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-05-13 14:30 Mike Rapoport [this message]
2009-05-14 2:50 ` [PATCH] input: add synaptics_i2c driver Dmitry Torokhov
2009-05-14 7:28 ` Jean Delvare
2009-05-14 9:04 ` Mike Rapoport
2009-05-14 11:00 ` Jean Delvare
2009-05-14 11:29 ` Mike Rapoport
2009-05-14 14:20 ` Mike Rapoport
2009-05-14 14:28 ` Jean Delvare
2009-05-14 15:01 ` Mike Rapoport
2009-05-14 15:03 ` Dmitry Torokhov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1242225003-8049-1-git-send-email-mike@compulab.co.il \
--to=mike@compulab.co.il \
--cc=dmitry.torokhov@gmail.com \
--cc=grinberg@compulab.co.il \
--cc=linux-input@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).