Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH 3/3] Input: synaptics-rmi4 - report sensor resolution
From: Christopher Heiny @ 2014-03-19  1:03 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
	Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
	David Herrmann, Jiri Kosina
In-Reply-To: <1395191031-3144-1-git-send-email-cheiny@synaptics.com>

Reports the sensor resolution by reading the size of the sensor
from F11 query registers or from the platform data if the firmware
does not contain the appropriate query registers.

Signed-off-by: Andrew Duggan <aduggan@synaptics.com>
Acked-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>

---
 drivers/input/rmi4/rmi_f11.c | 53 +++++++++++++++++++++++++++++++++++++++++++-
 include/linux/rmi.h          |  2 ++
 2 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
index f5b8b71..f2a6f5f 100644
--- a/drivers/input/rmi4/rmi_f11.c
+++ b/drivers/input/rmi4/rmi_f11.c
@@ -408,6 +408,10 @@ struct f11_2d_sensor_queries {
 	u8 clickpad_props;
 	u8 mouse_buttons;
 	bool has_advanced_gestures;
+
+	/* Query 15 - 18 */
+	u16 x_sensor_size_mm;
+	u16 y_sensor_size_mm;
 };
 
 /* Defs for Ctrl0. */
@@ -520,6 +524,8 @@ struct f11_2d_sensor {
 	struct rmi_function *fn;
 	char input_phys[NAME_BUFFER_SIZE];
 	char input_phys_mouse[NAME_BUFFER_SIZE];
+	u8 x_mm;
+	u8 y_mm;
 	u8 report_abs;
 	u8 report_rel;
 };
@@ -1098,6 +1104,20 @@ static int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev,
 		query_size++;
 	}
 
+	if (f11->has_query12 && sensor_query->has_physical_props) {
+		rc = rmi_read_block(rmi_dev, query_base_addr
+			+ query_size, query_buf, ARRAY_SIZE(query_buf));
+		if (rc < 0)
+			return rc;
+
+		sensor_query->x_sensor_size_mm =
+			(query_buf[0] | (query_buf[1] << 8)) / 10;
+		sensor_query->y_sensor_size_mm =
+			(query_buf[2] | (query_buf[3] << 8)) / 10;
+
+		query_size += 4;
+	}
+
 	return query_size;
 }
 
@@ -1119,6 +1139,7 @@ static void f11_set_abs_params(struct rmi_function *fn, struct f11_data *f11)
 			((f11->dev_controls.ctrl0_9[9] & 0x0F) << 8);
 	u16 x_min, x_max, y_min, y_max;
 	unsigned int input_flags;
+	int res_x, res_y;
 
 	/* We assume touchscreen unless demonstrably a touchpad or specified
 	 * as a touchpad in the platform data
@@ -1175,6 +1196,18 @@ static void f11_set_abs_params(struct rmi_function *fn, struct f11_data *f11)
 			x_min, x_max, 0, 0);
 	input_set_abs_params(input, ABS_MT_POSITION_Y,
 			y_min, y_max, 0, 0);
+
+	if (sensor->x_mm && sensor->y_mm) {
+		res_x = (x_max - x_min) / sensor->x_mm;
+		res_y = (y_max - y_min) / sensor->y_mm;
+
+		input_abs_set_res(input, ABS_X, res_x);
+		input_abs_set_res(input, ABS_Y, res_y);
+
+		input_abs_set_res(input, ABS_MT_POSITION_X, res_x);
+		input_abs_set_res(input, ABS_MT_POSITION_Y, res_y);
+	}
+
 	if (!sensor->type_a)
 		input_mt_init_slots(input, sensor->nbr_fingers, input_flags);
 	if (IS_ENABLED(CONFIG_RMI4_F11_PEN) && sensor->sens_query.has_pen)
@@ -1261,8 +1294,26 @@ static int rmi_f11_initialize(struct rmi_function *fn)
 		sensor->axis_align =
 			pdata->f11_sensor_data->axis_align;
 		sensor->type_a = pdata->f11_sensor_data->type_a;
-		sensor->sensor_type =
+
+		if (sensor->sens_query.has_info2) {
+			if (sensor->sens_query.is_clear)
+				sensor->sensor_type =
+					rmi_f11_sensor_touchscreen;
+			else
+				sensor->sensor_type = rmi_f11_sensor_touchpad;
+		} else {
+			sensor->sensor_type =
 				pdata->f11_sensor_data->sensor_type;
+		}
+
+		if (f11->has_query12
+			&& sensor->sens_query.has_physical_props) {
+			sensor->x_mm = sensor->sens_query.x_sensor_size_mm;
+			sensor->y_mm = sensor->sens_query.y_sensor_size_mm;
+		} else {
+			sensor->x_mm = pdata->f11_sensor_data->x_mm;
+			sensor->y_mm = pdata->f11_sensor_data->y_mm;
+		}
 
 		if (sensor->sens_query.has_abs)
 			sensor->report_abs = sensor->report_abs
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
index a0d0187..9139873 100644
--- a/include/linux/rmi.h
+++ b/include/linux/rmi.h
@@ -96,6 +96,8 @@ struct rmi_f11_sensor_data {
 	struct rmi_f11_2d_axis_alignment axis_align;
 	bool type_a;
 	enum rmi_f11_sensor_type sensor_type;
+	int x_mm;
+	int y_mm;
 	int disable_report_mask;
 };
 
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 1/3] Input: synaptics-rmi4 - add capabilities for touchpads
From: Christopher Heiny @ 2014-03-19  1:03 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
	Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
	David Herrmann, Jiri Kosina

When the device is a touchpad additional capabilities need to
be set and reported.

Signed-off-by: Andrew Duggan <aduggan@synaptics.com>
Acked-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>

---
 drivers/input/rmi4/rmi_f11.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
index 8709abe..07044d79 100644
--- a/drivers/input/rmi4/rmi_f11.c
+++ b/drivers/input/rmi4/rmi_f11.c
@@ -688,6 +688,9 @@ static void rmi_f11_abs_pos_report(struct f11_data *f11,
 	/* MT sync between fingers */
 	if (sensor->type_a)
 		input_mt_sync(sensor->input);
+
+	if (sensor->sensor_type == rmi_f11_sensor_touchpad)
+		input_mt_report_pointer_emulation(sensor->input, true);
 }
 
 static void rmi_f11_finger_handler(struct f11_data *f11,
@@ -717,7 +720,7 @@ static void rmi_f11_finger_handler(struct f11_data *f11,
 		if (sensor->data.rel_pos)
 			rmi_f11_rel_pos_report(sensor, i);
 	}
-	input_mt_sync(sensor->input);
+	input_report_key(sensor->input, BTN_TOUCH, finger_pressed_count);
 	input_sync(sensor->input);
 }
 
@@ -1137,6 +1140,9 @@ static void f11_set_abs_params(struct rmi_function *fn, struct f11_data *f11)
 	dev_dbg(&fn->dev, "Set ranges X=[%d..%d] Y=[%d..%d].",
 			x_min, x_max, y_min, y_max);
 
+	input_set_abs_params(input, ABS_X, x_min, x_max, 0, 0);
+	input_set_abs_params(input, ABS_Y, y_min, y_max, 0, 0);
+
 	input_set_abs_params(input, ABS_MT_PRESSURE, 0,
 			DEFAULT_MAX_ABS_MT_PRESSURE, 0, 0);
 	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
@@ -1374,6 +1380,15 @@ static int rmi_f11_register_devices(struct rmi_function *fn)
 		set_bit(BTN_RIGHT, input_dev_mouse->keybit);
 	}
 
+	if (sensor->sensor_type == rmi_f11_sensor_touchpad) {
+		set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+		set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+		set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
+		set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
+		set_bit(BTN_TOOL_QUINTTAP, input_dev->keybit);
+	}
+
+
 	return 0;
 
 error_unregister:
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 2/3] Input: synaptics-rmi4 - ability disable abs or rel reporting
From: Christopher Heiny @ 2014-03-19  1:03 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
	Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
	David Herrmann, Jiri Kosina
In-Reply-To: <1395191031-3144-1-git-send-email-cheiny@synaptics.com>

Even if the RMI4 touchscreen/touchpad provides reporting both
relative and absolute coordinates, reporting both to userspace
could be confusing. Allow the platform data to disable either
absolute or relative coordinates.

Signed-off-by: Andrew Duggan <aduggan@synaptics.com>
Acked-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>

---
 drivers/input/rmi4/rmi_f11.c | 78 +++++++++++++++++++++++++++++++++++++-------
 include/linux/rmi.h          |  6 ++++
 2 files changed, 73 insertions(+), 11 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
index 07044d79..c87c6cc3 100644
--- a/drivers/input/rmi4/rmi_f11.c
+++ b/drivers/input/rmi4/rmi_f11.c
@@ -520,6 +520,8 @@ struct f11_2d_sensor {
 	struct rmi_function *fn;
 	char input_phys[NAME_BUFFER_SIZE];
 	char input_phys_mouse[NAME_BUFFER_SIZE];
+	u8 report_abs;
+	u8 report_rel;
 };
 
 /** Data pertaining to F11 in general.  For per-sensor data, see struct
@@ -544,6 +546,10 @@ struct f11_data {
 	struct mutex dev_controls_mutex;
 	u16 rezero_wait_ms;
 	struct f11_2d_sensor sensor;
+	unsigned long *abs_mask;
+	unsigned long *rel_mask;
+	unsigned long *result_bits;
+	unsigned long mask_memory[];
 };
 
 enum finger_state_values {
@@ -591,10 +597,14 @@ static void rmi_f11_rel_pos_report(struct f11_2d_sensor *sensor, u8 n_finger)
 	if (x || y) {
 		input_report_rel(sensor->input, REL_X, x);
 		input_report_rel(sensor->input, REL_Y, y);
-		input_report_rel(sensor->mouse_input, REL_X, x);
-		input_report_rel(sensor->mouse_input, REL_Y, y);
+
+		if (sensor->mouse_input) {
+			input_report_rel(sensor->mouse_input, REL_X, x);
+			input_report_rel(sensor->mouse_input, REL_Y, y);
+		}
 	}
-	input_sync(sensor->mouse_input);
+	if (sensor->mouse_input)
+		input_sync(sensor->mouse_input);
 }
 
 static void rmi_f11_abs_pos_report(struct f11_data *f11,
@@ -694,13 +704,17 @@ static void rmi_f11_abs_pos_report(struct f11_data *f11,
 }
 
 static void rmi_f11_finger_handler(struct f11_data *f11,
-				   struct f11_2d_sensor *sensor)
+				   struct f11_2d_sensor *sensor,
+				   unsigned long *irq_bits, int num_irq_regs)
 {
 	const u8 *f_state = sensor->data.f_state;
 	u8 finger_state;
 	u8 finger_pressed_count;
 	u8 i;
 
+	int rel_bits;
+	int abs_bits;
+
 	for (i = 0, finger_pressed_count = 0; i < sensor->nbr_fingers; i++) {
 		/* Possible of having 4 fingers per f_statet register */
 		finger_state = (f_state[i / 4] >> (2 * (i % 4))) &
@@ -714,13 +728,19 @@ static void rmi_f11_finger_handler(struct f11_data *f11,
 			finger_pressed_count++;
 		}
 
-		if (sensor->data.abs_pos)
+		abs_bits = bitmap_and(f11->result_bits, irq_bits, f11->abs_mask,
+				num_irq_regs);
+		if (abs_bits)
 			rmi_f11_abs_pos_report(f11, sensor, finger_state, i);
 
-		if (sensor->data.rel_pos)
+		rel_bits = bitmap_and(f11->result_bits, irq_bits, f11->rel_mask,
+				num_irq_regs);
+		if (rel_bits)
 			rmi_f11_rel_pos_report(sensor, i);
 	}
+
 	input_report_key(sensor->input, BTN_TOUCH, finger_pressed_count);
+
 	input_sync(sensor->input);
 }
 
@@ -1180,21 +1200,33 @@ static int rmi_f11_initialize(struct rmi_function *fn)
 	u16 max_x_pos, max_y_pos, temp;
 	int rc;
 	const struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
+	struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
 	struct f11_2d_sensor *sensor;
 	u8 buf;
+	int mask_size;
 
 	dev_dbg(&fn->dev, "Initializing F11 values for %s.\n",
 		 pdata->sensor_name);
 
+	mask_size = BITS_TO_LONGS(drvdata->irq_count) * sizeof(unsigned long);
+
 	/*
 	** init instance data, fill in values and create any sysfs files
 	*/
-	f11 = devm_kzalloc(&fn->dev, sizeof(struct f11_data), GFP_KERNEL);
+	f11 = devm_kzalloc(&fn->dev, sizeof(struct f11_data) + mask_size * 3,
+			GFP_KERNEL);
 	if (!f11)
 		return -ENOMEM;
 
 	f11->rezero_wait_ms = pdata->f11_rezero_wait;
 
+	f11->abs_mask = f11->mask_memory + mask_size * 0;
+	f11->rel_mask = f11->mask_memory + mask_size * 1;
+	f11->result_bits = f11->mask_memory + mask_size * 2;
+
+	set_bit(fn->irq_pos, f11->abs_mask);
+	set_bit(fn->irq_pos + 1, f11->rel_mask);
+
 	query_base_addr = fn->fd.query_base_addr;
 	control_base_addr = fn->fd.control_base_addr;
 
@@ -1226,12 +1258,25 @@ static int rmi_f11_initialize(struct rmi_function *fn)
 		return rc;
 	}
 
+	sensor->report_rel = sensor->sens_query.has_rel;
+	sensor->report_abs = sensor->sens_query.has_abs;
+
 	if (pdata->f11_sensor_data) {
 		sensor->axis_align =
 			pdata->f11_sensor_data->axis_align;
 		sensor->type_a = pdata->f11_sensor_data->type_a;
 		sensor->sensor_type =
 				pdata->f11_sensor_data->sensor_type;
+
+		if (sensor->sens_query.has_abs)
+			sensor->report_abs = sensor->report_abs
+				&& !(pdata->f11_sensor_data->disable_report_mask
+					& RMI_F11_DISABLE_ABS_REPORT);
+
+		if (sensor->sens_query.has_rel)
+			sensor->report_rel = sensor->report_rel
+				&& !(pdata->f11_sensor_data->disable_report_mask
+					& RMI_F11_DISABLE_REL_REPORT);
 	}
 
 	rc = rmi_read_block(rmi_dev,
@@ -1324,9 +1369,10 @@ static int rmi_f11_register_devices(struct rmi_function *fn)
 	set_bit(EV_ABS, input_dev->evbit);
 	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
 
-	f11_set_abs_params(fn, f11);
+	if (sensor->report_abs)
+		f11_set_abs_params(fn, f11);
 
-	if (sensor->sens_query.has_rel) {
+	if (sensor->report_rel) {
 		set_bit(EV_REL, input_dev->evbit);
 		set_bit(REL_X, input_dev->relbit);
 		set_bit(REL_Y, input_dev->relbit);
@@ -1338,7 +1384,7 @@ static int rmi_f11_register_devices(struct rmi_function *fn)
 		goto error_unregister;
 	}
 
-	if (sensor->sens_query.has_rel) {
+	if (sensor->report_rel) {
 		/*create input device for mouse events  */
 		input_dev_mouse = input_allocate_device();
 		if (!input_dev_mouse) {
@@ -1407,8 +1453,16 @@ error_unregister:
 static int rmi_f11_config(struct rmi_function *fn)
 {
 	struct f11_data *f11 = dev_get_drvdata(&fn->dev);
+	struct rmi_driver *drv = fn->rmi_dev->driver;
+	struct f11_2d_sensor *sensor = &f11->sensor;
 	int rc;
 
+	if (!sensor->report_abs)
+		drv->clear_irq_bits(fn->rmi_dev, f11->abs_mask);
+
+	if (!sensor->report_rel)
+		drv->clear_irq_bits(fn->rmi_dev, f11->rel_mask);
+
 	rc = f11_write_control_regs(fn, &f11->sensor.sens_query,
 			   &f11->dev_controls, fn->fd.query_base_addr);
 	if (rc < 0)
@@ -1420,6 +1474,7 @@ static int rmi_f11_config(struct rmi_function *fn)
 static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits)
 {
 	struct rmi_device *rmi_dev = fn->rmi_dev;
+	struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
 	struct f11_data *f11 = dev_get_drvdata(&fn->dev);
 	u16 data_base_addr = fn->fd.data_base_addr;
 	u16 data_base_addr_offset = 0;
@@ -1432,7 +1487,8 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits)
 	if (error)
 		return error;
 
-	rmi_f11_finger_handler(f11, &f11->sensor);
+	rmi_f11_finger_handler(f11, &f11->sensor, irq_bits,
+				drvdata->num_of_irq_regs);
 	data_base_addr_offset += f11->sensor.pkt_size;
 
 	return 0;
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
index 735e978..a0d0187 100644
--- a/include/linux/rmi.h
+++ b/include/linux/rmi.h
@@ -76,6 +76,9 @@ enum rmi_f11_sensor_type {
 	rmi_f11_sensor_touchpad
 };
 
+#define RMI_F11_DISABLE_ABS_REPORT      (1 << 0)
+#define RMI_F11_DISABLE_REL_REPORT      (1 << 1)
+
 /**
  * struct rmi_f11_sensor_data - overrides defaults for a single F11 2D sensor.
  * @axis_align - provides axis alignment overrides (see above).
@@ -86,11 +89,14 @@ enum rmi_f11_sensor_type {
  * pointing device (touchpad) rather than a direct pointing device
  * (touchscreen).  This is useful when F11_2D_QUERY14 register is not
  * available.
+ * @disable_report_mask - Force data to not be reported even if it is supported
+ * by the firware.
  */
 struct rmi_f11_sensor_data {
 	struct rmi_f11_2d_axis_alignment axis_align;
 	bool type_a;
 	enum rmi_f11_sensor_type sensor_type;
+	int disable_report_mask;
 };
 
 /**
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH] HID: uhid: Add UHID_CREATE2 + UHID_INPUT2
From: Petri Gynther @ 2014-03-19  3:45 UTC (permalink / raw)
  To: linux-input

UHID_CREATE2:
HID report descriptor data (rd_data) is an array in struct uhid_create2_req,
instead of a pointer. Enables use from languages that don't support pointers,
e.g. Python.

UHID_INPUT2:
Data array is the last field of struct uhid_input2_req. Enables userspace to
write only the required bytes to kernel (ev.type + ev.u.input2.size + the part
of the data array that matters), instead of the entire struct uhid_input2_req.

Signed-off-by: Petri Gynther <pgynther@google.com>
---
 Documentation/hid/uhid.txt | 11 +++++++
 drivers/hid/uhid.c         | 80 ++++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/uhid.h  | 23 +++++++++++++
 3 files changed, 114 insertions(+)

diff --git a/Documentation/hid/uhid.txt b/Documentation/hid/uhid.txt
index dc35a2b..ee65936 100644
--- a/Documentation/hid/uhid.txt
+++ b/Documentation/hid/uhid.txt
@@ -93,6 +93,11 @@ the request was handled successfully.
   event to the kernel. The payload is of type struct uhid_create_req and
   contains information about your device. You can start I/O now.
 
+  UHID_CREATE2:
+  Same as UHID_CREATE, but the HID report descriptor data (rd_data) is an array
+  inside struct uhid_create2_req, instead of a pointer to a separate array.
+  Enables use from languages that don't support pointers, e.g. Python.
+
   UHID_DESTROY:
   This destroys the internal HID device. No further I/O will be accepted. There
   may still be pending messages that you can receive with read() but no further
@@ -105,6 +110,12 @@ the request was handled successfully.
   contains a data-payload. This is the raw data that you read from your device.
   The kernel will parse the HID reports and react on it.
 
+  UHID_INPUT2:
+  Same as UHID_INPUT, but the data array is the last field of uhid_input2_req.
+  Enables userspace to write only the required bytes to kernel (ev.type +
+  ev.u.input2.size + the part of the data array that matters), instead of
+  the entire struct uhid_input2_req.
+
   UHID_FEATURE_ANSWER:
   If you receive a UHID_FEATURE request you must answer with this request. You
   must copy the "id" field from the request into the answer. Set the "err" field
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index cedc6da..c5ee173 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -407,6 +407,69 @@ err_free:
 	return ret;
 }
 
+static int uhid_dev_create2(struct uhid_device *uhid,
+			    const struct uhid_event *ev)
+{
+	struct hid_device *hid;
+	int ret;
+
+	if (uhid->running)
+		return -EALREADY;
+
+	uhid->rd_size = ev->u.create2.rd_size;
+	if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)
+		return -EINVAL;
+
+	uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL);
+	if (!uhid->rd_data)
+		return -ENOMEM;
+
+	memcpy(uhid->rd_data, ev->u.create2.rd_data, uhid->rd_size);
+
+	hid = hid_allocate_device();
+	if (IS_ERR(hid)) {
+		ret = PTR_ERR(hid);
+		goto err_free;
+	}
+
+	strncpy(hid->name, ev->u.create2.name, 127);
+	hid->name[127] = 0;
+	strncpy(hid->phys, ev->u.create2.phys, 63);
+	hid->phys[63] = 0;
+	strncpy(hid->uniq, ev->u.create2.uniq, 63);
+	hid->uniq[63] = 0;
+
+	hid->ll_driver = &uhid_hid_driver;
+	hid->hid_get_raw_report = uhid_hid_get_raw;
+	hid->hid_output_raw_report = uhid_hid_output_raw;
+	hid->bus = ev->u.create2.bus;
+	hid->vendor = ev->u.create2.vendor;
+	hid->product = ev->u.create2.product;
+	hid->version = ev->u.create2.version;
+	hid->country = ev->u.create2.country;
+	hid->driver_data = uhid;
+	hid->dev.parent = uhid_misc.this_device;
+
+	uhid->hid = hid;
+	uhid->running = true;
+
+	ret = hid_add_device(hid);
+	if (ret) {
+		hid_err(hid, "Cannot register HID device\n");
+		goto err_hid;
+	}
+
+	return 0;
+
+err_hid:
+	hid_destroy_device(hid);
+	uhid->hid = NULL;
+	uhid->running = false;
+err_free:
+	kfree(uhid->rd_data);
+	return ret;
+}
+
 static int uhid_dev_destroy(struct uhid_device *uhid)
 {
 	if (!uhid->running)
@@ -435,6 +498,17 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)
 	return 0;
 }
 
+static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)
+{
+	if (!uhid->running)
+		return -EINVAL;
+
+	hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data,
+			 min_t(size_t, ev->u.input2.size, UHID_DATA_MAX), 0);
+
+	return 0;
+}
+
 static int uhid_dev_feature_answer(struct uhid_device *uhid,
 				   struct uhid_event *ev)
 {
@@ -571,12 +645,18 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
 	case UHID_CREATE:
 		ret = uhid_dev_create(uhid, &uhid->input_buf);
 		break;
+	case UHID_CREATE2:
+		ret = uhid_dev_create2(uhid, &uhid->input_buf);
+		break;
 	case UHID_DESTROY:
 		ret = uhid_dev_destroy(uhid);
 		break;
 	case UHID_INPUT:
 		ret = uhid_dev_input(uhid, &uhid->input_buf);
 		break;
+	case UHID_INPUT2:
+		ret = uhid_dev_input2(uhid, &uhid->input_buf);
+		break;
 	case UHID_FEATURE_ANSWER:
 		ret = uhid_dev_feature_answer(uhid, &uhid->input_buf);
 		break;
diff --git a/include/uapi/linux/uhid.h b/include/uapi/linux/uhid.h
index 414b74b..1e3b09c 100644
--- a/include/uapi/linux/uhid.h
+++ b/include/uapi/linux/uhid.h
@@ -21,6 +21,7 @@
 
 #include <linux/input.h>
 #include <linux/types.h>
+#include <linux/hid.h>
 
 enum uhid_event_type {
 	UHID_CREATE,
@@ -34,6 +35,8 @@ enum uhid_event_type {
 	UHID_INPUT,
 	UHID_FEATURE,
 	UHID_FEATURE_ANSWER,
+	UHID_CREATE2,
+	UHID_INPUT2,
 };
 
 struct uhid_create_req {
@@ -50,6 +53,19 @@ struct uhid_create_req {
 	__u32 country;
 } __attribute__((__packed__));
 
+struct uhid_create2_req {
+	__u8 name[128];
+	__u8 phys[64];
+	__u8 uniq[64];
+	__u16 rd_size;
+	__u16 bus;
+	__u32 vendor;
+	__u32 product;
+	__u32 version;
+	__u32 country;
+	__u8 rd_data[HID_MAX_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
 #define UHID_DATA_MAX 4096
 
 enum uhid_report_type {
@@ -63,6 +79,11 @@ struct uhid_input_req {
 	__u16 size;
 } __attribute__((__packed__));
 
+struct uhid_input2_req {
+	__u16 size;
+	__u8 data[UHID_DATA_MAX];
+} __attribute__((__packed__));
+
 struct uhid_output_req {
 	__u8 data[UHID_DATA_MAX];
 	__u16 size;
@@ -100,6 +121,8 @@ struct uhid_event {
 		struct uhid_output_ev_req output_ev;
 		struct uhid_feature_req feature;
 		struct uhid_feature_answer_req feature_answer;
+		struct uhid_create2_req create2;
+		struct uhid_input2_req input2;
 	} u;
 } __attribute__((__packed__));
 
-- 
1.9.0.279.gdc9e3eb


^ permalink raw reply related

* [PATCH] input: add support for ALPS v7 protocol device
From: Qiting Chen @ 2014-03-19  8:55 UTC (permalink / raw)
  To: dmitry.torokhov, cernekee, dturvene
  Cc: linux-input, ndevos, jclift, Qiting Chen

Here is a patch of supporting ALPS v7 protocol device.
ALPS v7 protocol device is a clickpad that is currently used on
Lenovo S430/S435/S530, Lenovo Z410/Z510, HP Odie, HP Revolve 810 G1,
as well as other machines with ALPS Touchpad of following infomation:
	Device ID = 0x73, 0x03, 0x0a
	Firmware ID = 0x88, 0xb*, 0x**

A v7 protocol support patch is first relesed 2 months ago:
http://www.spinics.net/lists/linux-input/msg29084.html
After that some feedbacks were received from end user. Now this patch fixed the bugs
reported by them:
1) Fix cursor jump when doing a right click drag
2) Fix cursor jitter when button clicking

Signed-off-by: Qiting Chen <qiting.chen@cn.alps.com>
---
 drivers/input/mouse/alps.c | 560 ++++++++++++++++++++++++++++++++++++++++++---
 drivers/input/mouse/alps.h | 132 +++++++++--
 2 files changed, 641 insertions(+), 51 deletions(-)

diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index fb15c64..383281f 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -32,6 +32,13 @@
 #define ALPS_REG_BASE_RUSHMORE	0xc2c0
 #define ALPS_REG_BASE_PINNACLE	0x0000
 
+#define LEFT_BUTTON_BIT			0x01
+#define RIGHT_BUTTON_BIT		0x02
+
+#define V7_LARGE_MOVEMENT		130
+#define V7_DEAD_ZONE_OFFSET_X	72
+#define V7_DEAD_ZONE_OFFSET_Y	72
+
 static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
 	{ PSMOUSE_CMD_SETPOLL,		0x00 }, /* 0 */
 	{ PSMOUSE_CMD_RESET_DIS,	0x00 }, /* 1 */
@@ -99,6 +106,7 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
 #define ALPS_FOUR_BUTTONS	0x40	/* 4 direction button present */
 #define ALPS_PS2_INTERLEAVED	0x80	/* 3-byte PS/2 packet interleaved with
 					   6-byte ALPS packet */
+#define ALPS_BTNLESS			0x100	/* ALPS ClickPad flag */
 
 static const struct alps_model_info alps_model_data[] = {
 	{ { 0x32, 0x02, 0x14 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },	/* Toshiba Salellite Pro M10 */
@@ -140,6 +148,20 @@ static void alps_set_abs_params_mt(struct alps_data *priv,
  * isn't valid per PS/2 spec.
  */
 
+static unsigned int alps_pt_distance(struct alps_abs_data *pt0,
+				    struct alps_abs_data *pt1)
+{
+	int vect_x, vect_y;
+
+	if (!pt0 || !pt1)
+		return 0;
+
+	vect_x = pt0->x - pt1->x;
+	vect_y = pt0->y - pt1->y;
+
+	return int_sqrt(vect_x * vect_x + vect_y * vect_y);
+}
+
 /* Packet formats are described in Documentation/input/alps.txt */
 
 static bool alps_is_valid_first_byte(struct alps_data *priv,
@@ -320,8 +342,8 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv,
 		end_bit = y_msb - 1;
 		box_middle_y = (priv->y_max * (start_bit + end_bit)) /
 				(2 * (priv->y_bits - 1));
-		*x1 = fields->x;
-		*y1 = fields->y;
+		*x1 = fields->pt.x;
+		*y1 = fields->pt.y;
 		*x2 = 2 * box_middle_x - *x1;
 		*y2 = 2 * box_middle_y - *y1;
 	}
@@ -461,6 +483,38 @@ static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers,
 	alps_set_slot(dev, 1, num_fingers == 2, x2, y2);
 }
 
+static void alps_report_coord_and_btn(struct psmouse *psmouse,
+				      struct alps_fields *f)
+{
+	struct input_dev *dev;
+
+	if (!psmouse || !f)
+		return;
+
+	dev = psmouse->dev;
+
+	if (f->fingers) {
+		input_report_key(dev, BTN_TOUCH, 1);
+		alps_report_semi_mt_data(dev, f->fingers,
+			f->pt_img[0].x, f->pt_img[0].y,
+			f->pt_img[1].x, f->pt_img[1].y);
+		input_mt_report_finger_count(dev, f->fingers);
+
+		input_report_abs(dev, ABS_X, f->pt_img[0].x);
+		input_report_abs(dev, ABS_Y, f->pt_img[0].y);
+		input_report_abs(dev, ABS_PRESSURE, f->pt_img[0].z);
+	} else {
+		input_report_key(dev, BTN_TOUCH, 0);
+		input_mt_report_finger_count(dev, 0);
+		input_report_abs(dev, ABS_PRESSURE, 0);
+	}
+
+	input_report_key(dev, BTN_LEFT, f->btn.left);
+	input_report_key(dev, BTN_RIGHT, f->btn.right);
+
+	input_sync(dev);
+}
+
 static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
 {
 	struct alps_data *priv = psmouse->private;
@@ -523,13 +577,13 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
 
 static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p)
 {
-	f->left = !!(p[3] & 0x01);
-	f->right = !!(p[3] & 0x02);
-	f->middle = !!(p[3] & 0x04);
+	f->btn.left = !!(p[3] & 0x01);
+	f->btn.right = !!(p[3] & 0x02);
+	f->btn.middle = !!(p[3] & 0x04);
 
-	f->ts_left = !!(p[3] & 0x10);
-	f->ts_right = !!(p[3] & 0x20);
-	f->ts_middle = !!(p[3] & 0x40);
+	f->btn.ts_left = !!(p[3] & 0x10);
+	f->btn.ts_right = !!(p[3] & 0x20);
+	f->btn.ts_middle = !!(p[3] & 0x40);
 }
 
 static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
@@ -546,10 +600,10 @@ static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
 		   ((p[2] & 0x7f) << 1) |
 		   (p[4] & 0x01);
 
-	f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
+	f->pt.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
 	       ((p[0] & 0x30) >> 4);
-	f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
-	f->z = p[5] & 0x7f;
+	f->pt.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
+	f->pt.z = p[5] & 0x7f;
 
 	alps_decode_buttons_v3(f, p);
 }
@@ -573,9 +627,9 @@ static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
 	f->is_mp = !!(p[0] & 0x20);
 
 	if (!f->is_mp) {
-		f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
-		f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
-		f->z = (p[0] & 4) ? 0 : p[5] & 0x7f;
+		f->pt.x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
+		f->pt.y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
+		f->pt.z = (p[0] & 4) ? 0 : p[5] & 0x7f;
 		alps_decode_buttons_v3(f, p);
 	} else {
 		f->fingers = ((p[0] & 0x6) >> 1 |
@@ -687,7 +741,7 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
 	 * with x, y, and z all zero, so these seem to be flukes.
 	 * Ignore them.
 	 */
-	if (f.x && f.y && !f.z)
+	if (f.pt.x && f.pt.y && !f.pt.z)
 		return;
 
 	/*
@@ -695,12 +749,12 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
 	 * to rely on ST data.
 	 */
 	if (!fingers) {
-		x1 = f.x;
-		y1 = f.y;
-		fingers = f.z > 0 ? 1 : 0;
+		x1 = f.pt.x;
+		y1 = f.pt.y;
+		fingers = f.pt.z > 0 ? 1 : 0;
 	}
 
-	if (f.z >= 64)
+	if (f.pt.z >= 64)
 		input_report_key(dev, BTN_TOUCH, 1);
 	else
 		input_report_key(dev, BTN_TOUCH, 0);
@@ -709,22 +763,22 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
 
 	input_mt_report_finger_count(dev, fingers);
 
-	input_report_key(dev, BTN_LEFT, f.left);
-	input_report_key(dev, BTN_RIGHT, f.right);
-	input_report_key(dev, BTN_MIDDLE, f.middle);
+	input_report_key(dev, BTN_LEFT, f.btn.left);
+	input_report_key(dev, BTN_RIGHT, f.btn.right);
+	input_report_key(dev, BTN_MIDDLE, f.btn.middle);
 
-	if (f.z > 0) {
-		input_report_abs(dev, ABS_X, f.x);
-		input_report_abs(dev, ABS_Y, f.y);
+	if (f.pt.z > 0) {
+		input_report_abs(dev, ABS_X, f.pt.x);
+		input_report_abs(dev, ABS_Y, f.pt.y);
 	}
-	input_report_abs(dev, ABS_PRESSURE, f.z);
+	input_report_abs(dev, ABS_PRESSURE, f.pt.z);
 
 	input_sync(dev);
 
 	if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
-		input_report_key(dev2, BTN_LEFT, f.ts_left);
-		input_report_key(dev2, BTN_RIGHT, f.ts_right);
-		input_report_key(dev2, BTN_MIDDLE, f.ts_middle);
+		input_report_key(dev2, BTN_LEFT, f.btn.ts_left);
+		input_report_key(dev2, BTN_RIGHT, f.btn.ts_right);
+		input_report_key(dev2, BTN_MIDDLE, f.btn.ts_middle);
 		input_sync(dev2);
 	}
 }
@@ -916,6 +970,364 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
 	input_sync(dev);
 }
 
+static bool alps_is_valid_package_v7(struct psmouse *psmouse)
+{
+	if ((psmouse->pktcnt == 3) && ((psmouse->packet[2] & 0x40) != 0x40))
+		return false;
+	if ((psmouse->pktcnt == 4) && ((psmouse->packet[3] & 0x48) != 0x48))
+		return false;
+	if ((psmouse->pktcnt == 6) && ((psmouse->packet[5] & 0x40) != 0x0))
+		return false;
+	return true;
+}
+
+static int alps_drop_unsupported_packet_v7(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	int drop = 1;
+
+	if (priv->r.v7.pkt_id == V7_PACKET_ID_NEW ||
+	    priv->r.v7.pkt_id == V7_PACKET_ID_TWO ||
+	    priv->r.v7.pkt_id == V7_PACKET_ID_MULTI ||
+	    priv->r.v7.pkt_id == V7_PACKET_ID_IDLE)
+		drop = 0;
+
+	return drop;
+}
+
+static unsigned char alps_get_packet_id_v7(char *byte)
+{
+	unsigned char packet_id;
+
+	if (byte[4] & 0x40)
+		packet_id = V7_PACKET_ID_TWO;
+	else if (byte[4] & 0x01)
+		packet_id = V7_PACKET_ID_MULTI;
+	else if ((byte[0] & 0x10) && !(byte[4] & 0x43))
+		packet_id = V7_PACKET_ID_NEW;
+	else
+		packet_id = V7_PACKET_ID_IDLE;
+
+	return packet_id;
+}
+
+static void alps_get_finger_coordinate_v7(struct alps_abs_data *pt,
+					  unsigned char *pkt,
+					  unsigned char pkt_id)
+{
+	if ((pkt_id == V7_PACKET_ID_TWO) ||
+	   (pkt_id == V7_PACKET_ID_MULTI) ||
+	   (pkt_id == V7_PACKET_ID_NEW)) {
+		pt[0].x = ((pkt[2] & 0x80) << 4);
+		pt[0].x |= ((pkt[2] & 0x3F) << 5);
+		pt[0].x |= ((pkt[3] & 0x30) >> 1);
+		pt[0].x |= (pkt[3] & 0x07);
+		pt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07);
+
+		pt[1].x = ((pkt[3] & 0x80) << 4);
+		pt[1].x |= ((pkt[4] & 0x80) << 3);
+		pt[1].x |= ((pkt[4] & 0x3F) << 4);
+		pt[1].y = ((pkt[5] & 0x80) << 3);
+		pt[1].y |= ((pkt[5] & 0x3F) << 4);
+
+		if (pkt_id == V7_PACKET_ID_TWO) {
+			pt[1].x &= ~0x000F;
+			pt[1].y |= 0x000F;
+		} else if (pkt_id == V7_PACKET_ID_MULTI) {
+			pt[1].x &= ~0x003F;
+			pt[1].y &= ~0x0020;
+			pt[1].y |= ((pkt[4] & 0x02) << 4);
+			pt[1].y |= 0x001F;
+		} else if (pkt_id == V7_PACKET_ID_NEW) {
+			pt[1].x &= ~0x003F;
+			pt[1].x |= (pkt[0] & 0x20);
+			pt[1].y |= 0x000F;
+		}
+
+		pt[0].y = 0x7FF - pt[0].y;
+		pt[1].y = 0x7FF - pt[1].y;
+
+		pt[0].z = (pt[0].x && pt[0].y) ? 62 : 0;
+		pt[1].z = (pt[1].x && pt[1].y) ? 62 : 0;
+	}
+}
+
+static void alps_decode_packet_v7(struct alps_fields *f,
+				  unsigned char *p,
+				  struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	static struct v7_raw prev_r;
+
+	priv->r.v7.pkt_id = alps_get_packet_id_v7(p);
+
+	alps_get_finger_coordinate_v7(f->pt_img, p, priv->r.v7.pkt_id);
+
+	priv->r.v7.rest_left = 0;
+	priv->r.v7.rest_right = 0;
+	priv->r.v7.additional_fingers = 0;
+	priv->phy_btn = 0;
+
+	if (priv->r.v7.pkt_id == V7_PACKET_ID_TWO ||
+	    priv->r.v7.pkt_id == V7_PACKET_ID_MULTI) {
+		priv->r.v7.rest_left = (p[0] & 0x10) >> 4;
+		priv->r.v7.rest_right = (p[0] & 0x20) >> 5;
+	}
+
+	if (priv->r.v7.pkt_id == V7_PACKET_ID_MULTI)
+		priv->r.v7.additional_fingers = p[5] & 0x03;
+
+	priv->phy_btn = (p[0] & 0x80) >> 7;
+
+	if (priv->r.v7.pkt_id == V7_PACKET_ID_TWO) {
+		if (f->pt_img[0].z != 0 && f->pt_img[1].z != 0)
+			priv->r.v7.raw_fn = 2;
+		else
+			priv->r.v7.raw_fn = 1;
+	} else if (priv->r.v7.pkt_id == V7_PACKET_ID_MULTI)
+		priv->r.v7.raw_fn = 3 + priv->r.v7.additional_fingers;
+	else if (priv->r.v7.pkt_id == V7_PACKET_ID_IDLE)
+		priv->r.v7.raw_fn = 0;
+	else if (priv->r.v7.pkt_id == V7_PACKET_ID_NEW)
+		priv->r.v7.raw_fn = prev_r.raw_fn;
+
+	/* It is a trick to bypass firmware bug of older version
+	that 'New' Packet is missed when finger number changed.
+	We fake a 'New' Packet in such cases.*/
+	if (priv->r.v7.pkt_id == V7_PACKET_ID_TWO ||
+		priv->r.v7.pkt_id == V7_PACKET_ID_MULTI ||
+		priv->r.v7.pkt_id == V7_PACKET_ID_IDLE) {
+		if (priv->r.v7.raw_fn != prev_r.raw_fn)
+			priv->r.v7.pkt_id = V7_PACKET_ID_NEW;
+	}
+
+	memcpy(&prev_r, &priv->r.v7, sizeof(struct v7_raw));
+}
+
+static void alps_set_each_pt_attr_v7(struct psmouse *psmouse,
+				     struct alps_abs_data *pt,
+				     struct alps_bl_pt_attr *pt_attr)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned int dist;
+
+	if (!pt_attr->is_init_pt_got && pt->z != 0) {
+		pt_attr->is_init_pt_got = 1;
+		pt_attr->is_counted = 0;
+		memcpy(&pt_attr->init_pt, pt, sizeof(pt_attr->init_pt));
+	}
+
+	if (pt->z != 0) {
+		if (pt->y < priv->resting_zone_y_min) {
+			/* A finger is recognized as a non-resting finger
+			if it's position is outside the resting finger zone.*/
+			pt_attr->zone = ZONE_NORMAL;
+			pt_attr->is_counted = 1;
+		} else {
+			/* A finger is recognized as a resting finger if it's
+			position is inside the resting finger zone and there's
+			no large movement from it's touch down position.*/
+			pt_attr->zone = ZONE_RESTING;
+
+			if (pt->x > priv->x_max / 2)
+				pt_attr->zone |= ZONE_RIGHT_BTN;
+			else
+				pt_attr->zone |= ZONE_LEFT_BTN;
+
+			/* A resting finger will turn to be a non-resting
+			finger if it has made large movement from it's touch
+			down position. A non-resting finger will never turn
+			to a resting finger before it leaves the touchpad
+			surface.*/
+			if (pt_attr->is_init_pt_got) {
+				dist = alps_pt_distance(pt, &pt_attr->init_pt);
+
+				if (dist > V7_LARGE_MOVEMENT)
+					pt_attr->is_counted = 1;
+			}
+		}
+	}
+}
+
+static void alps_set_pt_attr_v7(struct psmouse *psmouse,
+				       struct alps_fields *f)
+{
+	struct alps_data *priv = psmouse->private;
+	int i;
+
+	switch (priv->r.v7.pkt_id) {
+	case  V7_PACKET_ID_TWO:
+	case  V7_PACKET_ID_MULTI:
+		for (i = 0; i < V7_IMG_PT_NUM; i++) {
+			alps_set_each_pt_attr_v7(psmouse,
+						 &f->pt_img[i],
+						 &priv->pt_attr[i]);
+		}
+		break;
+	default:
+		/*All finger attributes are cleared when packet ID is
+		'IDLE', 'New'or other unknown IDs. An 'IDLE' packet
+		indicates that there's no finger and no button activity.
+		A 'NEW' packet indicates the finger position in packet
+		is not continues from previous packet. Such as the
+		condition there's finger placed or lifted. In these cases,
+		finger attributes will be reset.*/
+		memset(priv->pt_attr, 0, sizeof(priv->pt_attr[0]) * 2);
+		break;
+	}
+}
+
+static void alps_cal_output_finger_num_v7(struct psmouse *psmouse,
+					struct alps_fields *f)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned int fn = 0;
+	int i;
+
+	switch (priv->r.v7.pkt_id) {
+	case V7_PACKET_ID_IDLE:
+	case V7_PACKET_ID_NEW:
+		/*No finger is reported when packet ID is 'IDLE' or 'New'.
+		An 'IDLE' packet indicates that there's no finger on touchpad.
+		A 'NEW' packet indicates there's finger placed or lifted.
+		Finger position of 'New' packet is not continues from the
+		previous packet.*/
+		fn = 0;
+		break;
+	case V7_PACKET_ID_TWO:
+		if (f->pt_img[0].z == 0) {
+			/*The first finger slot is zero when a non-resting
+			finger lifted and remaining only one resting finger
+			on touchpad. Hardware report the remaining resting
+			finger in second slot. This resting is ignored*/
+			fn = 0;
+		} else if (f->pt_img[1].z == 0) {
+			/* The second finger slot is zero if there's
+			only one finger*/
+			fn = 1;
+		} else {
+			/*All non-resting fingers will be counted to report*/
+			fn = 0;
+			for (i = 0; i < V7_IMG_PT_NUM; i++) {
+				if (priv->pt_attr[i].is_counted)
+					fn++;
+			}
+
+			/*In the case that both fingers are
+			resting fingers, report the first one*/
+			if (!priv->pt_attr[0].is_counted &&
+			    !priv->pt_attr[1].is_counted) {
+				fn = 1;
+			}
+		}
+		break;
+	case V7_PACKET_ID_MULTI:
+		/*A packet ID 'MULTI' indicats that at least 3 non-resting
+		finger exist.*/
+		fn = 3 + priv->r.v7.additional_fingers;
+		break;
+	}
+
+	f->fingers = fn;
+}
+
+static void alps_button_dead_zone_filter(struct psmouse *psmouse,
+				   struct alps_fields *f,
+				   struct alps_fields *prev_f)
+{
+	struct alps_data *priv = psmouse->private;
+	int dx, dy;
+
+	if (priv->prev_phy_btn == 0 && priv->phy_btn != 0) {
+		memcpy(&priv->pt_attr[0].init_dead_pt,
+				&f->pt_img[0],
+				sizeof(struct alps_abs_data));
+	}
+
+	if (priv->pt_attr[0].init_dead_pt.x != 0 &&
+		priv->pt_attr[0].init_dead_pt.x != 0) {
+			dx = f->pt_img[0].x - priv->pt_attr[0].init_dead_pt.x;
+			dy = f->pt_img[0].y - priv->pt_attr[0].init_dead_pt.y;
+		if ((abs(dx) > V7_DEAD_ZONE_OFFSET_X) ||
+			(abs(dy) > V7_DEAD_ZONE_OFFSET_Y)) {
+				memset(&priv->pt_attr[0].init_dead_pt, 0,
+						sizeof(struct alps_abs_data));
+				priv->btn_delay_cnt = 0;
+		} else {
+			memcpy(&f->pt_img[0],
+					&prev_f->pt_img[0],
+					sizeof(struct alps_abs_data));
+			if (priv->prev_phy_btn == 0 && priv->phy_btn != 0)
+				priv->btn_delay_cnt = 2;
+		}
+	}
+
+	if (priv->btn_delay_cnt > 0) {
+		f->btn.left = 0;
+		f->btn.right = 0;
+		priv->btn_delay_cnt--;
+	}
+}
+
+static void alps_assign_buttons_v7(struct psmouse *psmouse,
+				   struct alps_fields *f,
+				   struct alps_fields *prev_f)
+{
+	struct alps_data *priv = psmouse->private;
+
+	if (priv->phy_btn) {
+		if (!priv->prev_phy_btn) {
+			/* Report a right click as long as there's finger on
+			right button zone. Othrewise, report a left click.*/
+			if (priv->r.v7.rest_right ||
+			    priv->pt_attr[0].zone & ZONE_RIGHT_BTN ||
+			    priv->pt_attr[1].zone & ZONE_RIGHT_BTN) {
+				f->btn.right = 1;
+				priv->pressed_btn_bits |= RIGHT_BUTTON_BIT;
+			} else {
+				f->btn.left = 1;
+				priv->pressed_btn_bits |= LEFT_BUTTON_BIT;
+			}
+		} else {
+			if (priv->pressed_btn_bits & RIGHT_BUTTON_BIT)
+				f->btn.right = 1;
+			if (priv->pressed_btn_bits & LEFT_BUTTON_BIT)
+				f->btn.left = 1;
+		}
+	} else {
+		priv->pressed_btn_bits = 0;
+		f->btn.right = 0;
+		f->btn.left = 0;
+	}
+
+	alps_button_dead_zone_filter(psmouse, f, prev_f);
+
+	priv->prev_phy_btn = priv->phy_btn;
+}
+
+static void alps_process_packet_v7(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	struct alps_fields f = {0};
+	static struct alps_fields prev_f;
+	unsigned char *packet = psmouse->packet;
+
+	priv->decode_fields(&f, packet, psmouse);
+
+	if (alps_drop_unsupported_packet_v7(psmouse))
+		return;
+
+	alps_set_pt_attr_v7(psmouse, &f);
+
+	alps_cal_output_finger_num_v7(psmouse, &f);
+
+	alps_assign_buttons_v7(psmouse, &f, &prev_f);
+
+	alps_report_coord_and_btn(psmouse, &f);
+
+	memcpy(&prev_f, &f, sizeof(struct alps_fields));
+}
+
 static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
 					unsigned char packet[],
 					bool report_buttons)
@@ -1080,6 +1492,14 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
 		return PSMOUSE_BAD_DATA;
 	}
 
+	if ((priv->proto_version == ALPS_PROTO_V7 &&
+	    !alps_is_valid_package_v7(psmouse))) {
+		psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
+			    psmouse->pktcnt - 1,
+			    psmouse->packet[psmouse->pktcnt - 1]);
+		return PSMOUSE_BAD_DATA;
+	}
+
 	if (psmouse->pktcnt == psmouse->pktsize) {
 		priv->process_packet(psmouse);
 		return PSMOUSE_FULL_PACKET;
@@ -1192,6 +1612,22 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command,
 	return 0;
 }
 
+static int alps_check_valid_firmware_id(unsigned char id[])
+{
+	int valid = 1;
+
+	if (id[0] == 0x73)
+		valid = 1;
+	else if (id[0] == 0x88) {
+		if ((id[1] == 0x07) ||
+		    (id[1] == 0x08) ||
+		    ((id[1] & 0xf0) == 0xB0))
+			valid = 1;
+	}
+
+	return valid;
+}
+
 static int alps_enter_command_mode(struct psmouse *psmouse)
 {
 	unsigned char param[4];
@@ -1201,8 +1637,7 @@ static int alps_enter_command_mode(struct psmouse *psmouse)
 		return -1;
 	}
 
-	if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) &&
-	    param[0] != 0x73) {
+	if (!alps_check_valid_firmware_id(param)) {
 		psmouse_dbg(psmouse,
 			    "unknown response while entering command mode\n");
 		return -1;
@@ -1704,6 +2139,36 @@ error:
 	return ret;
 }
 
+static int alps_hw_init_v7(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	int reg_val, ret = -1;
+
+	if (alps_enter_command_mode(psmouse))
+		goto error;
+
+	reg_val = alps_command_mode_read_reg(psmouse, 0xc2d9);
+	if (reg_val == -1)
+		goto error;
+
+	if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
+		goto error;
+
+	reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4);
+	if (reg_val == -1)
+		goto error;
+
+	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02))
+		goto error;
+
+	alps_exit_command_mode(psmouse);
+	return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+error:
+	alps_exit_command_mode(psmouse);
+	return ret;
+}
+
 /* Must be in command mode when calling this function */
 static int alps_absolute_mode_v4(struct psmouse *psmouse)
 {
@@ -1875,6 +2340,7 @@ static void alps_set_defaults(struct alps_data *priv)
 		priv->set_abs_params = alps_set_abs_params_st;
 		priv->x_max = 1023;
 		priv->y_max = 767;
+		priv->slot_number = 1;
 		break;
 	case ALPS_PROTO_V3:
 		priv->hw_init = alps_hw_init_v3;
@@ -1883,6 +2349,7 @@ static void alps_set_defaults(struct alps_data *priv)
 		priv->decode_fields = alps_decode_pinnacle;
 		priv->nibble_commands = alps_v3_nibble_commands;
 		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+		priv->slot_number = 2;
 		break;
 	case ALPS_PROTO_V4:
 		priv->hw_init = alps_hw_init_v4;
@@ -1890,6 +2357,7 @@ static void alps_set_defaults(struct alps_data *priv)
 		priv->set_abs_params = alps_set_abs_params_mt;
 		priv->nibble_commands = alps_v4_nibble_commands;
 		priv->addr_command = PSMOUSE_CMD_DISABLE;
+		priv->slot_number = 2;
 		break;
 	case ALPS_PROTO_V5:
 		priv->hw_init = alps_hw_init_dolphin_v1;
@@ -1905,6 +2373,7 @@ static void alps_set_defaults(struct alps_data *priv)
 		priv->y_max = 660;
 		priv->x_bits = 23;
 		priv->y_bits = 12;
+		priv->slot_number = 2;
 		break;
 	case ALPS_PROTO_V6:
 		priv->hw_init = alps_hw_init_v6;
@@ -1913,6 +2382,28 @@ static void alps_set_defaults(struct alps_data *priv)
 		priv->nibble_commands = alps_v6_nibble_commands;
 		priv->x_max = 2047;
 		priv->y_max = 1535;
+		priv->slot_number = 2;
+		break;
+	case ALPS_PROTO_V7:
+		priv->hw_init = alps_hw_init_v7;
+		priv->process_packet = alps_process_packet_v7;
+		priv->decode_fields = alps_decode_packet_v7;
+		priv->set_abs_params = alps_set_abs_params_mt;
+		priv->nibble_commands = alps_v3_nibble_commands;
+		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+		priv->x_max = 0xfff;
+		priv->y_max = 0x7ff;
+		priv->resting_zone_y_min = 0x654;
+		priv->byte0 = 0x48;
+		priv->mask0 = 0x48;
+		priv->flags = 0;
+		priv->slot_number = 2;
+
+		priv->phy_btn = 0;
+		priv->prev_phy_btn = 0;
+		priv->btn_delay_cnt = 0;
+		priv->pressed_btn_bits = 0;
+		memset(priv->pt_attr, 0, sizeof(priv->pt_attr[0]) * 2);
 		break;
 	}
 }
@@ -1982,6 +2473,11 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
 			return -EIO;
 		else
 			return 0;
+	} else if (ec[0] == 0x88 && (ec[1] & 0xf0) == 0xB0) {
+		priv->proto_version = ALPS_PROTO_V7;
+		alps_set_defaults(priv);
+
+		return 0;
 	} else if (ec[0] == 0x88 && ec[1] == 0x08) {
 		priv->proto_version = ALPS_PROTO_V3;
 		alps_set_defaults(priv);
@@ -2045,7 +2541,7 @@ static void alps_set_abs_params_mt(struct alps_data *priv,
 				   struct input_dev *dev1)
 {
 	set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
-	input_mt_init_slots(dev1, 2, 0);
+	input_mt_init_slots(dev1, priv->slot_number, 0);
 	input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
 	input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
 
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index 03f88b6..dedbd27 100644
--- a/drivers/input/mouse/alps.h
+++ b/drivers/input/mouse/alps.h
@@ -18,11 +18,36 @@
 #define ALPS_PROTO_V4	4
 #define ALPS_PROTO_V5	5
 #define ALPS_PROTO_V6	6
+#define ALPS_PROTO_V7	7
+
+#define MAX_IMG_PT_NUM		5
+#define V7_IMG_PT_NUM		2
+
+#define ZONE_NORMAL				0x01
+#define ZONE_RESTING			0x02
+#define ZONE_LEFT_BTN			0x04
+#define ZONE_RIGHT_BTN			0x08
 
 #define DOLPHIN_COUNT_PER_ELECTRODE	64
 #define DOLPHIN_PROFILE_XOFFSET		8	/* x-electrode offset */
 #define DOLPHIN_PROFILE_YOFFSET		1	/* y-electrode offset */
 
+/*
+ * enum V7_PACKET_ID - defines the packet type for V7
+ * V7_PACKET_ID_IDLE: There's no finger and no button activity.
+ * V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad
+ *  or there's button activities.
+ * V7_PACKET_ID_MULTI: There are at least three non-resting fingers.
+ * V7_PACKET_ID_NEW: The finger position in slot is not continues from
+ *  previous packet.
+*/
+enum V7_PACKET_ID {
+	 V7_PACKET_ID_IDLE,
+	 V7_PACKET_ID_TWO,
+	 V7_PACKET_ID_MULTI,
+	 V7_PACKET_ID_NEW,
+};
+
 /**
  * struct alps_model_info - touchpad ID table
  * @signature: E7 response string to match.
@@ -66,15 +91,7 @@ struct alps_nibble_commands {
 };
 
 /**
- * struct alps_fields - decoded version of the report packet
- * @x_map: Bitmap of active X positions for MT.
- * @y_map: Bitmap of active Y positions for MT.
- * @fingers: Number of fingers for MT.
- * @x: X position for ST.
- * @y: Y position for ST.
- * @z: Z position for ST.
- * @first_mp: Packet is the first of a multi-packet report.
- * @is_mp: Packet is part of a multi-packet report.
+ * struct alps_btn - decoded version of the button status
  * @left: Left touchpad button is active.
  * @right: Right touchpad button is active.
  * @middle: Middle touchpad button is active.
@@ -82,16 +99,7 @@ struct alps_nibble_commands {
  * @ts_right: Right trackstick button is active.
  * @ts_middle: Middle trackstick button is active.
  */
-struct alps_fields {
-	unsigned int x_map;
-	unsigned int y_map;
-	unsigned int fingers;
-	unsigned int x;
-	unsigned int y;
-	unsigned int z;
-	unsigned int first_mp:1;
-	unsigned int is_mp:1;
-
+struct alps_btn {
 	unsigned int left:1;
 	unsigned int right:1;
 	unsigned int middle:1;
@@ -102,6 +110,73 @@ struct alps_fields {
 };
 
 /**
+ * struct alps_btn - decoded version of the X Y Z postion for ST.
+ * @x: X position for ST.
+ * @y: Y position for ST.
+ * @z: Z position for ST.
+ */
+struct alps_abs_data {
+	unsigned int x;
+	unsigned int y;
+	unsigned int z;
+};
+
+/**
+ * struct alps_fields - decoded version of the report packet
+ * @fingers: Number of fingers for MT.
+ * @pt: X Y Z postion for ST.
+ * @pt: X Y Z postion for image MT.
+ * @x_map: Bitmap of active X positions for MT.
+ * @y_map: Bitmap of active Y positions for MT.
+ * @first_mp: Packet is the first of a multi-packet report.
+ * @is_mp: Packet is part of a multi-packet report.
+ * @btn: Button activity status
+ */
+struct alps_fields {
+	unsigned int fingers;
+	struct alps_abs_data pt;
+	struct alps_abs_data pt_img[MAX_IMG_PT_NUM];
+	unsigned int x_map;
+	unsigned int y_map;
+	unsigned int first_mp:1;
+	unsigned int is_mp:1;
+	struct alps_btn btn;
+};
+
+/**
+ * struct v7_raw - data decoded from raw packet for V7.
+ * @pkt_id: An id that specifies the type of packet.
+ * @additional_fingers: Number of additional finger that is neighter included
+ *  in pt slot nor reflected in rest_left and rest_right flag of data packet.
+ * @rest_left: There are fingers on left resting zone.
+ * @rest_right: There are fingers on right resting zone.
+ * @raw_fn: The number of finger on touchpad.
+ */
+struct v7_raw {
+	unsigned char pkt_id;
+	unsigned int additional_fingers;
+	unsigned char rest_left;
+	unsigned char rest_right;
+	unsigned char raw_fn;
+};
+
+/**
+ * struct alps_bl_pt_attr - generic attributes of touch points for buttonless device
+ * @zone: The part of touchpad that the touch point locates
+ * @is_counted: The touch point is not a resting finger.
+ * @is_init_pt_got: The touch down point is got.
+ * @init_pt: The X Y Z position of the touch down point.
+ * @init_dead_pt: The touch down point of a finger used by dead zone process.
+ */
+struct alps_bl_pt_attr {
+	unsigned char zone;
+	unsigned char is_counted;
+	unsigned char is_init_pt_got;
+	struct alps_abs_data init_pt;
+	struct alps_abs_data init_dead_pt;
+};
+
+/**
  * struct alps_data - private data structure for the ALPS driver
  * @dev2: "Relative" device used to report trackstick or mouse activity.
  * @phys: Physical path for the relative device.
@@ -116,8 +191,10 @@ struct alps_fields {
  * @flags: Additional device capabilities (passthrough port, trackstick, etc.).
  * @x_max: Largest possible X position value.
  * @y_max: Largest possible Y position value.
+ * @resting_zone_y_min: Smallest Y postion value of the bottom resting zone.
  * @x_bits: Number of X bits in the MT bitmap.
  * @y_bits: Number of Y bits in the MT bitmap.
+ * @img_fingers: Number of image fingers.
  * @hw_init: Protocol-specific hardware init function.
  * @process_packet: Protocol-specific function to process a report packet.
  * @decode_fields: Protocol-specific function to read packet bitfields.
@@ -132,6 +209,11 @@ struct alps_fields {
  * @fingers: Number of fingers from last MT report.
  * @quirks: Bitmap of ALPS_QUIRK_*.
  * @timer: Timer for flushing out the final report packet in the stream.
+ * @v7: Data decoded from raw packet for V7
+ * @phy_btn: Physical button is active.
+ * @prev_phy_btn: Physical button of previous packet is active.
+ * @pressed_btn_bits: Pressed positon of button zone
+ * @pt_attr: Generic attributes of touch points for buttonless device.
  */
 struct alps_data {
 	struct input_dev *dev2;
@@ -145,8 +227,10 @@ struct alps_data {
 	unsigned char flags;
 	int x_max;
 	int y_max;
+	int resting_zone_y_min;
 	int x_bits;
 	int y_bits;
+	unsigned char slot_number;
 
 	int (*hw_init)(struct psmouse *psmouse);
 	void (*process_packet)(struct psmouse *psmouse);
@@ -161,6 +245,16 @@ struct alps_data {
 	int fingers;
 	u8 quirks;
 	struct timer_list timer;
+
+	/* these are used for buttonless touchpad*/
+	union {
+		struct v7_raw v7;
+	} r;
+	unsigned char phy_btn;
+	unsigned char prev_phy_btn;
+	unsigned char btn_delay_cnt;
+	unsigned char pressed_btn_bits;
+	struct alps_bl_pt_attr pt_attr[MAX_IMG_PT_NUM];
 };
 
 #define ALPS_QUIRK_TRACKSTICK_BUTTONS	1 /* trakcstick buttons in trackstick packet */
-- 
1.8.3.2


^ permalink raw reply related

* Re: [PATCH] input: add support for ALPS v7 protocol device
From: vencik @ 2014-03-19  9:11 UTC (permalink / raw)
  To: Qiting Chen; +Cc: linux-input
In-Reply-To: <1395219353-27683-1-git-send-email-qiting.chen@cn.alps.com>

Hello,

by the E7 and EC identification, that's what I have on Toshiba Portege Z30-A-12N, too
(E7 = 0x73, 0x03, 0x0a, EC = 0x88, 0xb3, 0x22).

I was in process of creation of my own driver; you've overtaken me... :-)

Thanks,

Regards,

vencik


______________________________________________________________
> Od: Qiting Chen <elaineee66@gmail.com>
> Komu: <dmitry.torokhov@gmail.com>, <cernekee@gmail.com>, <dturvene@dahetral.com>
> Datum: 19.03.2014 09:57
> Předmět: [PATCH] input: add support for ALPS v7 protocol device
>
> CC: linux-input@vger.kernel.org, ndevos@redhat.com, jclift@redhat.com--cc, "Qiting Chen" <qiting.chen@cn.alps.com>
>Here is a patch of supporting ALPS v7 protocol device.
>ALPS v7 protocol device is a clickpad that is currently used on
>Lenovo S430/S435/S530, Lenovo Z410/Z510, HP Odie, HP Revolve 810 G1,
>as well as other machines with ALPS Touchpad of following infomation:
>	Device ID = 0x73, 0x03, 0x0a
>	Firmware ID = 0x88, 0xb*, 0x**
>
>A v7 protocol support patch is first relesed 2 months ago:
>http://www.spinics.net/lists/linux-input/msg29084.html
>After that some feedbacks were received from end user. Now this patch fixed the bugs
>reported by them:
>1) Fix cursor jump when doing a right click drag
>2) Fix cursor jitter when button clicking
>
>Signed-off-by: Qiting Chen <qiting.chen@cn.alps.com>
>---
> drivers/input/mouse/alps.c | 560 ++++++++++++++++++++++++++++++++++++++++++---
> drivers/input/mouse/alps.h | 132 +++++++++--
> 2 files changed, 641 insertions(+), 51 deletions(-)
>
>diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
>index fb15c64..383281f 100644
>--- a/drivers/input/mouse/alps.c
>+++ b/drivers/input/mouse/alps.c
>@@ -32,6 +32,13 @@
> #define ALPS_REG_BASE_RUSHMORE	0xc2c0
> #define ALPS_REG_BASE_PINNACLE	0x0000
> 
>+#define LEFT_BUTTON_BIT			0x01
>+#define RIGHT_BUTTON_BIT		0x02
>+
>+#define V7_LARGE_MOVEMENT		130
>+#define V7_DEAD_ZONE_OFFSET_X	72
>+#define V7_DEAD_ZONE_OFFSET_Y	72
>+
> static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
> 	{ PSMOUSE_CMD_SETPOLL,		0x00 }, /* 0 */
> 	{ PSMOUSE_CMD_RESET_DIS,	0x00 }, /* 1 */
>@@ -99,6 +106,7 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
> #define ALPS_FOUR_BUTTONS	0x40	/* 4 direction button present */
> #define ALPS_PS2_INTERLEAVED	0x80	/* 3-byte PS/2 packet interleaved with
> 					   6-byte ALPS packet */
>+#define ALPS_BTNLESS			0x100	/* ALPS ClickPad flag */
> 
> static const struct alps_model_info alps_model_data[] = {
> 	{ { 0x32, 0x02, 0x14 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },	/* Toshiba Salellite Pro M10 */
>@@ -140,6 +148,20 @@ static void alps_set_abs_params_mt(struct alps_data *priv,
>  * isn't valid per PS/2 spec.
>  */
> 
>+static unsigned int alps_pt_distance(struct alps_abs_data *pt0,
>+				    struct alps_abs_data *pt1)
>+{
>+	int vect_x, vect_y;
>+
>+	if (!pt0 || !pt1)
>+		return 0;
>+
>+	vect_x = pt0->x - pt1->x;
>+	vect_y = pt0->y - pt1->y;
>+
>+	return int_sqrt(vect_x * vect_x + vect_y * vect_y);
>+}
>+
> /* Packet formats are described in Documentation/input/alps.txt */
> 
> static bool alps_is_valid_first_byte(struct alps_data *priv,
>@@ -320,8 +342,8 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv,
> 		end_bit = y_msb - 1;
> 		box_middle_y = (priv->y_max * (start_bit + end_bit)) /
> 				(2 * (priv->y_bits - 1));
>-		*x1 = fields->x;
>-		*y1 = fields->y;
>+		*x1 = fields->pt.x;
>+		*y1 = fields->pt.y;
> 		*x2 = 2 * box_middle_x - *x1;
> 		*y2 = 2 * box_middle_y - *y1;
> 	}
>@@ -461,6 +483,38 @@ static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers,
> 	alps_set_slot(dev, 1, num_fingers == 2, x2, y2);
> }
> 
>+static void alps_report_coord_and_btn(struct psmouse *psmouse,
>+				      struct alps_fields *f)
>+{
>+	struct input_dev *dev;
>+
>+	if (!psmouse || !f)
>+		return;
>+
>+	dev = psmouse->dev;
>+
>+	if (f->fingers) {
>+		input_report_key(dev, BTN_TOUCH, 1);
>+		alps_report_semi_mt_data(dev, f->fingers,
>+			f->pt_img[0].x, f->pt_img[0].y,
>+			f->pt_img[1].x, f->pt_img[1].y);
>+		input_mt_report_finger_count(dev, f->fingers);
>+
>+		input_report_abs(dev, ABS_X, f->pt_img[0].x);
>+		input_report_abs(dev, ABS_Y, f->pt_img[0].y);
>+		input_report_abs(dev, ABS_PRESSURE, f->pt_img[0].z);
>+	} else {
>+		input_report_key(dev, BTN_TOUCH, 0);
>+		input_mt_report_finger_count(dev, 0);
>+		input_report_abs(dev, ABS_PRESSURE, 0);
>+	}
>+
>+	input_report_key(dev, BTN_LEFT, f->btn.left);
>+	input_report_key(dev, BTN_RIGHT, f->btn.right);
>+
>+	input_sync(dev);
>+}
>+
> static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
> {
> 	struct alps_data *priv = psmouse->private;
>@@ -523,13 +577,13 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
> 
> static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p)
> {
>-	f->left = !!(p[3] & 0x01);
>-	f->right = !!(p[3] & 0x02);
>-	f->middle = !!(p[3] & 0x04);
>+	f->btn.left = !!(p[3] & 0x01);
>+	f->btn.right = !!(p[3] & 0x02);
>+	f->btn.middle = !!(p[3] & 0x04);
> 
>-	f->ts_left = !!(p[3] & 0x10);
>-	f->ts_right = !!(p[3] & 0x20);
>-	f->ts_middle = !!(p[3] & 0x40);
>+	f->btn.ts_left = !!(p[3] & 0x10);
>+	f->btn.ts_right = !!(p[3] & 0x20);
>+	f->btn.ts_middle = !!(p[3] & 0x40);
> }
> 
> static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
>@@ -546,10 +600,10 @@ static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
> 		   ((p[2] & 0x7f) << 1) |
> 		   (p[4] & 0x01);
> 
>-	f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
>+	f->pt.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
> 	       ((p[0] & 0x30) >> 4);
>-	f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
>-	f->z = p[5] & 0x7f;
>+	f->pt.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
>+	f->pt.z = p[5] & 0x7f;
> 
> 	alps_decode_buttons_v3(f, p);
> }
>@@ -573,9 +627,9 @@ static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
> 	f->is_mp = !!(p[0] & 0x20);
> 
> 	if (!f->is_mp) {
>-		f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
>-		f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
>-		f->z = (p[0] & 4) ? 0 : p[5] & 0x7f;
>+		f->pt.x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
>+		f->pt.y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
>+		f->pt.z = (p[0] & 4) ? 0 : p[5] & 0x7f;
> 		alps_decode_buttons_v3(f, p);
> 	} else {
> 		f->fingers = ((p[0] & 0x6) >> 1 |
>@@ -687,7 +741,7 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
> 	 * with x, y, and z all zero, so these seem to be flukes.
> 	 * Ignore them.
> 	 */
>-	if (f.x && f.y && !f.z)
>+	if (f.pt.x && f.pt.y && !f.pt.z)
> 		return;
> 
> 	/*
>@@ -695,12 +749,12 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
> 	 * to rely on ST data.
> 	 */
> 	if (!fingers) {
>-		x1 = f.x;
>-		y1 = f.y;
>-		fingers = f.z > 0 ? 1 : 0;
>+		x1 = f.pt.x;
>+		y1 = f.pt.y;
>+		fingers = f.pt.z > 0 ? 1 : 0;
> 	}
> 
>-	if (f.z >= 64)
>+	if (f.pt.z >= 64)
> 		input_report_key(dev, BTN_TOUCH, 1);
> 	else
> 		input_report_key(dev, BTN_TOUCH, 0);
>@@ -709,22 +763,22 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
> 
> 	input_mt_report_finger_count(dev, fingers);
> 
>-	input_report_key(dev, BTN_LEFT, f.left);
>-	input_report_key(dev, BTN_RIGHT, f.right);
>-	input_report_key(dev, BTN_MIDDLE, f.middle);
>+	input_report_key(dev, BTN_LEFT, f.btn.left);
>+	input_report_key(dev, BTN_RIGHT, f.btn.right);
>+	input_report_key(dev, BTN_MIDDLE, f.btn.middle);
> 
>-	if (f.z > 0) {
>-		input_report_abs(dev, ABS_X, f.x);
>-		input_report_abs(dev, ABS_Y, f.y);
>+	if (f.pt.z > 0) {
>+		input_report_abs(dev, ABS_X, f.pt.x);
>+		input_report_abs(dev, ABS_Y, f.pt.y);
> 	}
>-	input_report_abs(dev, ABS_PRESSURE, f.z);
>+	input_report_abs(dev, ABS_PRESSURE, f.pt.z);
> 
> 	input_sync(dev);
> 
> 	if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
>-		input_report_key(dev2, BTN_LEFT, f.ts_left);
>-		input_report_key(dev2, BTN_RIGHT, f.ts_right);
>-		input_report_key(dev2, BTN_MIDDLE, f.ts_middle);
>+		input_report_key(dev2, BTN_LEFT, f.btn.ts_left);
>+		input_report_key(dev2, BTN_RIGHT, f.btn.ts_right);
>+		input_report_key(dev2, BTN_MIDDLE, f.btn.ts_middle);
> 		input_sync(dev2);
> 	}
> }
>@@ -916,6 +970,364 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
> 	input_sync(dev);
> }
> 
>+static bool alps_is_valid_package_v7(struct psmouse *psmouse)
>+{
>+	if ((psmouse->pktcnt == 3) && ((psmouse->packet[2] & 0x40) != 0x40))
>+		return false;
>+	if ((psmouse->pktcnt == 4) && ((psmouse->packet[3] & 0x48) != 0x48))
>+		return false;
>+	if ((psmouse->pktcnt == 6) && ((psmouse->packet[5] & 0x40) != 0x0))
>+		return false;
>+	return true;
>+}
>+
>+static int alps_drop_unsupported_packet_v7(struct psmouse *psmouse)
>+{
>+	struct alps_data *priv = psmouse->private;
>+	int drop = 1;
>+
>+	if (priv->r.v7.pkt_id == V7_PACKET_ID_NEW ||
>+	    priv->r.v7.pkt_id == V7_PACKET_ID_TWO ||
>+	    priv->r.v7.pkt_id == V7_PACKET_ID_MULTI ||
>+	    priv->r.v7.pkt_id == V7_PACKET_ID_IDLE)
>+		drop = 0;
>+
>+	return drop;
>+}
>+
>+static unsigned char alps_get_packet_id_v7(char *byte)
>+{
>+	unsigned char packet_id;
>+
>+	if (byte[4] & 0x40)
>+		packet_id = V7_PACKET_ID_TWO;
>+	else if (byte[4] & 0x01)
>+		packet_id = V7_PACKET_ID_MULTI;
>+	else if ((byte[0] & 0x10) && !(byte[4] & 0x43))
>+		packet_id = V7_PACKET_ID_NEW;
>+	else
>+		packet_id = V7_PACKET_ID_IDLE;
>+
>+	return packet_id;
>+}
>+
>+static void alps_get_finger_coordinate_v7(struct alps_abs_data *pt,
>+					  unsigned char *pkt,
>+					  unsigned char pkt_id)
>+{
>+	if ((pkt_id == V7_PACKET_ID_TWO) ||
>+	   (pkt_id == V7_PACKET_ID_MULTI) ||
>+	   (pkt_id == V7_PACKET_ID_NEW)) {
>+		pt[0].x = ((pkt[2] & 0x80) << 4);
>+		pt[0].x |= ((pkt[2] & 0x3F) << 5);
>+		pt[0].x |= ((pkt[3] & 0x30) >> 1);
>+		pt[0].x |= (pkt[3] & 0x07);
>+		pt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07);
>+
>+		pt[1].x = ((pkt[3] & 0x80) << 4);
>+		pt[1].x |= ((pkt[4] & 0x80) << 3);
>+		pt[1].x |= ((pkt[4] & 0x3F) << 4);
>+		pt[1].y = ((pkt[5] & 0x80) << 3);
>+		pt[1].y |= ((pkt[5] & 0x3F) << 4);
>+
>+		if (pkt_id == V7_PACKET_ID_TWO) {
>+			pt[1].x &= ~0x000F;
>+			pt[1].y |= 0x000F;
>+		} else if (pkt_id == V7_PACKET_ID_MULTI) {
>+			pt[1].x &= ~0x003F;
>+			pt[1].y &= ~0x0020;
>+			pt[1].y |= ((pkt[4] & 0x02) << 4);
>+			pt[1].y |= 0x001F;
>+		} else if (pkt_id == V7_PACKET_ID_NEW) {
>+			pt[1].x &= ~0x003F;
>+			pt[1].x |= (pkt[0] & 0x20);
>+			pt[1].y |= 0x000F;
>+		}
>+
>+		pt[0].y = 0x7FF - pt[0].y;
>+		pt[1].y = 0x7FF - pt[1].y;
>+
>+		pt[0].z = (pt[0].x && pt[0].y) ? 62 : 0;
>+		pt[1].z = (pt[1].x && pt[1].y) ? 62 : 0;
>+	}
>+}
>+
>+static void alps_decode_packet_v7(struct alps_fields *f,
>+				  unsigned char *p,
>+				  struct psmouse *psmouse)
>+{
>+	struct alps_data *priv = psmouse->private;
>+	static struct v7_raw prev_r;
>+
>+	priv->r.v7.pkt_id = alps_get_packet_id_v7(p);
>+
>+	alps_get_finger_coordinate_v7(f->pt_img, p, priv->r.v7.pkt_id);
>+
>+	priv->r.v7.rest_left = 0;
>+	priv->r.v7.rest_right = 0;
>+	priv->r.v7.additional_fingers = 0;
>+	priv->phy_btn = 0;
>+
>+	if (priv->r.v7.pkt_id == V7_PACKET_ID_TWO ||
>+	    priv->r.v7.pkt_id == V7_PACKET_ID_MULTI) {
>+		priv->r.v7.rest_left = (p[0] & 0x10) >> 4;
>+		priv->r.v7.rest_right = (p[0] & 0x20) >> 5;
>+	}
>+
>+	if (priv->r.v7.pkt_id == V7_PACKET_ID_MULTI)
>+		priv->r.v7.additional_fingers = p[5] & 0x03;
>+
>+	priv->phy_btn = (p[0] & 0x80) >> 7;
>+
>+	if (priv->r.v7.pkt_id == V7_PACKET_ID_TWO) {
>+		if (f->pt_img[0].z != 0 && f->pt_img[1].z != 0)
>+			priv->r.v7.raw_fn = 2;
>+		else
>+			priv->r.v7.raw_fn = 1;
>+	} else if (priv->r.v7.pkt_id == V7_PACKET_ID_MULTI)
>+		priv->r.v7.raw_fn = 3 + priv->r.v7.additional_fingers;
>+	else if (priv->r.v7.pkt_id == V7_PACKET_ID_IDLE)
>+		priv->r.v7.raw_fn = 0;
>+	else if (priv->r.v7.pkt_id == V7_PACKET_ID_NEW)
>+		priv->r.v7.raw_fn = prev_r.raw_fn;
>+
>+	/* It is a trick to bypass firmware bug of older version
>+	that 'New' Packet is missed when finger number changed.
>+	We fake a 'New' Packet in such cases.*/
>+	if (priv->r.v7.pkt_id == V7_PACKET_ID_TWO ||
>+		priv->r.v7.pkt_id == V7_PACKET_ID_MULTI ||
>+		priv->r.v7.pkt_id == V7_PACKET_ID_IDLE) {
>+		if (priv->r.v7.raw_fn != prev_r.raw_fn)
>+			priv->r.v7.pkt_id = V7_PACKET_ID_NEW;
>+	}
>+
>+	memcpy(&prev_r, &priv->r.v7, sizeof(struct v7_raw));
>+}
>+
>+static void alps_set_each_pt_attr_v7(struct psmouse *psmouse,
>+				     struct alps_abs_data *pt,
>+				     struct alps_bl_pt_attr *pt_attr)
>+{
>+	struct alps_data *priv = psmouse->private;
>+	unsigned int dist;
>+
>+	if (!pt_attr->is_init_pt_got && pt->z != 0) {
>+		pt_attr->is_init_pt_got = 1;
>+		pt_attr->is_counted = 0;
>+		memcpy(&pt_attr->init_pt, pt, sizeof(pt_attr->init_pt));
>+	}
>+
>+	if (pt->z != 0) {
>+		if (pt->y < priv->resting_zone_y_min) {
>+			/* A finger is recognized as a non-resting finger
>+			if it's position is outside the resting finger zone.*/
>+			pt_attr->zone = ZONE_NORMAL;
>+			pt_attr->is_counted = 1;
>+		} else {
>+			/* A finger is recognized as a resting finger if it's
>+			position is inside the resting finger zone and there's
>+			no large movement from it's touch down position.*/
>+			pt_attr->zone = ZONE_RESTING;
>+
>+			if (pt->x > priv->x_max / 2)
>+				pt_attr->zone |= ZONE_RIGHT_BTN;
>+			else
>+				pt_attr->zone |= ZONE_LEFT_BTN;
>+
>+			/* A resting finger will turn to be a non-resting
>+			finger if it has made large movement from it's touch
>+			down position. A non-resting finger will never turn
>+			to a resting finger before it leaves the touchpad
>+			surface.*/
>+			if (pt_attr->is_init_pt_got) {
>+				dist = alps_pt_distance(pt, &pt_attr->init_pt);
>+
>+				if (dist > V7_LARGE_MOVEMENT)
>+					pt_attr->is_counted = 1;
>+			}
>+		}
>+	}
>+}
>+
>+static void alps_set_pt_attr_v7(struct psmouse *psmouse,
>+				       struct alps_fields *f)
>+{
>+	struct alps_data *priv = psmouse->private;
>+	int i;
>+
>+	switch (priv->r.v7.pkt_id) {
>+	case  V7_PACKET_ID_TWO:
>+	case  V7_PACKET_ID_MULTI:
>+		for (i = 0; i < V7_IMG_PT_NUM; i++) {
>+			alps_set_each_pt_attr_v7(psmouse,
>+						 &f->pt_img[i],
>+						 &priv->pt_attr[i]);
>+		}
>+		break;
>+	default:
>+		/*All finger attributes are cleared when packet ID is
>+		'IDLE', 'New'or other unknown IDs. An 'IDLE' packet
>+		indicates that there's no finger and no button activity.
>+		A 'NEW' packet indicates the finger position in packet
>+		is not continues from previous packet. Such as the
>+		condition there's finger placed or lifted. In these cases,
>+		finger attributes will be reset.*/
>+		memset(priv->pt_attr, 0, sizeof(priv->pt_attr[0]) * 2);
>+		break;
>+	}
>+}
>+
>+static void alps_cal_output_finger_num_v7(struct psmouse *psmouse,
>+					struct alps_fields *f)
>+{
>+	struct alps_data *priv = psmouse->private;
>+	unsigned int fn = 0;
>+	int i;
>+
>+	switch (priv->r.v7.pkt_id) {
>+	case V7_PACKET_ID_IDLE:
>+	case V7_PACKET_ID_NEW:
>+		/*No finger is reported when packet ID is 'IDLE' or 'New'.
>+		An 'IDLE' packet indicates that there's no finger on touchpad.
>+		A 'NEW' packet indicates there's finger placed or lifted.
>+		Finger position of 'New' packet is not continues from the
>+		previous packet.*/
>+		fn = 0;
>+		break;
>+	case V7_PACKET_ID_TWO:
>+		if (f->pt_img[0].z == 0) {
>+			/*The first finger slot is zero when a non-resting
>+			finger lifted and remaining only one resting finger
>+			on touchpad. Hardware report the remaining resting
>+			finger in second slot. This resting is ignored*/
>+			fn = 0;
>+		} else if (f->pt_img[1].z == 0) {
>+			/* The second finger slot is zero if there's
>+			only one finger*/
>+			fn = 1;
>+		} else {
>+			/*All non-resting fingers will be counted to report*/
>+			fn = 0;
>+			for (i = 0; i < V7_IMG_PT_NUM; i++) {
>+				if (priv->pt_attr[i].is_counted)
>+					fn++;
>+			}
>+
>+			/*In the case that both fingers are
>+			resting fingers, report the first one*/
>+			if (!priv->pt_attr[0].is_counted &&
>+			    !priv->pt_attr[1].is_counted) {
>+				fn = 1;
>+			}
>+		}
>+		break;
>+	case V7_PACKET_ID_MULTI:
>+		/*A packet ID 'MULTI' indicats that at least 3 non-resting
>+		finger exist.*/
>+		fn = 3 + priv->r.v7.additional_fingers;
>+		break;
>+	}
>+
>+	f->fingers = fn;
>+}
>+
>+static void alps_button_dead_zone_filter(struct psmouse *psmouse,
>+				   struct alps_fields *f,
>+				   struct alps_fields *prev_f)
>+{
>+	struct alps_data *priv = psmouse->private;
>+	int dx, dy;
>+
>+	if (priv->prev_phy_btn == 0 && priv->phy_btn != 0) {
>+		memcpy(&priv->pt_attr[0].init_dead_pt,
>+				&f->pt_img[0],
>+				sizeof(struct alps_abs_data));
>+	}
>+
>+	if (priv->pt_attr[0].init_dead_pt.x != 0 &&
>+		priv->pt_attr[0].init_dead_pt.x != 0) {
>+			dx = f->pt_img[0].x - priv->pt_attr[0].init_dead_pt.x;
>+			dy = f->pt_img[0].y - priv->pt_attr[0].init_dead_pt.y;
>+		if ((abs(dx) > V7_DEAD_ZONE_OFFSET_X) ||
>+			(abs(dy) > V7_DEAD_ZONE_OFFSET_Y)) {
>+				memset(&priv->pt_attr[0].init_dead_pt, 0,
>+						sizeof(struct alps_abs_data));
>+				priv->btn_delay_cnt = 0;
>+		} else {
>+			memcpy(&f->pt_img[0],
>+					&prev_f->pt_img[0],
>+					sizeof(struct alps_abs_data));
>+			if (priv->prev_phy_btn == 0 && priv->phy_btn != 0)
>+				priv->btn_delay_cnt = 2;
>+		}
>+	}
>+
>+	if (priv->btn_delay_cnt > 0) {
>+		f->btn.left = 0;
>+		f->btn.right = 0;
>+		priv->btn_delay_cnt--;
>+	}
>+}
>+
>+static void alps_assign_buttons_v7(struct psmouse *psmouse,
>+				   struct alps_fields *f,
>+				   struct alps_fields *prev_f)
>+{
>+	struct alps_data *priv = psmouse->private;
>+
>+	if (priv->phy_btn) {
>+		if (!priv->prev_phy_btn) {
>+			/* Report a right click as long as there's finger on
>+			right button zone. Othrewise, report a left click.*/
>+			if (priv->r.v7.rest_right ||
>+			    priv->pt_attr[0].zone & ZONE_RIGHT_BTN ||
>+			    priv->pt_attr[1].zone & ZONE_RIGHT_BTN) {
>+				f->btn.right = 1;
>+				priv->pressed_btn_bits |= RIGHT_BUTTON_BIT;
>+			} else {
>+				f->btn.left = 1;
>+				priv->pressed_btn_bits |= LEFT_BUTTON_BIT;
>+			}
>+		} else {
>+			if (priv->pressed_btn_bits & RIGHT_BUTTON_BIT)
>+				f->btn.right = 1;
>+			if (priv->pressed_btn_bits & LEFT_BUTTON_BIT)
>+				f->btn.left = 1;
>+		}
>+	} else {
>+		priv->pressed_btn_bits = 0;
>+		f->btn.right = 0;
>+		f->btn.left = 0;
>+	}
>+
>+	alps_button_dead_zone_filter(psmouse, f, prev_f);
>+
>+	priv->prev_phy_btn = priv->phy_btn;
>+}
>+
>+static void alps_process_packet_v7(struct psmouse *psmouse)
>+{
>+	struct alps_data *priv = psmouse->private;
>+	struct alps_fields f = {0};
>+	static struct alps_fields prev_f;
>+	unsigned char *packet = psmouse->packet;
>+
>+	priv->decode_fields(&f, packet, psmouse);
>+
>+	if (alps_drop_unsupported_packet_v7(psmouse))
>+		return;
>+
>+	alps_set_pt_attr_v7(psmouse, &f);
>+
>+	alps_cal_output_finger_num_v7(psmouse, &f);
>+
>+	alps_assign_buttons_v7(psmouse, &f, &prev_f);
>+
>+	alps_report_coord_and_btn(psmouse, &f);
>+
>+	memcpy(&prev_f, &f, sizeof(struct alps_fields));
>+}
>+
> static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
> 					unsigned char packet[],
> 					bool report_buttons)
>@@ -1080,6 +1492,14 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
> 		return PSMOUSE_BAD_DATA;
> 	}
> 
>+	if ((priv->proto_version == ALPS_PROTO_V7 &&
>+	    !alps_is_valid_package_v7(psmouse))) {
>+		psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
>+			    psmouse->pktcnt - 1,
>+			    psmouse->packet[psmouse->pktcnt - 1]);
>+		return PSMOUSE_BAD_DATA;
>+	}
>+
> 	if (psmouse->pktcnt == psmouse->pktsize) {
> 		priv->process_packet(psmouse);
> 		return PSMOUSE_FULL_PACKET;
>@@ -1192,6 +1612,22 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command,
> 	return 0;
> }
> 
>+static int alps_check_valid_firmware_id(unsigned char id[])
>+{
>+	int valid = 1;
>+
>+	if (id[0] == 0x73)
>+		valid = 1;
>+	else if (id[0] == 0x88) {
>+		if ((id[1] == 0x07) ||
>+		    (id[1] == 0x08) ||
>+		    ((id[1] & 0xf0) == 0xB0))
>+			valid = 1;
>+	}
>+
>+	return valid;
>+}
>+
> static int alps_enter_command_mode(struct psmouse *psmouse)
> {
> 	unsigned char param[4];
>@@ -1201,8 +1637,7 @@ static int alps_enter_command_mode(struct psmouse *psmouse)
> 		return -1;
> 	}
> 
>-	if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) &&
>-	    param[0] != 0x73) {
>+	if (!alps_check_valid_firmware_id(param)) {
> 		psmouse_dbg(psmouse,
> 			    "unknown response while entering command mode\n");
> 		return -1;
>@@ -1704,6 +2139,36 @@ error:
> 	return ret;
> }
> 
>+static int alps_hw_init_v7(struct psmouse *psmouse)
>+{
>+	struct ps2dev *ps2dev = &psmouse->ps2dev;
>+	int reg_val, ret = -1;
>+
>+	if (alps_enter_command_mode(psmouse))
>+		goto error;
>+
>+	reg_val = alps_command_mode_read_reg(psmouse, 0xc2d9);
>+	if (reg_val == -1)
>+		goto error;
>+
>+	if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
>+		goto error;
>+
>+	reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4);
>+	if (reg_val == -1)
>+		goto error;
>+
>+	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02))
>+		goto error;
>+
>+	alps_exit_command_mode(psmouse);
>+	return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
>+
>+error:
>+	alps_exit_command_mode(psmouse);
>+	return ret;
>+}
>+
> /* Must be in command mode when calling this function */
> static int alps_absolute_mode_v4(struct psmouse *psmouse)
> {
>@@ -1875,6 +2340,7 @@ static void alps_set_defaults(struct alps_data *priv)
> 		priv->set_abs_params = alps_set_abs_params_st;
> 		priv->x_max = 1023;
> 		priv->y_max = 767;
>+		priv->slot_number = 1;
> 		break;
> 	case ALPS_PROTO_V3:
> 		priv->hw_init = alps_hw_init_v3;
>@@ -1883,6 +2349,7 @@ static void alps_set_defaults(struct alps_data *priv)
> 		priv->decode_fields = alps_decode_pinnacle;
> 		priv->nibble_commands = alps_v3_nibble_commands;
> 		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
>+		priv->slot_number = 2;
> 		break;
> 	case ALPS_PROTO_V4:
> 		priv->hw_init = alps_hw_init_v4;
>@@ -1890,6 +2357,7 @@ static void alps_set_defaults(struct alps_data *priv)
> 		priv->set_abs_params = alps_set_abs_params_mt;
> 		priv->nibble_commands = alps_v4_nibble_commands;
> 		priv->addr_command = PSMOUSE_CMD_DISABLE;
>+		priv->slot_number = 2;
> 		break;
> 	case ALPS_PROTO_V5:
> 		priv->hw_init = alps_hw_init_dolphin_v1;
>@@ -1905,6 +2373,7 @@ static void alps_set_defaults(struct alps_data *priv)
> 		priv->y_max = 660;
> 		priv->x_bits = 23;
> 		priv->y_bits = 12;
>+		priv->slot_number = 2;
> 		break;
> 	case ALPS_PROTO_V6:
> 		priv->hw_init = alps_hw_init_v6;
>@@ -1913,6 +2382,28 @@ static void alps_set_defaults(struct alps_data *priv)
> 		priv->nibble_commands = alps_v6_nibble_commands;
> 		priv->x_max = 2047;
> 		priv->y_max = 1535;
>+		priv->slot_number = 2;
>+		break;
>+	case ALPS_PROTO_V7:
>+		priv->hw_init = alps_hw_init_v7;
>+		priv->process_packet = alps_process_packet_v7;
>+		priv->decode_fields = alps_decode_packet_v7;
>+		priv->set_abs_params = alps_set_abs_params_mt;
>+		priv->nibble_commands = alps_v3_nibble_commands;
>+		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
>+		priv->x_max = 0xfff;
>+		priv->y_max = 0x7ff;
>+		priv->resting_zone_y_min = 0x654;
>+		priv->byte0 = 0x48;
>+		priv->mask0 = 0x48;
>+		priv->flags = 0;
>+		priv->slot_number = 2;
>+
>+		priv->phy_btn = 0;
>+		priv->prev_phy_btn = 0;
>+		priv->btn_delay_cnt = 0;
>+		priv->pressed_btn_bits = 0;
>+		memset(priv->pt_attr, 0, sizeof(priv->pt_attr[0]) * 2);
> 		break;
> 	}
> }
>@@ -1982,6 +2473,11 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
> 			return -EIO;
> 		else
> 			return 0;
>+	} else if (ec[0] == 0x88 && (ec[1] & 0xf0) == 0xB0) {
>+		priv->proto_version = ALPS_PROTO_V7;
>+		alps_set_defaults(priv);
>+
>+		return 0;
> 	} else if (ec[0] == 0x88 && ec[1] == 0x08) {
> 		priv->proto_version = ALPS_PROTO_V3;
> 		alps_set_defaults(priv);
>@@ -2045,7 +2541,7 @@ static void alps_set_abs_params_mt(struct alps_data *priv,
> 				   struct input_dev *dev1)
> {
> 	set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
>-	input_mt_init_slots(dev1, 2, 0);
>+	input_mt_init_slots(dev1, priv->slot_number, 0);
> 	input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
> 	input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
> 
>diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
>index 03f88b6..dedbd27 100644
>--- a/drivers/input/mouse/alps.h
>+++ b/drivers/input/mouse/alps.h
>@@ -18,11 +18,36 @@
> #define ALPS_PROTO_V4	4
> #define ALPS_PROTO_V5	5
> #define ALPS_PROTO_V6	6
>+#define ALPS_PROTO_V7	7
>+
>+#define MAX_IMG_PT_NUM		5
>+#define V7_IMG_PT_NUM		2
>+
>+#define ZONE_NORMAL				0x01
>+#define ZONE_RESTING			0x02
>+#define ZONE_LEFT_BTN			0x04
>+#define ZONE_RIGHT_BTN			0x08
> 
> #define DOLPHIN_COUNT_PER_ELECTRODE	64
> #define DOLPHIN_PROFILE_XOFFSET		8	/* x-electrode offset */
> #define DOLPHIN_PROFILE_YOFFSET		1	/* y-electrode offset */
> 
>+/*
>+ * enum V7_PACKET_ID - defines the packet type for V7
>+ * V7_PACKET_ID_IDLE: There's no finger and no button activity.
>+ * V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad
>+ *  or there's button activities.
>+ * V7_PACKET_ID_MULTI: There are at least three non-resting fingers.
>+ * V7_PACKET_ID_NEW: The finger position in slot is not continues from
>+ *  previous packet.
>+*/
>+enum V7_PACKET_ID {
>+	 V7_PACKET_ID_IDLE,
>+	 V7_PACKET_ID_TWO,
>+	 V7_PACKET_ID_MULTI,
>+	 V7_PACKET_ID_NEW,
>+};
>+
> /**
>  * struct alps_model_info - touchpad ID table
>  * @signature: E7 response string to match.
>@@ -66,15 +91,7 @@ struct alps_nibble_commands {
> };
> 
> /**
>- * struct alps_fields - decoded version of the report packet
>- * @x_map: Bitmap of active X positions for MT.
>- * @y_map: Bitmap of active Y positions for MT.
>- * @fingers: Number of fingers for MT.
>- * @x: X position for ST.
>- * @y: Y position for ST.
>- * @z: Z position for ST.
>- * @first_mp: Packet is the first of a multi-packet report.
>- * @is_mp: Packet is part of a multi-packet report.
>+ * struct alps_btn - decoded version of the button status
>  * @left: Left touchpad button is active.
>  * @right: Right touchpad button is active.
>  * @middle: Middle touchpad button is active.
>@@ -82,16 +99,7 @@ struct alps_nibble_commands {
>  * @ts_right: Right trackstick button is active.
>  * @ts_middle: Middle trackstick button is active.
>  */
>-struct alps_fields {
>-	unsigned int x_map;
>-	unsigned int y_map;
>-	unsigned int fingers;
>-	unsigned int x;
>-	unsigned int y;
>-	unsigned int z;
>-	unsigned int first_mp:1;
>-	unsigned int is_mp:1;
>-
>+struct alps_btn {
> 	unsigned int left:1;
> 	unsigned int right:1;
> 	unsigned int middle:1;
>@@ -102,6 +110,73 @@ struct alps_fields {
> };
> 
> /**
>+ * struct alps_btn - decoded version of the X Y Z postion for ST.
>+ * @x: X position for ST.
>+ * @y: Y position for ST.
>+ * @z: Z position for ST.
>+ */
>+struct alps_abs_data {
>+	unsigned int x;
>+	unsigned int y;
>+	unsigned int z;
>+};
>+
>+/**
>+ * struct alps_fields - decoded version of the report packet
>+ * @fingers: Number of fingers for MT.
>+ * @pt: X Y Z postion for ST.
>+ * @pt: X Y Z postion for image MT.
>+ * @x_map: Bitmap of active X positions for MT.
>+ * @y_map: Bitmap of active Y positions for MT.
>+ * @first_mp: Packet is the first of a multi-packet report.
>+ * @is_mp: Packet is part of a multi-packet report.
>+ * @btn: Button activity status
>+ */
>+struct alps_fields {
>+	unsigned int fingers;
>+	struct alps_abs_data pt;
>+	struct alps_abs_data pt_img[MAX_IMG_PT_NUM];
>+	unsigned int x_map;
>+	unsigned int y_map;
>+	unsigned int first_mp:1;
>+	unsigned int is_mp:1;
>+	struct alps_btn btn;
>+};
>+
>+/**
>+ * struct v7_raw - data decoded from raw packet for V7.
>+ * @pkt_id: An id that specifies the type of packet.
>+ * @additional_fingers: Number of additional finger that is neighter included
>+ *  in pt slot nor reflected in rest_left and rest_right flag of data packet.
>+ * @rest_left: There are fingers on left resting zone.
>+ * @rest_right: There are fingers on right resting zone.
>+ * @raw_fn: The number of finger on touchpad.
>+ */
>+struct v7_raw {
>+	unsigned char pkt_id;
>+	unsigned int additional_fingers;
>+	unsigned char rest_left;
>+	unsigned char rest_right;
>+	unsigned char raw_fn;
>+};
>+
>+/**
>+ * struct alps_bl_pt_attr - generic attributes of touch points for buttonless device
>+ * @zone: The part of touchpad that the touch point locates
>+ * @is_counted: The touch point is not a resting finger.
>+ * @is_init_pt_got: The touch down point is got.
>+ * @init_pt: The X Y Z position of the touch down point.
>+ * @init_dead_pt: The touch down point of a finger used by dead zone process.
>+ */
>+struct alps_bl_pt_attr {
>+	unsigned char zone;
>+	unsigned char is_counted;
>+	unsigned char is_init_pt_got;
>+	struct alps_abs_data init_pt;
>+	struct alps_abs_data init_dead_pt;
>+};
>+
>+/**
>  * struct alps_data - private data structure for the ALPS driver
>  * @dev2: "Relative" device used to report trackstick or mouse activity.
>  * @phys: Physical path for the relative device.
>@@ -116,8 +191,10 @@ struct alps_fields {
>  * @flags: Additional device capabilities (passthrough port, trackstick, etc.).
>  * @x_max: Largest possible X position value.
>  * @y_max: Largest possible Y position value.
>+ * @resting_zone_y_min: Smallest Y postion value of the bottom resting zone.
>  * @x_bits: Number of X bits in the MT bitmap.
>  * @y_bits: Number of Y bits in the MT bitmap.
>+ * @img_fingers: Number of image fingers.
>  * @hw_init: Protocol-specific hardware init function.
>  * @process_packet: Protocol-specific function to process a report packet.
>  * @decode_fields: Protocol-specific function to read packet bitfields.
>@@ -132,6 +209,11 @@ struct alps_fields {
>  * @fingers: Number of fingers from last MT report.
>  * @quirks: Bitmap of ALPS_QUIRK_*.
>  * @timer: Timer for flushing out the final report packet in the stream.
>+ * @v7: Data decoded from raw packet for V7
>+ * @phy_btn: Physical button is active.
>+ * @prev_phy_btn: Physical button of previous packet is active.
>+ * @pressed_btn_bits: Pressed positon of button zone
>+ * @pt_attr: Generic attributes of touch points for buttonless device.
>  */
> struct alps_data {
> 	struct input_dev *dev2;
>@@ -145,8 +227,10 @@ struct alps_data {
> 	unsigned char flags;
> 	int x_max;
> 	int y_max;
>+	int resting_zone_y_min;
> 	int x_bits;
> 	int y_bits;
>+	unsigned char slot_number;
> 
> 	int (*hw_init)(struct psmouse *psmouse);
> 	void (*process_packet)(struct psmouse *psmouse);
>@@ -161,6 +245,16 @@ struct alps_data {
> 	int fingers;
> 	u8 quirks;
> 	struct timer_list timer;
>+
>+	/* these are used for buttonless touchpad*/
>+	union {
>+		struct v7_raw v7;
>+	} r;
>+	unsigned char phy_btn;
>+	unsigned char prev_phy_btn;
>+	unsigned char btn_delay_cnt;
>+	unsigned char pressed_btn_bits;
>+	struct alps_bl_pt_attr pt_attr[MAX_IMG_PT_NUM];
> };
> 
> #define ALPS_QUIRK_TRACKSTICK_BUTTONS	1 /* trakcstick buttons in trackstick packet */
>-- 
>1.8.3.2
>
>--
>To unsubscribe from this list: send the line "unsubscribe linux-input" in
>the body of a message to majordomo@vger.kernel.org
>More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 00/15] iio: adc: at91 cleanups and atmel_tsadcc removal
From: Nicolas Ferre @ 2014-03-19 10:19 UTC (permalink / raw)
  To: Jonathan Cameron, Alexandre Belloni, Dmitry Torokhov,
	ARM Maintainers
  Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA, Gregory Clement,
	Maxime Ripard, Jean-Christophe Plagniol-Villard,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <5325EDAF.4060003-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

On 16/03/2014 19:30, Jonathan Cameron :
> On 05/03/14 17:35, Alexandre Belloni wrote:
>> This patch set is a followup of my previous series: iio: adc: at91 fixes
>>
>> I'm sorry it is so long, I made sure this is bissectable.
> It's a nice series, don't apologise for doing things right!
> 
> Clearly there are a few outstanding snippets from the reviews so far, but we
> are getting to the point where we need to agree on a path for this.  I'm
> happy to take the whole series through the IIO tree if Dmitry and the at91
> maintainers are happy with that.  If it is considered two invasive form the
> atmel side then I'm perfectly happy with someone to take it through the relevant
> platform tree instead.

Jonathan, Dmitry,

Considering the amount of AT91 files touched by this series, I would
like to take everything through Atmel's git tree and later on arm-soc.


> I'm obviously not going to pick it up at all until it has a complete set
> of at91 maintainer acks though!
> 
> Realistically it's missed the coming merge window anyway, so we have a fair
> bit of time.

Yes, I will review Alexandre's v2 series.

Thanks a lot for your review and for having thought about the path that
these patches would take.

Bye,


> Jonathan
>>
>> 1-3) The first 3 patches are cleaning up the patform_data used for at91_adc.
>>
>> 4-5) Then touchscreen support for older ADCs is added, this allows to use that
>>       for the sam9m10g45ek.
>>
>> 6) Following those modifications, the mach/at91_adc.h is not used anywhere but
>>     in the at91_adc driver so it is remove and its content (register definitions)
>>     placed directly in the driver.
>>
>> 7-9) at91sam9rl support is added to at91_adc and is used for the at91sam9rl
>>       based boards.
>>
>> 10-11) Prepare the atmel_tsadcc removal by switching sam9rl and sam9g45 to use
>>         only at91_adc instead of atmel_tsadcc.
>>
>> 12-15) atmel_tsadcc removal
>>
>>
>> Alexandre Belloni (15):
>>    ARM: at91: sam9g45: remove unused platform_data
>>    ARM: at91: sam9260: remove unused platform_data
>>    iio: adc: at91: cleanup platform_data
>>    iio: adc: at91_adc: Add support for touchscreens without TSMR
>>    ARM: at91: sam9m10g45ek: Add touchscreen support through at91_adc
>>    iio: adc: at91: remove unused include from include/mach
>>    iio: adc: at91: add sam9rl support
>>    ARM: at91: sam9rl: add at91_adc to support adc and touchscreen
>>    ARM: at91: sam9rlek add touchscreen support through at91_adc
>>    ARM: at91: sam9g45: switch from atmel_tsadcc to at91_adc
>>    ARM: at91: sam9rl: switch from atmel_tsadcc to at91_adc
>>    ARM: at91: remove atmel_tsadcc platform_data
>>    ARM: at91: remove atmel_tsadcc from sama5_defconfig
>>    Input: atmel_tsadcc: remove driver
>>    ARM: at91/dt: at91-cosino_mega2560 remove useless tsadcc node
>>
>>   MAINTAINERS                                |   6 -
>>   arch/arm/boot/dts/at91-cosino_mega2560.dts |   5 -
>>   arch/arm/configs/at91sam9g45_defconfig     |   3 +-
>>   arch/arm/configs/at91sam9rl_defconfig      |   3 +-
>>   arch/arm/configs/sama5_defconfig           |   1 -
>>   arch/arm/mach-at91/at91sam9260_devices.c   |  10 -
>>   arch/arm/mach-at91/at91sam9g45.c           |   2 +-
>>   arch/arm/mach-at91/at91sam9g45_devices.c   |  63 +----
>>   arch/arm/mach-at91/at91sam9rl.c            |   7 +
>>   arch/arm/mach-at91/at91sam9rl_devices.c    |  83 +++++--
>>   arch/arm/mach-at91/board-sam9m10g45ek.c    |  16 +-
>>   arch/arm/mach-at91/board-sam9rlek.c        |  16 +-
>>   arch/arm/mach-at91/board.h                 |   3 -
>>   arch/arm/mach-at91/include/mach/at91_adc.h | 107 ---------
>>   drivers/iio/adc/at91_adc.c                 | 339 +++++++++++++++++++++++----
>>   drivers/input/touchscreen/Kconfig          |  12 -
>>   drivers/input/touchscreen/Makefile         |   1 -
>>   drivers/input/touchscreen/atmel_tsadcc.c   | 358 -----------------------------
>>   include/linux/platform_data/at91_adc.h     |  27 +--
>>   include/linux/platform_data/atmel.h        |   7 -
>>   20 files changed, 380 insertions(+), 689 deletions(-)
>>   delete mode 100644 arch/arm/mach-at91/include/mach/at91_adc.h
>>   delete mode 100644 drivers/input/touchscreen/atmel_tsadcc.c
>>
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 


-- 
Nicolas Ferre

^ permalink raw reply

* Re: [PATCH v2 4/8] Input: pixcir_i2c_ts: Use Type-B Multi-Touch protocol
From: Roger Quadros @ 2014-03-19 11:27 UTC (permalink / raw)
  To: Henrik Rydberg
  Cc: balbi, dmitry.torokhov, jcbian, dmurphy, mugunthanvnm,
	linux-input, linux-kernel, devicetree
In-Reply-To: <20140310163701.GA30397@saruman.home>

Henrik,

On 03/10/2014 06:37 PM, Felipe Balbi wrote:
> On Mon, Mar 10, 2014 at 10:57:10AM +0200, Roger Quadros wrote:
>> Hi Henrik,
>>
>> On 03/08/2014 05:11 PM, Henrik Rydberg wrote:
>>> Hi Roger,
>>>
>>> the MT implementation seems mostly fine, just one curiosity:
>>>
>>>>  static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
>>>>  {
>>>>  	struct pixcir_i2c_ts_data *tsdata = dev_id;
>>>>  	const struct pixcir_ts_platform_data *pdata = tsdata->chip;
>>>> +	struct pixcir_report_data report;
>>>>  
>>>>  	while (!tsdata->exiting) {
>>>> -		pixcir_ts_poscheck(tsdata);
>>>> -
>>>> -		if (gpio_get_value(pdata->gpio_attb))
>>>> +		/* parse packet */
>>>> +		pixcir_ts_parse(tsdata, &report);
>>>> +
>>>> +		/* report it */
>>>> +		pixcir_ts_report(tsdata, &report);
>>>> +
>>>> +		if (gpio_get_value(pdata->gpio_attb)) {
>>>> +			if (report.num_touches) {
>>>> +				/*
>>>> +				 * Last report with no finger up?
>>>> +				 * Do it now then.
>>>> +				 */
>>>> +				input_mt_sync_frame(tsdata->input);
>>>> +				input_sync(tsdata->input);
>>>
>>> Why is this special handling needed?
>>
>> This is needed because the controller doesn't always report when all fingers
>> have left the screen. e.g. report might contain 3 fingers touched and then
>> gpio_attb line is de-asserted. There's no report with 0 fingers touched even
>> if the user's fingers have left the screen. So we never detect a BUTTON_UP.
>>
>> Without this s/w workaround we observe side effects like buttons being pressed
>> but not released. To me it looks like a bug in the controller.
> 
> the other way would be to *also* use IRQF_TRIGGER_RISING, then you get
> an IRQ when fingers leave the screen. No ?
> 

If you are OK with my explanation and the patches, could you please Ack them? Thanks.

cheers,
-roger

^ permalink raw reply

* Input: edt-ft5x06: Add DT support
From: Lothar Waßmann @ 2014-03-19 13:09 UTC (permalink / raw)
  To: Dmitry Torokhov, Fugang Duan, Grant Likely, Henrik Rydberg,
	Ian Campbell, Jingoo Han, Kumar Gala, Mark Rutland, Pawel Moll,
	Rob Herring, Rob Landley, Sachin Kamat, devicetree, linux-doc,
	linux-input, linux-kernel, Simon Budig, Lothar Waßmann

Changes wrt. v1:
addressed the comments from Jingoo Han and Mark Rutland
- added another patch to convert the driver to use devm_* functions
- removed sysfs reference from bindings documentation
- changed '_' to '-' in property name
- added 'edt,' prefix to properties names
- added sanity check for parameters read from DT
- cleaned up the gpio handling code

Changes wrt. v2:
- fixed the devm_* messup reported by Dmitry Torokhov
- added unit for report-rate property to the binding doc
- added separate patch to fix the reset delays

Changes wrt: v3:
- removed patches that have already been applied in the mean time
- ignore touchdown events, since those may report bad coordinates
- added support for a new firmware version

^ permalink raw reply

* [PATCHv4 1/5] Input: edt-ft5x06: several cleanups; no functional change
From: Lothar Waßmann @ 2014-03-19 13:09 UTC (permalink / raw)
  To: Dmitry Torokhov, Fugang Duan, Grant Likely, Henrik Rydberg,
	Ian Campbell, Jingoo Han, Kumar Gala, Mark Rutland, Pawel Moll,
	Rob Herring, Rob Landley, Sachin Kamat, devicetree, linux-doc,
	linux-input, linux-kernel, Simon Budig, Lothar Waßmann
In-Reply-To: <1395234563-11034-1-git-send-email-LW@KARO-electronics.de>

- remove redundant parens
- remove redundant type casts
- fix mixed tab/space indentation

Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 drivers/input/touchscreen/edt-ft5x06.c |   12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index 412a85e..7b4470d 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -173,7 +173,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
 		x = ((buf[0] << 8) | buf[1]) & 0x0fff;
 		y = ((buf[2] << 8) | buf[3]) & 0x0fff;
 		id = (buf[2] >> 4) & 0x0f;
-		down = (type != TOUCH_EVENT_UP);
+		down = type != TOUCH_EVENT_UP;
 
 		input_mt_slot(tsdata->input, id);
 		input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down);
@@ -257,7 +257,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
 	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
 	struct edt_ft5x06_attribute *attr =
 			container_of(dattr, struct edt_ft5x06_attribute, dattr);
-	u8 *field = (u8 *)((char *)tsdata + attr->field_offset);
+	u8 *field = (u8 *)tsdata + attr->field_offset;
 	int val;
 	size_t count = 0;
 	int error = 0;
@@ -299,7 +299,7 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
 	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
 	struct edt_ft5x06_attribute *attr =
 			container_of(dattr, struct edt_ft5x06_attribute, dattr);
-	u8 *field = (u8 *)((char *)tsdata + attr->field_offset);
+	u8 *field = (u8 *)tsdata + attr->field_offset;
 	unsigned int val;
 	int error;
 
@@ -479,7 +479,7 @@ static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode)
 
 	if (mode != tsdata->factory_mode) {
 		retval = mode ? edt_ft5x06_factory_mode(tsdata) :
-			        edt_ft5x06_work_mode(tsdata);
+				edt_ft5x06_work_mode(tsdata);
 	}
 
 	mutex_unlock(&tsdata->mutex);
@@ -852,8 +852,8 @@ static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
 			 edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume);
 
 static const struct i2c_device_id edt_ft5x06_ts_id[] = {
-	{ "edt-ft5x06", 0 },
-	{ }
+	{ "edt-ft5x06", 0, },
+	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
 
-- 
1.7.10.4

^ permalink raw reply related

* [PATCHv4 2/5] Input: edt-ft5x06: Add DT support
From: Lothar Waßmann @ 2014-03-19 13:09 UTC (permalink / raw)
  To: Dmitry Torokhov, Fugang Duan, Grant Likely, Henrik Rydberg,
	Ian Campbell, Jingoo Han, Kumar Gala, Mark Rutland, Pawel Moll,
	Rob Herring, Rob Landley, Sachin Kamat, devicetree, linux-doc,
	linux-input, linux-kernel, Simon Budig, Lothar Waßmann
In-Reply-To: <1395234563-11034-1-git-send-email-LW@KARO-electronics.de>


Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 .../bindings/input/touchscreen/edt-ft5x06.txt      |   41 ++++++
 drivers/input/touchscreen/edt-ft5x06.c             |  144 +++++++++++++++-----
 2 files changed, 154 insertions(+), 31 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt

diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
new file mode 100644
index 0000000..e5adc76
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
@@ -0,0 +1,41 @@
+FocalTech EDT-FT5x06 Polytouch driver
+=====================================
+
+Required properties:
+ - compatible:  "edt,edt-ft5x06"
+ - reg:         I2C slave address of the chip (0x38)
+ - interrupt-parent: a phandle pointing to the interrupt controller
+                     serving the interrupt for this chip
+ - interrupts:       interrupt specification for this chip
+
+Optional properties:
+ - reset-gpios: GPIO specification for the RESET input
+ - wake-gpios:  GPIO specification for the WAKE input
+
+ - pinctrl-names: should be "default"
+ - pinctrl-0:   a phandle pointing to the pin settings for the
+                control gpios
+
+ - threshold:   allows setting the "click"-threshold in the range
+                from 20 to 80.
+
+ - gain:        allows setting the sensitivity in the range from 0 to
+                31. Note that lower values indicate higher
+                sensitivity.
+
+ - offset:      allows setting the edge compensation in the range from
+                0 to 31.
+ - report_rate: allows setting the report rate in the range from 3 to
+                14.
+
+Example:
+	polytouch: edt-ft5x06@38 {
+		compatible = "edt,edt-ft5x06";
+		reg = <0x38>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&edt_ft5x06_pins>;
+		interrupt-parent = <&gpio2>;
+		interrupts = <5 0>;
+		reset-gpios = <&gpio2 6 1>;
+		wake-gpios = <&gpio4 9 0>;
+	};
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index 7b4470d..257a1c8 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -33,6 +33,7 @@
 #include <linux/debugfs.h>
 #include <linux/slab.h>
 #include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <linux/input/mt.h>
 #include <linux/input/edt-ft5x06.h>
 
@@ -65,6 +66,10 @@ struct edt_ft5x06_ts_data {
 	u16 num_x;
 	u16 num_y;
 
+	int reset_pin;
+	int irq_pin;
+	int wake_pin;
+
 #if defined(CONFIG_DEBUG_FS)
 	struct dentry *debug_dir;
 	u8 *raw_buffer;
@@ -617,24 +622,38 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
 
 
 static int edt_ft5x06_ts_reset(struct i2c_client *client,
-					 int reset_pin)
+			struct edt_ft5x06_ts_data *tsdata)
 {
 	int error;
 
-	if (gpio_is_valid(reset_pin)) {
+	if (gpio_is_valid(tsdata->wake_pin)) {
+		error = devm_gpio_request_one(&client->dev,
+					tsdata->wake_pin, GPIOF_OUT_INIT_LOW,
+					"edt-ft5x06 wake");
+		if (error) {
+			dev_err(&client->dev,
+				"Failed to request GPIO %d as wake pin, error %d\n",
+				tsdata->wake_pin, error);
+			return error;
+		}
+
+		mdelay(5);
+		gpio_set_value(tsdata->wake_pin, 1);
+	}
+	if (gpio_is_valid(tsdata->reset_pin)) {
 		/* this pulls reset down, enabling the low active reset */
-		error = devm_gpio_request_one(&client->dev, reset_pin,
-					      GPIOF_OUT_INIT_LOW,
-					      "edt-ft5x06 reset");
+		error = devm_gpio_request_one(&client->dev,
+					tsdata->reset_pin, GPIOF_OUT_INIT_LOW,
+					"edt-ft5x06 reset");
 		if (error) {
 			dev_err(&client->dev,
 				"Failed to request GPIO %d as reset pin, error %d\n",
-				reset_pin, error);
+				tsdata->reset_pin, error);
 			return error;
 		}
 
 		mdelay(50);
-		gpio_set_value(reset_pin, 1);
+		gpio_set_value(tsdata->reset_pin, 1);
 		mdelay(100);
 	}
 
@@ -675,6 +694,21 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
 	    pdata->name <= edt_ft5x06_attr_##name.limit_high)		\
 		edt_ft5x06_register_write(tsdata, reg, pdata->name)
 
+#define EDT_GET_PROP(name, reg) {					\
+	const u32 *prop = of_get_property(np, #name, NULL);		\
+	if (prop)							\
+		edt_ft5x06_register_write(tsdata, reg, be32_to_cpu(*prop)); \
+}
+
+static void edt_ft5x06_ts_get_dt_defaults(struct device_node *np,
+					struct edt_ft5x06_ts_data *tsdata)
+{
+	EDT_GET_PROP(threshold, WORK_REGISTER_THRESHOLD);
+	EDT_GET_PROP(gain, WORK_REGISTER_GAIN);
+	EDT_GET_PROP(offset, WORK_REGISTER_OFFSET);
+	EDT_GET_PROP(report_rate, WORK_REGISTER_REPORT_RATE);
+}
+
 static void
 edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata,
 			   const struct edt_ft5x06_platform_data *pdata)
@@ -702,6 +736,33 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
 	tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y);
 }
 
+#ifdef CONFIG_OF
+static int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
+				struct edt_ft5x06_ts_data *tsdata)
+{
+	struct device_node *np = dev->of_node;
+
+	if (!np)
+		return -ENODEV;
+
+	/*
+	 * irq_pin is not needed for DT setup.
+	 * irq is associated via 'interrupts' property in DT
+	 */
+	tsdata->irq_pin = -EINVAL;
+	tsdata->reset_pin = of_get_named_gpio(np, "reset-gpios", 0);
+	tsdata->wake_pin = of_get_named_gpio(np, "wake-gpios", 0);
+
+	return 0;
+}
+#else
+static inline int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
+					struct edt_ft5x06_i2c_ts_data *tsdata)
+{
+	return -ENODEV;
+}
+#endif
+
 static int edt_ft5x06_ts_probe(struct i2c_client *client,
 					 const struct i2c_device_id *id)
 {
@@ -714,32 +775,40 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
 
 	dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
 
+	tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
+	if (!tsdata) {
+		dev_err(&client->dev, "failed to allocate driver data.\n");
+		return -ENOMEM;
+	}
+
 	if (!pdata) {
-		dev_err(&client->dev, "no platform data?\n");
-		return -EINVAL;
+		error = edt_ft5x06_i2c_ts_probe_dt(&client->dev, tsdata);
+		if (error) {
+			dev_err(&client->dev,
+				"DT probe failed and no platform data present\n");
+			return error;
+		}
+	} else {
+		tsdata->reset_pin = pdata->reset_pin;
+		tsdata->irq_pin = pdata->irq_pin;
+		tsdata->wake_pin = -EINVAL;
 	}
 
-	error = edt_ft5x06_ts_reset(client, pdata->reset_pin);
+	error = edt_ft5x06_ts_reset(client, tsdata);
 	if (error)
 		return error;
 
-	if (gpio_is_valid(pdata->irq_pin)) {
-		error = devm_gpio_request_one(&client->dev, pdata->irq_pin,
-					      GPIOF_IN, "edt-ft5x06 irq");
+	if (gpio_is_valid(tsdata->irq_pin)) {
+		error = devm_gpio_request_one(&client->dev, tsdata->irq_pin,
+					GPIOF_IN, "edt-ft5x06 irq");
 		if (error) {
 			dev_err(&client->dev,
 				"Failed to request GPIO %d, error %d\n",
-				pdata->irq_pin, error);
+				tsdata->irq_pin, error);
 			return error;
 		}
 	}
 
-	tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
-	if (!tsdata) {
-		dev_err(&client->dev, "failed to allocate driver data.\n");
-		return -ENOMEM;
-	}
-
 	input = devm_input_allocate_device(&client->dev);
 	if (!input) {
 		dev_err(&client->dev, "failed to allocate input device.\n");
@@ -757,7 +826,11 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
 		return error;
 	}
 
-	edt_ft5x06_ts_get_defaults(tsdata, pdata);
+	if (!pdata)
+		edt_ft5x06_ts_get_dt_defaults(client->dev.of_node, tsdata);
+	else
+		edt_ft5x06_ts_get_defaults(tsdata, pdata);
+
 	edt_ft5x06_ts_get_parameters(tsdata);
 
 	dev_dbg(&client->dev,
@@ -787,10 +860,10 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
 	input_set_drvdata(input, tsdata);
 	i2c_set_clientdata(client, tsdata);
 
-	error = devm_request_threaded_irq(&client->dev, client->irq,
-					  NULL, edt_ft5x06_ts_isr,
-					  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-					  client->name, tsdata);
+	error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+					edt_ft5x06_ts_isr,
+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					client->name, tsdata);
 	if (error) {
 		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
 		return error;
@@ -801,19 +874,21 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
 		return error;
 
 	error = input_register_device(input);
-	if (error) {
-		sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group);
-		return error;
-	}
+	if (error)
+		goto err_remove_attrs;
 
 	edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev));
 	device_init_wakeup(&client->dev, 1);
 
 	dev_dbg(&client->dev,
-		"EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.\n",
-		pdata->irq_pin, pdata->reset_pin);
+		"EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n",
+		client->irq, tsdata->wake_pin, tsdata->reset_pin);
 
 	return 0;
+
+err_remove_attrs:
+	sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+	return error;
 }
 
 static int edt_ft5x06_ts_remove(struct i2c_client *client)
@@ -857,10 +932,17 @@ static const struct i2c_device_id edt_ft5x06_ts_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
 
+static const struct of_device_id edt_ft5x06_of_match[] = {
+	{ .compatible = "edt,edt-ft5x06", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
+
 static struct i2c_driver edt_ft5x06_ts_driver = {
 	.driver = {
 		.owner = THIS_MODULE,
 		.name = "edt_ft5x06",
+		.of_match_table = edt_ft5x06_of_match,
 		.pm = &edt_ft5x06_ts_pm_ops,
 	},
 	.id_table = edt_ft5x06_ts_id,
-- 
1.7.10.4

^ permalink raw reply related

* [PATCHv4 3/5] Input: edt-ft5x06: Adjust delays to conform datasheet
From: Lothar Waßmann @ 2014-03-19 13:09 UTC (permalink / raw)
  To: Dmitry Torokhov, Fugang Duan, Grant Likely, Henrik Rydberg,
	Ian Campbell, Jingoo Han, Kumar Gala, Mark Rutland, Pawel Moll,
	Rob Herring, Rob Landley, Sachin Kamat, devicetree, linux-doc,
	linux-input, linux-kernel, Simon Budig, Lothar Waßmann
In-Reply-To: <1395234563-11034-1-git-send-email-LW@KARO-electronics.de>

The FT5x06 datasheet specifies a minimum reset width of 5ms and a
delay between deassertion of reset and start of reporting of 300ms.
Adjust the delays to conform to the datasheet.

With the original delays I sometimes experienced communication
timeouts when initializing the controller.

Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 drivers/input/touchscreen/edt-ft5x06.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index 257a1c8..27dccfc 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -652,9 +652,9 @@ static int edt_ft5x06_ts_reset(struct i2c_client *client,
 			return error;
 		}
 
-		mdelay(50);
+		mdelay(5);
 		gpio_set_value(tsdata->reset_pin, 1);
-		mdelay(100);
+		msleep(300);
 	}
 
 	return 0;
-- 
1.7.10.4

^ permalink raw reply related

* [PATCHv4 4/5] Input: edt-ft5x06: Ignore touchdown events
From: Lothar Waßmann @ 2014-03-19 13:09 UTC (permalink / raw)
  To: Dmitry Torokhov, Fugang Duan, Grant Likely, Henrik Rydberg,
	Ian Campbell, Jingoo Han, Kumar Gala, Mark Rutland, Pawel Moll,
	Rob Herring, Rob Landley, Sachin Kamat, devicetree, linux-doc,
	linux-input, linux-kernel, Simon Budig, Lothar Waßmann
In-Reply-To: <1395234563-11034-1-git-send-email-LW@KARO-electronics.de>

The chip may report invalid coordinates on touchdown events, so don't
report the initial touchdown event.

Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 drivers/input/touchscreen/edt-ft5x06.c |    4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index 27dccfc..af736e4 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -175,6 +175,10 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
 		if (type == TOUCH_EVENT_RESERVED)
 			continue;
 
+		/* ignore TOUCH_DOWN events, might have bogus coordinates */
+		if (type == TOUCH_EVENT_DOWN)
+			continue;
+
 		x = ((buf[0] << 8) | buf[1]) & 0x0fff;
 		y = ((buf[2] << 8) | buf[3]) & 0x0fff;
 		id = (buf[2] >> 4) & 0x0f;
-- 
1.7.10.4

^ permalink raw reply related

* [PATCHv4 5/5] Input: edt-ft5x06: Add support for M09 firmware version
From: Lothar Waßmann @ 2014-03-19 13:09 UTC (permalink / raw)
  To: Dmitry Torokhov, Fugang Duan, Grant Likely, Henrik Rydberg,
	Ian Campbell, Jingoo Han, Kumar Gala, Mark Rutland, Pawel Moll,
	Rob Herring, Rob Landley, Sachin Kamat, devicetree, linux-doc,
	linux-input, linux-kernel, Simon Budig, Lothar Waßmann
In-Reply-To: <1395234563-11034-1-git-send-email-LW@KARO-electronics.de>

There is a new firmware version for the EDT-FT5x06 chip.
Add support for detecting the firmware version and handle the
differences appropriately.

Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 drivers/input/touchscreen/edt-ft5x06.c |  360 ++++++++++++++++++++++++--------
 1 file changed, 277 insertions(+), 83 deletions(-)

diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index af736e4..acdda82 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -46,6 +46,14 @@
 #define WORK_REGISTER_NUM_X		0x33
 #define WORK_REGISTER_NUM_Y		0x34
 
+#define M09_REGISTER_THRESHOLD		0x80
+#define M09_REGISTER_GAIN		0x92
+#define M09_REGISTER_OFFSET		0x93
+#define M09_REGISTER_NUM_X		0x94
+#define M09_REGISTER_NUM_Y		0x95
+
+#define NO_REGISTER			0xff
+
 #define WORK_REGISTER_OPMODE		0x3c
 #define FACTORY_REGISTER_OPMODE		0x01
 
@@ -60,6 +68,20 @@
 #define EDT_RAW_DATA_RETRIES		100
 #define EDT_RAW_DATA_DELAY		1 /* msec */
 
+enum edt_ver {
+	M06,
+	M09,
+};
+
+struct edt_reg_addr {
+	int reg_threshold;
+	int reg_report_rate;
+	int reg_gain;
+	int reg_offset;
+	int reg_num_x;
+	int reg_num_y;
+};
+
 struct edt_ft5x06_ts_data {
 	struct i2c_client *client;
 	struct input_dev *input;
@@ -84,6 +106,9 @@ struct edt_ft5x06_ts_data {
 	int report_rate;
 
 	char name[EDT_NAME_LEN];
+
+	struct edt_reg_addr reg_addr;
+	enum edt_ver version;
 };
 
 static int edt_ft5x06_ts_readwrite(struct i2c_client *client,
@@ -141,33 +166,58 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
 {
 	struct edt_ft5x06_ts_data *tsdata = dev_id;
 	struct device *dev = &tsdata->client->dev;
-	u8 cmd = 0xf9;
-	u8 rdbuf[26];
+	u8 cmd;
+	u8 rdbuf[29];
 	int i, type, x, y, id;
+	int offset, tplen, datalen;
 	int error;
 
+	switch (tsdata->version) {
+	case M06:
+		cmd = 0xf9; /* tell the controller to send touch data */
+		offset = 5; /* where the actual touch data starts */
+		tplen = 4;  /* data comes in so called frames */
+		datalen = 26; /* how much bytes to listen for */
+		break;
+
+	case M09:
+		cmd = 0x02;
+		offset = 1;
+		tplen = 6;
+		datalen = 29;
+		break;
+
+	default:
+		goto out;
+	}
+
 	memset(rdbuf, 0, sizeof(rdbuf));
 
 	error = edt_ft5x06_ts_readwrite(tsdata->client,
 					sizeof(cmd), &cmd,
-					sizeof(rdbuf), rdbuf);
+					datalen, rdbuf);
 	if (error) {
 		dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",
 				    error);
 		goto out;
 	}
 
-	if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != 26) {
-		dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n",
-				    rdbuf[0], rdbuf[1], rdbuf[2]);
-		goto out;
-	}
+	/* M09 does not send header or CRC */
+	if (tsdata->version == M06) {
+		if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa ||
+			rdbuf[2] != datalen) {
+			dev_err_ratelimited(dev,
+					"Unexpected header: %02x%02x%02x!\n",
+					rdbuf[0], rdbuf[1], rdbuf[2]);
+			goto out;
+		}
 
-	if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, 26))
-		goto out;
+		if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen))
+			goto out;
+	}
 
 	for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
-		u8 *buf = &rdbuf[i * 4 + 5];
+		u8 *buf = &rdbuf[i * tplen + offset];
 		bool down;
 
 		type = buf[0] >> 6;
@@ -175,8 +225,8 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
 		if (type == TOUCH_EVENT_RESERVED)
 			continue;
 
-		/* ignore TOUCH_DOWN events, might have bogus coordinates */
-		if (type == TOUCH_EVENT_DOWN)
+		/* M06 sometimes sends bogus coordinates in TOUCH_DOWN */
+		if (tsdata->version == M06 && type == TOUCH_EVENT_DOWN)
 			continue;
 
 		x = ((buf[0] << 8) | buf[1]) & 0x0fff;
@@ -206,12 +256,25 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
 {
 	u8 wrbuf[4];
 
-	wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
-	wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
-	wrbuf[2] = value;
-	wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
-
-	return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL);
+	switch (tsdata->version) {
+	case M06:
+		wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+		wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+		wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+		wrbuf[2] = value;
+		wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
+		return edt_ft5x06_ts_readwrite(tsdata->client, 4,
+					wrbuf, 0, NULL);
+	case M09:
+		wrbuf[0] = addr;
+		wrbuf[1] = value;
+
+		return edt_ft5x06_ts_readwrite(tsdata->client, 3,
+					wrbuf, 0, NULL);
+
+	default:
+		return -EINVAL;
+	}
 }
 
 static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
@@ -220,19 +283,35 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
 	u8 wrbuf[2], rdbuf[2];
 	int error;
 
-	wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
-	wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
-	wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40;
+	switch (tsdata->version) {
+	case M06:
+		wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+		wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+		wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40;
 
-	error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, rdbuf);
-	if (error)
+		error = edt_ft5x06_ts_readwrite(tsdata->client,
+						2, wrbuf, 2, rdbuf);
 		return error;
 
-	if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) {
-		dev_err(&tsdata->client->dev,
-			"crc error: 0x%02x expected, got 0x%02x\n",
-			wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], rdbuf[1]);
-		return -EIO;
+		if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) {
+			dev_err(&tsdata->client->dev,
+				"crc error: 0x%02x expected, got 0x%02x\n",
+				wrbuf[0] ^ wrbuf[1] ^ rdbuf[0],
+				rdbuf[1]);
+			return -EIO;
+		}
+		break;
+
+	case M09:
+		wrbuf[0] = addr;
+		error = edt_ft5x06_ts_readwrite(tsdata->client, 1,
+						wrbuf, 1, rdbuf);
+		if (error)
+			return error;
+		break;
+
+	default:
+		return -EINVAL;
 	}
 
 	return rdbuf[0];
@@ -243,19 +322,21 @@ struct edt_ft5x06_attribute {
 	size_t field_offset;
 	u8 limit_low;
 	u8 limit_high;
-	u8 addr;
+	u8 addr_m06;
+	u8 addr_m09;
 };
 
-#define EDT_ATTR(_field, _mode, _addr, _limit_low, _limit_high)		\
+#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09,			\
+		_limit_low, _limit_high)				\
 	struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = {	\
 		.dattr = __ATTR(_field, _mode,				\
 				edt_ft5x06_setting_show,		\
 				edt_ft5x06_setting_store),		\
-		.field_offset =						\
-			offsetof(struct edt_ft5x06_ts_data, _field),	\
+		.field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \
+		.addr_m06 = _addr_m06,					\
+		.addr_m09 = _addr_m09,					\
 		.limit_low = _limit_low,				\
 		.limit_high = _limit_high,				\
-		.addr = _addr,						\
 	}
 
 static ssize_t edt_ft5x06_setting_show(struct device *dev,
@@ -270,6 +351,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
 	int val;
 	size_t count = 0;
 	int error = 0;
+	u8 addr;
 
 	mutex_lock(&tsdata->mutex);
 
@@ -278,15 +360,33 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
 		goto out;
 	}
 
-	val = edt_ft5x06_register_read(tsdata, attr->addr);
-	if (val < 0) {
-		error = val;
-		dev_err(&tsdata->client->dev,
-			"Failed to fetch attribute %s, error %d\n",
-			dattr->attr.name, error);
+	switch (tsdata->version) {
+	case M06:
+		addr = attr->addr_m06;
+		break;
+
+	case M09:
+		addr = attr->addr_m09;
+		break;
+
+	default:
+		error = -ENODEV;
 		goto out;
 	}
 
+	if (addr != NO_REGISTER) {
+		val = edt_ft5x06_register_read(tsdata, addr);
+		if (val < 0) {
+			error = val;
+			dev_err(&tsdata->client->dev,
+				"Failed to fetch attribute %s, error %d\n",
+				dattr->attr.name, error);
+			goto out;
+		}
+	} else {
+		val = *field;
+	}
+
 	if (val != *field) {
 		dev_warn(&tsdata->client->dev,
 			 "%s: read (%d) and stored value (%d) differ\n",
@@ -311,6 +411,7 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
 	u8 *field = (u8 *)tsdata + attr->field_offset;
 	unsigned int val;
 	int error;
+	u8 addr;
 
 	mutex_lock(&tsdata->mutex);
 
@@ -328,14 +429,29 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
 		goto out;
 	}
 
-	error = edt_ft5x06_register_write(tsdata, attr->addr, val);
-	if (error) {
-		dev_err(&tsdata->client->dev,
-			"Failed to update attribute %s, error: %d\n",
-			dattr->attr.name, error);
+	switch (tsdata->version) {
+	case M06:
+		addr = attr->addr_m06;
+		break;
+
+	case M09:
+		addr = attr->addr_m09;
+		break;
+
+	default:
+		error = -ENODEV;
 		goto out;
 	}
 
+	if (addr != NO_REGISTER) {
+		error = edt_ft5x06_register_write(tsdata, addr, val);
+		if (error) {
+			dev_err(&tsdata->client->dev,
+				"Failed to update attribute %s, error: %d\n",
+				dattr->attr.name, error);
+			goto out;
+		}
+	}
 	*field = val;
 
 out:
@@ -343,12 +459,14 @@ out:
 	return error ?: count;
 }
 
-static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, 0, 31);
-static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, 0, 31);
-static EDT_ATTR(threshold, S_IWUSR | S_IRUGO,
-		WORK_REGISTER_THRESHOLD, 20, 80);
-static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO,
-		WORK_REGISTER_REPORT_RATE, 3, 14);
+static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN,
+		M09_REGISTER_GAIN, 0, 31);
+static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET,
+		M09_REGISTER_OFFSET, 0, 31);
+static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD,
+		M09_REGISTER_THRESHOLD, 20, 80);
+static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE,
+		NO_REGISTER, 3, 14);
 
 static struct attribute *edt_ft5x06_attrs[] = {
 	&edt_ft5x06_attr_gain.dattr.attr,
@@ -383,6 +501,9 @@ static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
 	}
 
 	/* mode register is 0x3c when in the work mode */
+	if (tsdata->version == M09)
+		goto m09_out;
+
 	error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03);
 	if (error) {
 		dev_err(&client->dev,
@@ -415,12 +536,18 @@ err_out:
 	enable_irq(client->irq);
 
 	return error;
+
+m09_out:
+	dev_err(&client->dev, "No factory mode support for M09\n");
+	return -EINVAL;
+
 }
 
 static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
 {
 	struct i2c_client *client = tsdata->client;
 	int retries = EDT_SWITCH_MODE_RETRIES;
+	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
 	int ret;
 	int error;
 
@@ -453,13 +580,14 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
 	tsdata->raw_buffer = NULL;
 
 	/* restore parameters */
-	edt_ft5x06_register_write(tsdata, WORK_REGISTER_THRESHOLD,
+	edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold,
 				  tsdata->threshold);
-	edt_ft5x06_register_write(tsdata, WORK_REGISTER_GAIN,
+	edt_ft5x06_register_write(tsdata, reg_addr->reg_gain,
 				  tsdata->gain);
-	edt_ft5x06_register_write(tsdata, WORK_REGISTER_OFFSET,
+	edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
 				  tsdata->offset);
-	edt_ft5x06_register_write(tsdata, WORK_REGISTER_REPORT_RATE,
+	if (reg_addr->reg_report_rate)
+		edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
 				  tsdata->report_rate);
 
 	enable_irq(client->irq);
@@ -665,30 +793,60 @@ static int edt_ft5x06_ts_reset(struct i2c_client *client,
 }
 
 static int edt_ft5x06_ts_identify(struct i2c_client *client,
-					    char *model_name,
-					    char *fw_version)
+					struct edt_ft5x06_ts_data *tsdata,
+					char *fw_version)
 {
 	u8 rdbuf[EDT_NAME_LEN];
 	char *p;
 	int error;
+	char *model_name = tsdata->name;
 
+	/* see what we find if we assume it is a M06 *
+	 * if we get less than EDT_NAME_LEN, we don't want
+	 * to have garbage in there
+	 */
+	memset(rdbuf, 0, sizeof(rdbuf));
 	error = edt_ft5x06_ts_readwrite(client, 1, "\xbb",
 					EDT_NAME_LEN - 1, rdbuf);
 	if (error)
 		return error;
 
-	/* remove last '$' end marker */
-	rdbuf[EDT_NAME_LEN - 1] = '\0';
-	if (rdbuf[EDT_NAME_LEN - 2] == '$')
-		rdbuf[EDT_NAME_LEN - 2] = '\0';
+	/* if we find something consistent, stay with that assumption
+	 * at least M09 won't send 3 bytes here
+	 */
+	if (!(strnicmp(rdbuf + 1, "EP0", 3))) {
+		tsdata->version = M06;
+
+		/* remove last '$' end marker */
+		rdbuf[EDT_NAME_LEN - 1] = '\0';
+		if (rdbuf[EDT_NAME_LEN - 2] == '$')
+			rdbuf[EDT_NAME_LEN - 2] = '\0';
+
+		/* look for Model/Version separator */
+		p = strchr(rdbuf, '*');
+		if (p)
+			*p++ = '\0';
+		strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN);
+		strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
+	} else {
+		/* since there are only two versions around (M06, M09) */
+		tsdata->version = M09;
+
+		error = edt_ft5x06_ts_readwrite(client, 1, "\xA6",
+						2, rdbuf);
+		if (error)
+			return error;
 
-	/* look for Model/Version separator */
-	p = strchr(rdbuf, '*');
-	if (p)
-		*p++ = '\0';
+		strlcpy(fw_version, rdbuf, 2);
 
-	strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN);
-	strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
+		error = edt_ft5x06_ts_readwrite(client, 1, "\xA8",
+						1, rdbuf);
+		if (error)
+			return error;
+
+		snprintf(model_name, EDT_NAME_LEN, "EP0%i%i0M09",
+			rdbuf[0] >> 4, rdbuf[0] & 0x0F);
+	}
 
 	return 0;
 }
@@ -707,37 +865,71 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
 static void edt_ft5x06_ts_get_dt_defaults(struct device_node *np,
 					struct edt_ft5x06_ts_data *tsdata)
 {
-	EDT_GET_PROP(threshold, WORK_REGISTER_THRESHOLD);
-	EDT_GET_PROP(gain, WORK_REGISTER_GAIN);
-	EDT_GET_PROP(offset, WORK_REGISTER_OFFSET);
-	EDT_GET_PROP(report_rate, WORK_REGISTER_REPORT_RATE);
+	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
+	EDT_GET_PROP(threshold, reg_addr->reg_threshold);
+	EDT_GET_PROP(gain, reg_addr->reg_gain);
+	EDT_GET_PROP(offset, reg_addr->reg_offset);
+	if (reg_addr->reg_report_rate != NO_REGISTER)
+		EDT_GET_PROP(report_rate, reg_addr->reg_report_rate);
 }
 
 static void
 edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata,
 			   const struct edt_ft5x06_platform_data *pdata)
 {
+	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
 	if (!pdata->use_parameters)
 		return;
 
 	/* pick up defaults from the platform data */
-	EDT_ATTR_CHECKSET(threshold, WORK_REGISTER_THRESHOLD);
-	EDT_ATTR_CHECKSET(gain, WORK_REGISTER_GAIN);
-	EDT_ATTR_CHECKSET(offset, WORK_REGISTER_OFFSET);
-	EDT_ATTR_CHECKSET(report_rate, WORK_REGISTER_REPORT_RATE);
+	EDT_ATTR_CHECKSET(threshold, reg_addr->reg_threshold);
+	EDT_ATTR_CHECKSET(gain, reg_addr->reg_gain);
+	EDT_ATTR_CHECKSET(offset, reg_addr->reg_offset);
+	if (reg_addr->reg_report_rate != NO_REGISTER)
+		EDT_ATTR_CHECKSET(report_rate, reg_addr->reg_report_rate);
 }
 
 static void
 edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
 {
+	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
 	tsdata->threshold = edt_ft5x06_register_read(tsdata,
-						     WORK_REGISTER_THRESHOLD);
-	tsdata->gain = edt_ft5x06_register_read(tsdata, WORK_REGISTER_GAIN);
-	tsdata->offset = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OFFSET);
-	tsdata->report_rate = edt_ft5x06_register_read(tsdata,
-						WORK_REGISTER_REPORT_RATE);
-	tsdata->num_x = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_X);
-	tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y);
+						     reg_addr->reg_threshold);
+	tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain);
+	tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset);
+	if (reg_addr->reg_report_rate != NO_REGISTER)
+		tsdata->report_rate = edt_ft5x06_register_read(tsdata,
+						reg_addr->reg_report_rate);
+	tsdata->num_x = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_x);
+	tsdata->num_y = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_y);
+}
+
+static void
+edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
+{
+	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
+	switch (tsdata->version) {
+	case M06:
+		reg_addr->reg_threshold = WORK_REGISTER_THRESHOLD;
+		reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE;
+		reg_addr->reg_gain = WORK_REGISTER_GAIN;
+		reg_addr->reg_offset = WORK_REGISTER_OFFSET;
+		reg_addr->reg_num_x = WORK_REGISTER_NUM_X;
+		reg_addr->reg_num_y = WORK_REGISTER_NUM_Y;
+		break;
+
+	case M09:
+		reg_addr->reg_threshold = M09_REGISTER_THRESHOLD;
+		reg_addr->reg_gain = M09_REGISTER_GAIN;
+		reg_addr->reg_offset = M09_REGISTER_OFFSET;
+		reg_addr->reg_num_x = M09_REGISTER_NUM_X;
+		reg_addr->reg_num_y = M09_REGISTER_NUM_Y;
+		break;
+	}
 }
 
 #ifdef CONFIG_OF
@@ -824,12 +1016,14 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
 	tsdata->input = input;
 	tsdata->factory_mode = false;
 
-	error = edt_ft5x06_ts_identify(client, tsdata->name, fw_version);
+	error = edt_ft5x06_ts_identify(client, tsdata, fw_version);
 	if (error) {
 		dev_err(&client->dev, "touchscreen probe failed\n");
 		return error;
 	}
 
+	edt_ft5x06_ts_set_regs(tsdata);
+
 	if (!pdata)
 		edt_ft5x06_ts_get_dt_defaults(client->dev.of_node, tsdata);
 	else
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* Re: [PATCH 1/3] Input: synaptics-rmi4 - add capabilities for touchpads
From: Benjamin Tissoires @ 2014-03-19 14:29 UTC (permalink / raw)
  To: Christopher Heiny, Dmitry Torokhov
  Cc: Linux Input, Andrew Duggan, Vincent Huang, Vivian Ly,
	Daniel Rosenberg, Linus Walleij, David Herrmann, Jiri Kosina
In-Reply-To: <1395191031-3144-1-git-send-email-cheiny@synaptics.com>

Hi Chris,

On 03/18/2014 09:03 PM, Christopher Heiny wrote:
> When the device is a touchpad additional capabilities need to
> be set and reported.
>

We have a problem here. While this patch would have been fine in the 
pre-v3.8 era, it is not true anymore.
However, the current branch where synaptics-rmi4 is attached is v3.4.

So if you use the right API (the current one), it will not compile :(

Dmitry, would it be possible to update the branch to at least v3.8 to 
get the new input-mt API? (if the Synaptics guys are ok).

> Signed-off-by: Andrew Duggan <aduggan@synaptics.com>
> Acked-by: Christopher Heiny <cheiny@synaptics.com>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> Cc: Linux Walleij <linus.walleij@linaro.org>
> Cc: David Herrmann <dh.herrmann@gmail.com>
> Cc: Jiri Kosina <jkosina@suse.cz>
>
> ---
>   drivers/input/rmi4/rmi_f11.c | 17 ++++++++++++++++-
>   1 file changed, 16 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
> index 8709abe..07044d79 100644
> --- a/drivers/input/rmi4/rmi_f11.c
> +++ b/drivers/input/rmi4/rmi_f11.c
> @@ -688,6 +688,9 @@ static void rmi_f11_abs_pos_report(struct f11_data *f11,
>   	/* MT sync between fingers */
>   	if (sensor->type_a)
>   		input_mt_sync(sensor->input);
> +
> +	if (sensor->sensor_type == rmi_f11_sensor_touchpad)
> +		input_mt_report_pointer_emulation(sensor->input, true);

In recent kernels, you just need to set the input mt flags 
INPUT_MT_POINTER when initializing the slots, and you just need to call 
input_mt_sync_frame at the end of the report. The mt lib will take care 
of the pointer emulation, finger count, etc...

(see commit 55e49089f4589908eb688742d2d7eff33b74ac78)

>   }
>
>   static void rmi_f11_finger_handler(struct f11_data *f11,
> @@ -717,7 +720,7 @@ static void rmi_f11_finger_handler(struct f11_data *f11,
>   		if (sensor->data.rel_pos)
>   			rmi_f11_rel_pos_report(sensor, i);
>   	}
> -	input_mt_sync(sensor->input);

This particular line is a bug in the current implementation. Only 
multitouch protocol A devices should use input_mt_sync.

> +	input_report_key(sensor->input, BTN_TOUCH, finger_pressed_count);

This is already handled by input_mt_sync_frame.

>   	input_sync(sensor->input);
>   }
>
> @@ -1137,6 +1140,9 @@ static void f11_set_abs_params(struct rmi_function *fn, struct f11_data *f11)
>   	dev_dbg(&fn->dev, "Set ranges X=[%d..%d] Y=[%d..%d].",
>   			x_min, x_max, y_min, y_max);
>
> +	input_set_abs_params(input, ABS_X, x_min, x_max, 0, 0);
> +	input_set_abs_params(input, ABS_Y, y_min, y_max, 0, 0);
> +

There is no need (and it's not the way you should do) to setup the ABS_X 
and ABS_Y (and ABS_PRESSURE) axis if you call input_mt_init_slot after 
having set all the input mt axis.

As a general rule, set all the mt axis, then call input_mt_init_slot. It 
will handle the single touch emulation for you in a better way (like 
fuzz should not be set for ABS_X|Y otherwise it will be called twice).

>   	input_set_abs_params(input, ABS_MT_PRESSURE, 0,
>   			DEFAULT_MAX_ABS_MT_PRESSURE, 0, 0);
>   	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
> @@ -1374,6 +1380,15 @@ static int rmi_f11_register_devices(struct rmi_function *fn)
>   		set_bit(BTN_RIGHT, input_dev_mouse->keybit);
>   	}
>
> +	if (sensor->sensor_type == rmi_f11_sensor_touchpad) {
> +		set_bit(BTN_TOOL_FINGER, input_dev->keybit);
> +		set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
> +		set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
> +		set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
> +		set_bit(BTN_TOOL_QUINTTAP, input_dev->keybit);
> +	}
> +

This is already handled by input_mt_init_slot with the flag 
INPUT_MT_POINTER.

Cheers,
Benjamin

> +
>   	return 0;
>
>   error_unregister:
>

^ permalink raw reply

* Re: [PATCH 2/3] Input: synaptics-rmi4 - ability disable abs or rel reporting
From: Benjamin Tissoires @ 2014-03-19 15:02 UTC (permalink / raw)
  To: Christopher Heiny, Dmitry Torokhov
  Cc: Linux Input, Andrew Duggan, Vincent Huang, Vivian Ly,
	Daniel Rosenberg, Linus Walleij, David Herrmann, Jiri Kosina
In-Reply-To: <1395191031-3144-2-git-send-email-cheiny@synaptics.com>



On 03/18/2014 09:03 PM, Christopher Heiny wrote:
> Even if the RMI4 touchscreen/touchpad provides reporting both
> relative and absolute coordinates, reporting both to userspace
> could be confusing. Allow the platform data to disable either
> absolute or relative coordinates.

General comments on the patch:
Is there really a need to export the rel axis when there is already an 
abs collection?
I mean, with the RMI4 over HID over I2C found on the XPS Haswell series, 
RMI4 will be bound automatically, and the sensor may (will) pretend that 
it can do both abs and rel. However, we are not using a platform_data 
for them (I think we should not), and we will get the two collections.

I would personally be in favor of having a priority mechanism: if abs is 
here, skip rel, otherwise use rel. But I have no clue if you will ship 
devices which will require both. So you make the call.

>
> Signed-off-by: Andrew Duggan <aduggan@synaptics.com>
> Acked-by: Christopher Heiny <cheiny@synaptics.com>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> Cc: Linux Walleij <linus.walleij@linaro.org>
> Cc: David Herrmann <dh.herrmann@gmail.com>
> Cc: Jiri Kosina <jkosina@suse.cz>
>
> ---
>   drivers/input/rmi4/rmi_f11.c | 78 +++++++++++++++++++++++++++++++++++++-------
>   include/linux/rmi.h          |  6 ++++
>   2 files changed, 73 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
> index 07044d79..c87c6cc3 100644
> --- a/drivers/input/rmi4/rmi_f11.c
> +++ b/drivers/input/rmi4/rmi_f11.c
> @@ -520,6 +520,8 @@ struct f11_2d_sensor {
>   	struct rmi_function *fn;
>   	char input_phys[NAME_BUFFER_SIZE];
>   	char input_phys_mouse[NAME_BUFFER_SIZE];
> +	u8 report_abs;
> +	u8 report_rel;
>   };
>
>   /** Data pertaining to F11 in general.  For per-sensor data, see struct
> @@ -544,6 +546,10 @@ struct f11_data {
>   	struct mutex dev_controls_mutex;
>   	u16 rezero_wait_ms;
>   	struct f11_2d_sensor sensor;
> +	unsigned long *abs_mask;
> +	unsigned long *rel_mask;
> +	unsigned long *result_bits;
> +	unsigned long mask_memory[];
>   };
>
>   enum finger_state_values {
> @@ -591,10 +597,14 @@ static void rmi_f11_rel_pos_report(struct f11_2d_sensor *sensor, u8 n_finger)
>   	if (x || y) {
>   		input_report_rel(sensor->input, REL_X, x);
>   		input_report_rel(sensor->input, REL_Y, y);
> -		input_report_rel(sensor->mouse_input, REL_X, x);
> -		input_report_rel(sensor->mouse_input, REL_Y, y);
> +
> +		if (sensor->mouse_input) {
> +			input_report_rel(sensor->mouse_input, REL_X, x);
> +			input_report_rel(sensor->mouse_input, REL_Y, y);
> +		}
>   	}
> -	input_sync(sensor->mouse_input);
> +	if (sensor->mouse_input)
> +		input_sync(sensor->mouse_input);
>   }
>
>   static void rmi_f11_abs_pos_report(struct f11_data *f11,
> @@ -694,13 +704,17 @@ static void rmi_f11_abs_pos_report(struct f11_data *f11,
>   }
>
>   static void rmi_f11_finger_handler(struct f11_data *f11,
> -				   struct f11_2d_sensor *sensor)
> +				   struct f11_2d_sensor *sensor,
> +				   unsigned long *irq_bits, int num_irq_regs)
>   {
>   	const u8 *f_state = sensor->data.f_state;
>   	u8 finger_state;
>   	u8 finger_pressed_count;
>   	u8 i;
>
> +	int rel_bits;
> +	int abs_bits;
> +
>   	for (i = 0, finger_pressed_count = 0; i < sensor->nbr_fingers; i++) {
>   		/* Possible of having 4 fingers per f_statet register */
>   		finger_state = (f_state[i / 4] >> (2 * (i % 4))) &
> @@ -714,13 +728,19 @@ static void rmi_f11_finger_handler(struct f11_data *f11,
>   			finger_pressed_count++;
>   		}
>
> -		if (sensor->data.abs_pos)
> +		abs_bits = bitmap_and(f11->result_bits, irq_bits, f11->abs_mask,
> +				num_irq_regs);
> +		if (abs_bits)
>   			rmi_f11_abs_pos_report(f11, sensor, finger_state, i);

rmi_driver.c uses bitmap_and this way:
bitmap_and(data->fn_irq_bits, data->irq_status, fn->irq_mask, 
data->irq_count);
if (!bitmap_empty(data->fn_irq_bits, data->irq_count))
	fh->attention(fn, data->fn_irq_bits);

Not sure which way is the best.

>
> -		if (sensor->data.rel_pos)
> +		rel_bits = bitmap_and(f11->result_bits, irq_bits, f11->rel_mask,
> +				num_irq_regs);
> +		if (rel_bits)
>   			rmi_f11_rel_pos_report(sensor, i);
>   	}
> +
>   	input_report_key(sensor->input, BTN_TOUCH, finger_pressed_count);
> +

those two blank lines are unrelated to the commit.

>   	input_sync(sensor->input);
>   }
>
> @@ -1180,21 +1200,33 @@ static int rmi_f11_initialize(struct rmi_function *fn)
>   	u16 max_x_pos, max_y_pos, temp;
>   	int rc;
>   	const struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
> +	struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
>   	struct f11_2d_sensor *sensor;
>   	u8 buf;
> +	int mask_size;
>
>   	dev_dbg(&fn->dev, "Initializing F11 values for %s.\n",
>   		 pdata->sensor_name);
>
> +	mask_size = BITS_TO_LONGS(drvdata->irq_count) * sizeof(unsigned long);
> +
>   	/*
>   	** init instance data, fill in values and create any sysfs files
>   	*/
> -	f11 = devm_kzalloc(&fn->dev, sizeof(struct f11_data), GFP_KERNEL);
> +	f11 = devm_kzalloc(&fn->dev, sizeof(struct f11_data) + mask_size * 3,
> +			GFP_KERNEL);
>   	if (!f11)
>   		return -ENOMEM;
>
>   	f11->rezero_wait_ms = pdata->f11_rezero_wait;
>
> +	f11->abs_mask = f11->mask_memory + mask_size * 0;

I personally don't like the " + mask_size * 0"

Can't you just also remove the mask_memory field and use sizeof(struct 
f11_data)?

> +	f11->rel_mask = f11->mask_memory + mask_size * 1;
> +	f11->result_bits = f11->mask_memory + mask_size * 2;
> +
> +	set_bit(fn->irq_pos, f11->abs_mask);
> +	set_bit(fn->irq_pos + 1, f11->rel_mask);
> +
>   	query_base_addr = fn->fd.query_base_addr;
>   	control_base_addr = fn->fd.control_base_addr;
>
> @@ -1226,12 +1258,25 @@ static int rmi_f11_initialize(struct rmi_function *fn)
>   		return rc;
>   	}
>
> +	sensor->report_rel = sensor->sens_query.has_rel;
> +	sensor->report_abs = sensor->sens_query.has_abs;
> +
>   	if (pdata->f11_sensor_data) {
>   		sensor->axis_align =
>   			pdata->f11_sensor_data->axis_align;
>   		sensor->type_a = pdata->f11_sensor_data->type_a;
>   		sensor->sensor_type =
>   				pdata->f11_sensor_data->sensor_type;
> +
> +		if (sensor->sens_query.has_abs)
> +			sensor->report_abs = sensor->report_abs
> +				&& !(pdata->f11_sensor_data->disable_report_mask
> +					& RMI_F11_DISABLE_ABS_REPORT);

sensor->report_abs already contains sensor->sens_query.has_abs (set few 
lines above)...

so you can just skip the if test here.


> +
> +		if (sensor->sens_query.has_rel)
> +			sensor->report_rel = sensor->report_rel
> +				&& !(pdata->f11_sensor_data->disable_report_mask
> +					& RMI_F11_DISABLE_REL_REPORT);

same here.

>   	}
>
>   	rc = rmi_read_block(rmi_dev,
> @@ -1324,9 +1369,10 @@ static int rmi_f11_register_devices(struct rmi_function *fn)
>   	set_bit(EV_ABS, input_dev->evbit);
>   	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
>
> -	f11_set_abs_params(fn, f11);
> +	if (sensor->report_abs)
> +		f11_set_abs_params(fn, f11);
>
> -	if (sensor->sens_query.has_rel) {
> +	if (sensor->report_rel) {
>   		set_bit(EV_REL, input_dev->evbit);
>   		set_bit(REL_X, input_dev->relbit);
>   		set_bit(REL_Y, input_dev->relbit);
> @@ -1338,7 +1384,7 @@ static int rmi_f11_register_devices(struct rmi_function *fn)
>   		goto error_unregister;
>   	}
>
> -	if (sensor->sens_query.has_rel) {
> +	if (sensor->report_rel) {
>   		/*create input device for mouse events  */
>   		input_dev_mouse = input_allocate_device();
>   		if (!input_dev_mouse) {
> @@ -1407,8 +1453,16 @@ error_unregister:
>   static int rmi_f11_config(struct rmi_function *fn)
>   {
>   	struct f11_data *f11 = dev_get_drvdata(&fn->dev);
> +	struct rmi_driver *drv = fn->rmi_dev->driver;
> +	struct f11_2d_sensor *sensor = &f11->sensor;
>   	int rc;
>
> +	if (!sensor->report_abs)
> +		drv->clear_irq_bits(fn->rmi_dev, f11->abs_mask);
> +
> +	if (!sensor->report_rel)
> +		drv->clear_irq_bits(fn->rmi_dev, f11->rel_mask);
> +
>   	rc = f11_write_control_regs(fn, &f11->sensor.sens_query,
>   			   &f11->dev_controls, fn->fd.query_base_addr);
>   	if (rc < 0)
> @@ -1420,6 +1474,7 @@ static int rmi_f11_config(struct rmi_function *fn)
>   static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits)
>   {
>   	struct rmi_device *rmi_dev = fn->rmi_dev;
> +	struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
>   	struct f11_data *f11 = dev_get_drvdata(&fn->dev);
>   	u16 data_base_addr = fn->fd.data_base_addr;
>   	u16 data_base_addr_offset = 0;
> @@ -1432,7 +1487,8 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits)
>   	if (error)
>   		return error;
>
> -	rmi_f11_finger_handler(f11, &f11->sensor);
> +	rmi_f11_finger_handler(f11, &f11->sensor, irq_bits,
> +				drvdata->num_of_irq_regs);
>   	data_base_addr_offset += f11->sensor.pkt_size;
>
>   	return 0;
> diff --git a/include/linux/rmi.h b/include/linux/rmi.h
> index 735e978..a0d0187 100644
> --- a/include/linux/rmi.h
> +++ b/include/linux/rmi.h
> @@ -76,6 +76,9 @@ enum rmi_f11_sensor_type {
>   	rmi_f11_sensor_touchpad
>   };
>
> +#define RMI_F11_DISABLE_ABS_REPORT      (1 << 0)
> +#define RMI_F11_DISABLE_REL_REPORT      (1 << 1)

We have BIT() macro in the kernel for this (I know, I do not use it that 
much either... :-P )

Cheers,
Benjamin

> +
>   /**
>    * struct rmi_f11_sensor_data - overrides defaults for a single F11 2D sensor.
>    * @axis_align - provides axis alignment overrides (see above).
> @@ -86,11 +89,14 @@ enum rmi_f11_sensor_type {
>    * pointing device (touchpad) rather than a direct pointing device
>    * (touchscreen).  This is useful when F11_2D_QUERY14 register is not
>    * available.
> + * @disable_report_mask - Force data to not be reported even if it is supported
> + * by the firware.
>    */
>   struct rmi_f11_sensor_data {
>   	struct rmi_f11_2d_axis_alignment axis_align;
>   	bool type_a;
>   	enum rmi_f11_sensor_type sensor_type;
> +	int disable_report_mask;
>   };
>
>   /**
>

^ permalink raw reply

* Re: [PATCH 3/3] Input: synaptics-rmi4 - report sensor resolution
From: Benjamin Tissoires @ 2014-03-19 15:11 UTC (permalink / raw)
  To: Christopher Heiny, Dmitry Torokhov
  Cc: Linux Input, Andrew Duggan, Vincent Huang, Vivian Ly,
	Daniel Rosenberg, Linus Walleij, David Herrmann, Jiri Kosina
In-Reply-To: <1395191031-3144-3-git-send-email-cheiny@synaptics.com>



On 03/18/2014 09:03 PM, Christopher Heiny wrote:
> Reports the sensor resolution by reading the size of the sensor
> from F11 query registers or from the platform data if the firmware
> does not contain the appropriate query registers.

Hehe, nice, I was just wondering if it was possible to retrieve this 
info from the FW. :)

>
> Signed-off-by: Andrew Duggan <aduggan@synaptics.com>
> Acked-by: Christopher Heiny <cheiny@synaptics.com>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> Cc: Linux Walleij <linus.walleij@linaro.org>
> Cc: David Herrmann <dh.herrmann@gmail.com>
> Cc: Jiri Kosina <jkosina@suse.cz>
>
> ---
>   drivers/input/rmi4/rmi_f11.c | 53 +++++++++++++++++++++++++++++++++++++++++++-
>   include/linux/rmi.h          |  2 ++
>   2 files changed, 54 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
> index f5b8b71..f2a6f5f 100644
> --- a/drivers/input/rmi4/rmi_f11.c
> +++ b/drivers/input/rmi4/rmi_f11.c
> @@ -408,6 +408,10 @@ struct f11_2d_sensor_queries {
>   	u8 clickpad_props;
>   	u8 mouse_buttons;
>   	bool has_advanced_gestures;
> +
> +	/* Query 15 - 18 */
> +	u16 x_sensor_size_mm;
> +	u16 y_sensor_size_mm;
>   };
>
>   /* Defs for Ctrl0. */
> @@ -520,6 +524,8 @@ struct f11_2d_sensor {
>   	struct rmi_function *fn;
>   	char input_phys[NAME_BUFFER_SIZE];
>   	char input_phys_mouse[NAME_BUFFER_SIZE];
> +	u8 x_mm;
> +	u8 y_mm;
>   	u8 report_abs;
>   	u8 report_rel;
>   };
> @@ -1098,6 +1104,20 @@ static int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev,
>   		query_size++;
>   	}
>
> +	if (f11->has_query12 && sensor_query->has_physical_props) {

sensor->has_physical_props is only set to true if f11->has_query12 is 
true (the initial struct is zero-allocated).

So we can safely skip the test against f11->has_query12

> +		rc = rmi_read_block(rmi_dev, query_base_addr
> +			+ query_size, query_buf, ARRAY_SIZE(query_buf));
> +		if (rc < 0)
> +			return rc;
> +
> +		sensor_query->x_sensor_size_mm =
> +			(query_buf[0] | (query_buf[1] << 8)) / 10;
> +		sensor_query->y_sensor_size_mm =
> +			(query_buf[2] | (query_buf[3] << 8)) / 10;
> +
> +		query_size += 4;

should be ARRAY_SIZE(query_buf). Or maybe we should use 4 in the 
rmi_read_block call (jsut in case someone changes the size of buf).

> +	}
> +
>   	return query_size;
>   }
>
> @@ -1119,6 +1139,7 @@ static void f11_set_abs_params(struct rmi_function *fn, struct f11_data *f11)
>   			((f11->dev_controls.ctrl0_9[9] & 0x0F) << 8);
>   	u16 x_min, x_max, y_min, y_max;
>   	unsigned int input_flags;
> +	int res_x, res_y;
>
>   	/* We assume touchscreen unless demonstrably a touchpad or specified
>   	 * as a touchpad in the platform data
> @@ -1175,6 +1196,18 @@ static void f11_set_abs_params(struct rmi_function *fn, struct f11_data *f11)
>   			x_min, x_max, 0, 0);
>   	input_set_abs_params(input, ABS_MT_POSITION_Y,
>   			y_min, y_max, 0, 0);
> +
> +	if (sensor->x_mm && sensor->y_mm) {
> +		res_x = (x_max - x_min) / sensor->x_mm;
> +		res_y = (y_max - y_min) / sensor->y_mm;
> +
> +		input_abs_set_res(input, ABS_X, res_x);
> +		input_abs_set_res(input, ABS_Y, res_y);
> +
> +		input_abs_set_res(input, ABS_MT_POSITION_X, res_x);
> +		input_abs_set_res(input, ABS_MT_POSITION_Y, res_y);
> +	}
> +
>   	if (!sensor->type_a)
>   		input_mt_init_slots(input, sensor->nbr_fingers, input_flags);
>   	if (IS_ENABLED(CONFIG_RMI4_F11_PEN) && sensor->sens_query.has_pen)
> @@ -1261,8 +1294,26 @@ static int rmi_f11_initialize(struct rmi_function *fn)
>   		sensor->axis_align =
>   			pdata->f11_sensor_data->axis_align;
>   		sensor->type_a = pdata->f11_sensor_data->type_a;
> -		sensor->sensor_type =
> +
> +		if (sensor->sens_query.has_info2) {
> +			if (sensor->sens_query.is_clear)
> +				sensor->sensor_type =
> +					rmi_f11_sensor_touchscreen;
> +			else
> +				sensor->sensor_type = rmi_f11_sensor_touchpad;
> +		} else {
> +			sensor->sensor_type =
>   				pdata->f11_sensor_data->sensor_type;

These few lines above are not related to the current commit. Please 
split this.

> +		}
> +
> +		if (f11->has_query12
> +			&& sensor->sens_query.has_physical_props) {

again, f11->has_query12 can be skipped

> +			sensor->x_mm = sensor->sens_query.x_sensor_size_mm;
> +			sensor->y_mm = sensor->sens_query.y_sensor_size_mm;
> +		} else {
> +			sensor->x_mm = pdata->f11_sensor_data->x_mm;
> +			sensor->y_mm = pdata->f11_sensor_data->y_mm;

there is a test regarding pdata->f11_sensor_data in 
rmi_f11_initialize(). So I guess this pointer might be null, and you 
will get an oops.

Cheers,
Benjamin

> +		}
>
>   		if (sensor->sens_query.has_abs)
>   			sensor->report_abs = sensor->report_abs
> diff --git a/include/linux/rmi.h b/include/linux/rmi.h
> index a0d0187..9139873 100644
> --- a/include/linux/rmi.h
> +++ b/include/linux/rmi.h
> @@ -96,6 +96,8 @@ struct rmi_f11_sensor_data {
>   	struct rmi_f11_2d_axis_alignment axis_align;
>   	bool type_a;
>   	enum rmi_f11_sensor_type sensor_type;
> +	int x_mm;
> +	int y_mm;
>   	int disable_report_mask;
>   };
>
>

^ permalink raw reply

* Re: [patch] Input: remove a duplicative NULL test
From: Nick Dyer @ 2014-03-19 16:02 UTC (permalink / raw)
  To: Benson Leung, fugang.duan@freescale.com
  Cc: Dan Carpenter, Dmitry Torokhov, Paul Gortmaker, Jingoo Han,
	Daniel Kurtz, linux-input@vger.kernel.org,
	kernel-janitors@vger.kernel.org
In-Reply-To: <CANLzEktLFDcMpt7Gq19=7T1Ak2Be4qs3fmDPyswHUNrVJiGjSQ@mail.gmail.com>

> On Fri, Feb 21, 2014 at 1:14 AM, fugang.duan@freescale.com
> <fugang.duan@freescale.com> wrote:
>>
>> Agree, it is redundant. And if you have free time, you can convert
>> the driver to support devicetree.
> 
> Please check with Nick Dyer before you convert this driver to support
> devicetree. Much of the platform data this driver uses has been
> eliminated or consolidated in a patch series that he's been working
> on.

If you look at the "improve T19 GPIO keys handling" patch in the patch set
I posted to linux-input on 17th March, this redundant NULL test has been
removed.

I want to remove most of the existing platform data since it is not generic
across all chips - see "remove unnecessary platform data" and "read screen
config from chip" in that series of patches. It's high priority on my list
to implement device tree support for the remaining items, which will be
just irqflags and the touchpad key config.

^ permalink raw reply

* Re: [PATCH] Input: i8042-io - Exclude mips platforms when allocating/deallocating IO regions.
From: Ralf Baechle @ 2014-03-19 20:49 UTC (permalink / raw)
  To: Raghu Gandham
  Cc: Aaro Koskinen, Dmitry Torokhov, linux-input@vger.kernel.org,
	linux-mips@linux-mips.org, linux-kernel@vger.kernel.org,
	Thomas Bogendoerfer
In-Reply-To: <E2EE47005FA75F44B80E1019FDD2EBBB6E394CB2@BADAG02.ba.imgtec.org>

On Tue, Jan 28, 2014 at 06:25:39AM +0000, Raghu Gandham wrote:
> Date:   Tue, 28 Jan 2014 06:25:39 +0000
> From: Raghu Gandham <Raghu.Gandham@imgtec.com>
> To: Aaro Koskinen <aaro.koskinen@iki.fi>, Dmitry Torokhov
>  <dmitry.torokhov@gmail.com>
> CC: "linux-input@vger.kernel.org" <linux-input@vger.kernel.org>,
>  "linux-mips@linux-mips.org" <linux-mips@linux-mips.org>,
>  "linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
> Subject: RE: [PATCH] Input: i8042-io - Exclude mips platforms when
>  allocating/deallocating IO regions.
> Content-Type: text/plain; charset="us-ascii"
> 
> Hi Aaro,
> 
> > 
> > On Sun, Jan 26, 2014 at 10:56:38PM -0800, Dmitry Torokhov wrote:
> > > On Mon, Jan 27, 2014 at 12:32:36AM +0000, Raghu Gandham wrote:
> > > > > On Sat, Jan 25, 2014 at 11:01:54AM -0800, Raghu Gandham wrote:
> > > > > > The standard IO regions are already reserved by the platform
> > > > > > code on most MIPS devices(malta, cobalt, sni). The Commit
> > > > > > 197a1e96c8be5b6005145af3a4c0e45e2d651444
> > > > > > ("Input: i8042-io - fix up region handling on MIPS") introduced
> > > > > > a bug on these MIPS platforms causing i8042 driver to fail when
> > > > > > trying to reserve IO ports.
> > > > > > Prior to the above mentioned commit request_region is skipped on
> > > > > > MIPS but release_region is called.
> > > > > >
> > > > > > This patch reverts commit
> > > > > > 197a1e96c8be5b6005145af3a4c0e45e2d651444
> > > > > > and also avoids calling release_region for MIPS.
> > > > >
> > > > > The problem is that IO regions are reserved on _most_, but not
> > > > > _all_ devices.
> > > > > MIPS should figure out what they want to do with i8042 registers
> > > > > and be consistent on all devices.
> > > >
> > > > Please examine the attached patch which went upstream in April of 2004.
> > > > Since then it had been a convention not to call request_region
> > > > routine in
> > > > i8042 for MIPS. The attached patch had a glitch that it guarded
> > > > request_region in i8042-io.h but skipped guarding release_region in
> > > > i8042-io.h. I believe that the issue Aaro saw was due to this
> > > > glitch. Below is the error quoted in Aaro's commit message.
> > > >
> > > >     [    2.112000] Trying to free nonexistent resource <0000000000000060-
> > 000000000000006f>
> > > >
> > > > My patch reinstates the convention followed on MIPS devices along
> > > > with fixing Aaro's issue.
> > >
> > > I assume that Aaro did test his patch and on his box request_region()
> > > succeeds. That would indicate that various MIPS sub-arches still not
> > > settled on the topic.
> > 
> > request_region() succeeds on Loongson and OCTEON.
> 
> This would mean that before your patch in oct of 2012, Loongson and Octeon 
> were not reserving the IO space for i8042.
> 
> > 
> > On OCTEONs without PCI, request_region() will fail which is correct as there
> > is no I/O space.
> > 
> > I wasn't aware of that 2004 patch (it pre-dates GIT history of mainline Linux).
> > Why the regions are already reserved by the platform code?
> 
> The only information I have is the comment before request_region in i8042-io.h that
> touching data register on some platforms is flaky.  If your patch was primarily aimed at
> addressing the error message from release_region, the current patch I uploaded should
> also take care of it too. 

I think the patch (http://patchwork.linux-mips.org/patch/6419/) should be
applied.  The argumentation for reserving ports in the platform code are
the same as on x86 - touch the registers and bad things may happen.  This
is because a fair number of older MIPS platforms were based on x86 chipsets
or at least are using very similar designs.

The fact that on certain platforms such as Loongson, some Octeon systems
and others the request_region() call to reserve the keyboard call succeeds
is by accident not design.

I wish i8042.c was a real platform driver, not using platform_create_bundle.
That would leave a natural place in the arch/platform code to deal with
I/O port allocation and platform_device creation, as necessary for a
platform.

  Ralf

^ permalink raw reply

* [PATCH] HID: multitouch: add support for Win 8.1 multitouch touchpads
From: Andrew Duggan @ 2014-03-19 20:39 UTC (permalink / raw)
  To: benjamin.tissoires, linux-input, jkosina, rydberg; +Cc: Andrew Duggan

Multitouch touchpads built for Win 8.1 need to be sent an input mode feature report
in order to start reporting multitouch events. This is the same process sent
to Win 7 multitouch touchscreens except the value of the feature report is 3 for
touchpads.

Signed-off-by: Andrew Duggan <aduggan@synaptics.com>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/hid-multitouch.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 221d503..b428a3d 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -68,6 +68,9 @@ MODULE_LICENSE("GPL");
 #define MT_QUIRK_HOVERING		(1 << 11)
 #define MT_QUIRK_CONTACT_CNT_ACCURATE	(1 << 12)
 
+#define MT_INPUTMODE_TOUCHSCREEN	0x02
+#define MT_INPUTMODE_TOUCHPAD		0x03
+
 struct mt_slot {
 	__s32 x, y, cx, cy, p, w, h;
 	__s32 contactid;	/* the device ContactID assigned to this slot */
@@ -105,6 +108,7 @@ struct mt_device {
 	__s16 inputmode_index;	/* InputMode HID feature index in the report */
 	__s16 maxcontact_report_id;	/* Maximum Contact Number HID feature,
 				   -1 if non-existent */
+	__u8 inputmode_value;  /* InputMode HID feature value */
 	__u8 num_received;	/* how many contacts we received */
 	__u8 num_expected;	/* expected last contact index */
 	__u8 maxcontacts;
@@ -415,8 +419,10 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 	 * Model touchscreens providing buttons as touchpads.
 	 */
 	if (field->application == HID_DG_TOUCHPAD ||
-	    (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
+	    (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
 		td->mt_flags |= INPUT_MT_POINTER;
+		td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
+	}
 
 	if (usage->usage_index)
 		prev_usage = &field->usage[usage->usage_index - 1];
@@ -841,7 +847,7 @@ static void mt_set_input_mode(struct hid_device *hdev)
 	re = &(hdev->report_enum[HID_FEATURE_REPORT]);
 	r = re->report_id_hash[td->inputmode];
 	if (r) {
-		r->field[0]->value[td->inputmode_index] = 0x02;
+		r->field[0]->value[td->inputmode_index] = td->inputmode_value;
 		hid_hw_request(hdev, r, HID_REQ_SET_REPORT);
 	}
 }
@@ -973,6 +979,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	td->mtclass = *mtclass;
 	td->inputmode = -1;
 	td->maxcontact_report_id = -1;
+	td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
 	td->cc_index = -1;
 	td->mt_report_id = -1;
 	td->pen_report_id = -1;
-- 
1.8.3.2


^ permalink raw reply related

* Re: [PATCH] Input: i8042-io - Exclude mips platforms when allocating/deallocating IO regions.
From: Aaro Koskinen @ 2014-03-19 23:44 UTC (permalink / raw)
  To: Ralf Baechle
  Cc: Raghu Gandham, Dmitry Torokhov, linux-input@vger.kernel.org,
	linux-mips@linux-mips.org, linux-kernel@vger.kernel.org,
	Thomas Bogendoerfer
In-Reply-To: <20140319204931.GI17197@linux-mips.org>

Hi,

On Wed, Mar 19, 2014 at 09:49:31PM +0100, Ralf Baechle wrote:
> I think the patch (http://patchwork.linux-mips.org/patch/6419/) should be
> applied.  The argumentation for reserving ports in the platform code are
> the same as on x86 - touch the registers and bad things may happen.  This
> is because a fair number of older MIPS platforms were based on x86 chipsets
> or at least are using very similar designs.

If you drop the request_region() from the driver, it will try to probe
anyway regardless what the platform code codes. So bad things could happen,
no?

Currently we can prevent i8042 driver from probing I/O space on PCI-less
Octeons (for example), because we define empty I/O space so request_region()
by driver will fail. So we can actually prevent bad things from happening.
I would call this good design, not an accident.

Maybe I'm missing something? Anyway, I don't have strong feelings whether
this patch is applied or not. My computers will keep on working on either
case.

A.

^ permalink raw reply

* RE: [PATCHv4 1/5] Input: edt-ft5x06: several cleanups; no functional change
From: fugang.duan-KZfg59tc24xl57MIdRCFDg @ 2014-03-20  2:42 UTC (permalink / raw)
  To: Lothar Waßmann, Dmitry Torokhov, Grant Likely,
	Henrik Rydberg, Ian Campbell, Jingoo Han, Kumar Gala,
	Mark Rutland, Pawel Moll, Rob Herring, Rob Landley, Sachin Kamat,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-doc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Simon Budig
In-Reply-To: <1395234563-11034-2-git-send-email-LW-bxm8fMRDkQLDiMYJYoSAnRvVK+yQ3ZXh@public.gmane.org>

From: Lothar Waßmann <LW@KARO-electronics.de>
Data: Wednesday, March 19, 2014 9:09 PM

>To: Dmitry Torokhov; Duan Fugang-B38611; Grant Likely; Henrik Rydberg; Ian
>Campbell; Jingoo Han; Kumar Gala; Mark Rutland; Pawel Moll; Rob Herring; Rob
>Landley; Sachin Kamat; devicetree@vger.kernel.org; linux-doc@vger.kernel.org;
>linux-input@vger.kernel.org; linux-kernel@vger.kernel.org; Simon Budig; Lothar
>Waßmann
>Subject: [PATCHv4 1/5] Input: edt-ft5x06: several cleanups; no functional
>change
>
>- remove redundant parens
>- remove redundant type casts
>- fix mixed tab/space indentation
>
>Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
>---
> drivers/input/touchscreen/edt-ft5x06.c |   12 ++++++------
> 1 file changed, 6 insertions(+), 6 deletions(-)
>
>diff --git a/drivers/input/touchscreen/edt-ft5x06.c
>b/drivers/input/touchscreen/edt-ft5x06.c
>index 412a85e..7b4470d 100644
>--- a/drivers/input/touchscreen/edt-ft5x06.c
>+++ b/drivers/input/touchscreen/edt-ft5x06.c
>@@ -173,7 +173,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
> 		x = ((buf[0] << 8) | buf[1]) & 0x0fff;
> 		y = ((buf[2] << 8) | buf[3]) & 0x0fff;
> 		id = (buf[2] >> 4) & 0x0f;
>-		down = (type != TOUCH_EVENT_UP);
>+		down = type != TOUCH_EVENT_UP;
>
> 		input_mt_slot(tsdata->input, id);
> 		input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down);
>@@ -257,7 +257,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
> 	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
> 	struct edt_ft5x06_attribute *attr =
> 			container_of(dattr, struct edt_ft5x06_attribute, dattr);
>-	u8 *field = (u8 *)((char *)tsdata + attr->field_offset);
>+	u8 *field = (u8 *)tsdata + attr->field_offset;
> 	int val;
> 	size_t count = 0;
> 	int error = 0;
>@@ -299,7 +299,7 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
> 	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
> 	struct edt_ft5x06_attribute *attr =
> 			container_of(dattr, struct edt_ft5x06_attribute, dattr);
>-	u8 *field = (u8 *)((char *)tsdata + attr->field_offset);
>+	u8 *field = (u8 *)tsdata + attr->field_offset;
> 	unsigned int val;
> 	int error;
>
>@@ -479,7 +479,7 @@ static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode)
>
> 	if (mode != tsdata->factory_mode) {
> 		retval = mode ? edt_ft5x06_factory_mode(tsdata) :
>-			        edt_ft5x06_work_mode(tsdata);
>+				edt_ft5x06_work_mode(tsdata);
> 	}
>
> 	mutex_unlock(&tsdata->mutex);
>@@ -852,8 +852,8 @@ static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
> 			 edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume);
>
> static const struct i2c_device_id edt_ft5x06_ts_id[] = {
>-	{ "edt-ft5x06", 0 },
>-	{ }
>+	{ "edt-ft5x06", 0, },
>+	{ /* sentinel */ }
> };
> MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
>
>--
>1.7.10.4
>
>

Acked-by: Fugang Duan <B38611@freescale.com>

^ permalink raw reply

* RE: [PATCHv4 2/5] Input: edt-ft5x06: Add DT support
From: fugang.duan @ 2014-03-20  5:19 UTC (permalink / raw)
  To: Lothar Waßmann, Dmitry Torokhov, Grant Likely,
	Henrik Rydberg, Ian Campbell, Jingoo Han, Kumar Gala,
	Mark Rutland, Pawel Moll, Rob Herring, Rob Landley, Sachin Kamat,
	devicetree@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	Simon Budig
In-Reply-To: <1395234563-11034-3-git-send-email-LW@KARO-electronics.de>

From: Lothar Waßmann <LW@KARO-electronics.de>
Data: Wednesday, March 19, 2014 9:09 PM

>To: Dmitry Torokhov; Duan Fugang-B38611; Grant Likely; Henrik Rydberg; Ian
>Campbell; Jingoo Han; Kumar Gala; Mark Rutland; Pawel Moll; Rob Herring; Rob
>Landley; Sachin Kamat; devicetree@vger.kernel.org; linux-doc@vger.kernel.org;
>linux-input@vger.kernel.org; linux-kernel@vger.kernel.org; Simon Budig; Lothar
>Waßmann
>Subject: [PATCHv4 2/5] Input: edt-ft5x06: Add DT support
>
>
>Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
>---
> .../bindings/input/touchscreen/edt-ft5x06.txt      |   41 ++++++
> drivers/input/touchscreen/edt-ft5x06.c             |  144 +++++++++++++++-----
> 2 files changed, 154 insertions(+), 31 deletions(-)  create mode 100644
>Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
>
>diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
>b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
>new file mode 100644
>index 0000000..e5adc76
>--- /dev/null
>+++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
>@@ -0,0 +1,41 @@
>+FocalTech EDT-FT5x06 Polytouch driver
>+=====================================
>+
>+Required properties:
>+ - compatible:  "edt,edt-ft5x06"
>+ - reg:         I2C slave address of the chip (0x38)
>+ - interrupt-parent: a phandle pointing to the interrupt controller
>+                     serving the interrupt for this chip
>+ - interrupts:       interrupt specification for this chip
>+
>+Optional properties:
>+ - reset-gpios: GPIO specification for the RESET input
>+ - wake-gpios:  GPIO specification for the WAKE input
>+
>+ - pinctrl-names: should be "default"
>+ - pinctrl-0:   a phandle pointing to the pin settings for the
>+                control gpios
>+
>+ - threshold:   allows setting the "click"-threshold in the range
>+                from 20 to 80.
>+
>+ - gain:        allows setting the sensitivity in the range from 0 to
>+                31. Note that lower values indicate higher
>+                sensitivity.
>+
>+ - offset:      allows setting the edge compensation in the range from
>+                0 to 31.
>+ - report_rate: allows setting the report rate in the range from 3 to
>+                14.
>+
>+Example:
>+	polytouch: edt-ft5x06@38 {
>+		compatible = "edt,edt-ft5x06";
>+		reg = <0x38>;
>+		pinctrl-names = "default";
>+		pinctrl-0 = <&edt_ft5x06_pins>;
>+		interrupt-parent = <&gpio2>;
>+		interrupts = <5 0>;
>+		reset-gpios = <&gpio2 6 1>;
>+		wake-gpios = <&gpio4 9 0>;
>+	};
>diff --git a/drivers/input/touchscreen/edt-ft5x06.c
>b/drivers/input/touchscreen/edt-ft5x06.c
>index 7b4470d..257a1c8 100644
>--- a/drivers/input/touchscreen/edt-ft5x06.c
>+++ b/drivers/input/touchscreen/edt-ft5x06.c
>@@ -33,6 +33,7 @@
> #include <linux/debugfs.h>
> #include <linux/slab.h>
> #include <linux/gpio.h>
>+#include <linux/of_gpio.h>
> #include <linux/input/mt.h>
> #include <linux/input/edt-ft5x06.h>
>
[...]
>+#ifdef CONFIG_OF
>+static int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
>+				struct edt_ft5x06_ts_data *tsdata)
>+{
>+	struct device_node *np = dev->of_node;
>+
>+	if (!np)
>+		return -ENODEV;
Don't need to check the device node valid. If the device node is not existed, the driver don't run probe.

>+
>+	/*
>+	 * irq_pin is not needed for DT setup.
>+	 * irq is associated via 'interrupts' property in DT
>+	 */
>+	tsdata->irq_pin = -EINVAL;
>+	tsdata->reset_pin = of_get_named_gpio(np, "reset-gpios", 0);
>+	tsdata->wake_pin = of_get_named_gpio(np, "wake-gpios", 0);
>+
>+	return 0;
>+}
>+#else
>+static inline int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
>+					struct edt_ft5x06_i2c_ts_data *tsdata) {
>+	return -ENODEV;
>+}
>+#endif
>+
> static int edt_ft5x06_ts_probe(struct i2c_client *client,
> 					 const struct i2c_device_id *id)
> {
>@@ -714,32 +775,40 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
>
> 	dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
>
>+	tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
>+	if (!tsdata) {
>+		dev_err(&client->dev, "failed to allocate driver data.\n");
>+		return -ENOMEM;
>+	}
>+
> 	if (!pdata) {
>-		dev_err(&client->dev, "no platform data?\n");
>-		return -EINVAL;
>+		error = edt_ft5x06_i2c_ts_probe_dt(&client->dev, tsdata);
>+		if (error) {
>+			dev_err(&client->dev,
>+				"DT probe failed and no platform data present\n");
>+			return error;
>+		}
>+	} else {
>+		tsdata->reset_pin = pdata->reset_pin;
>+		tsdata->irq_pin = pdata->irq_pin;
>+		tsdata->wake_pin = -EINVAL;
> 	}
[...]

Thanks,
Andy

^ permalink raw reply

* RE: [PATCHv4 4/5] Input: edt-ft5x06: Ignore touchdown events
From: fugang.duan @ 2014-03-20  5:22 UTC (permalink / raw)
  To: Lothar Waßmann, Dmitry Torokhov, Grant Likely,
	Henrik Rydberg, Ian Campbell, Jingoo Han, Kumar Gala,
	Mark Rutland, Pawel Moll, Rob Herring, Rob Landley, Sachin Kamat,
	devicetree@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	Simon Budig
In-Reply-To: <1395234563-11034-5-git-send-email-LW@KARO-electronics.de>

From: Lothar Waßmann <LW@KARO-electronics.de>
Data: Wednesday, March 19, 2014 9:09 PM

>To: Dmitry Torokhov; Duan Fugang-B38611; Grant Likely; Henrik Rydberg; Ian
>Campbell; Jingoo Han; Kumar Gala; Mark Rutland; Pawel Moll; Rob Herring; Rob
>Landley; Sachin Kamat; devicetree@vger.kernel.org; linux-doc@vger.kernel.org;
>linux-input@vger.kernel.org; linux-kernel@vger.kernel.org; Simon Budig; Lothar
>Waßmann
>Subject: [PATCHv4 4/5] Input: edt-ft5x06: Ignore touchdown events
>
>The chip may report invalid coordinates on touchdown events, so don't report
>the initial touchdown event.
>
>Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
>---
> drivers/input/touchscreen/edt-ft5x06.c |    4 ++++
> 1 file changed, 4 insertions(+)
>
>diff --git a/drivers/input/touchscreen/edt-ft5x06.c
>b/drivers/input/touchscreen/edt-ft5x06.c
>index 27dccfc..af736e4 100644
>--- a/drivers/input/touchscreen/edt-ft5x06.c
>+++ b/drivers/input/touchscreen/edt-ft5x06.c
>@@ -175,6 +175,10 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
> 		if (type == TOUCH_EVENT_RESERVED)
> 			continue;
>
>+		/* ignore TOUCH_DOWN events, might have bogus coordinates */
>+		if (type == TOUCH_EVENT_DOWN)
>+			continue;
>+
> 		x = ((buf[0] << 8) | buf[1]) & 0x0fff;
> 		y = ((buf[2] << 8) | buf[3]) & 0x0fff;
> 		id = (buf[2] >> 4) & 0x0f;
>--
>1.7.10.4
>
>

Acked-by: Fugang Duan <B38611@freescale.com>

^ permalink raw reply

* Re: [PATCH] HID: multitouch: add support for Win 8.1 multitouch touchpads
From: Jiri Kosina @ 2014-03-20  9:02 UTC (permalink / raw)
  To: Andrew Duggan; +Cc: benjamin.tissoires, linux-input, rydberg
In-Reply-To: <1395261543-3103-1-git-send-email-aduggan@synaptics.com>

On Wed, 19 Mar 2014, Andrew Duggan wrote:

> Multitouch touchpads built for Win 8.1 need to be sent an input mode feature report
> in order to start reporting multitouch events. This is the same process sent
> to Win 7 multitouch touchscreens except the value of the feature report is 3 for
> touchpads.
> 
> Signed-off-by: Andrew Duggan <aduggan@synaptics.com>
> Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

Applied, thanks.

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox