linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan
@ 2017-01-10 16:11 Benjamin Tissoires
  2017-01-10 16:11 ` [PATCH 01/10] Input: serio - store the pt_buttons in the struct serio directly Benjamin Tissoires
                   ` (10 more replies)
  0 siblings, 11 replies; 21+ messages in thread
From: Benjamin Tissoires @ 2017-01-10 16:11 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

Hi,

Well, this is the last series which enables RMI4 over SMBus for the Thinkpad
t*40, t*50, t*60 series. Few comments on the patches:

patches 1 to 3 allows the re-routing of the trackstick buttons from the touchpad
  to the trackstick
patch 4 to 7 allow rmi_smbus to actually control the PS/2 node to not resume and
  mess up the configuration after a resume
patch 8 was originally sent as a RMI4 specific driver, but I came to realise
  that Elan could also benefit from the same driver
patch 9 has already been sent once, but there was no clear way of enabling the
  trackstick, so it was not merged
patch 10 allows the elantech driver to create an instance of elan_i2c and
  forwards the trackstick information from PS/2

Note that patch 10 has only been tested on a machine without a functional
elan_i2c touchpad, so only the binding has been tested.

This series requires the Host Notify flag patch from Dmitry:
http://patchwork.ozlabs.org/patch/711229/

Cheers,
Benjamin

Benjamin Tissoires (10):
  Input: serio - store the pt_buttons in the struct serio directly
  Input: synaptics-rmi4 - Add rmi_find_function()
  Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on
    buttonpads to PS/2 guest
  Input: psmouse - allow to deactivate a driver from the serio handle
  Input: synaptics - allocate a Synaptics Intertouch device
  Input: synaptics-rmi4 - smbus: call psmouse_deactivate before
    binding/resume
  Input: synaptics-rmi4 - smbus: on resume, try 3 times if init fails
  Input: add a PS/2 to SMBus platform module
  Input: elan_i2c - add trackstick report
  Input: elantech - automatically bind an SMBus device when acceptable

 drivers/input/misc/Kconfig          |  11 ++
 drivers/input/misc/Makefile         |   1 +
 drivers/input/misc/ps2_smbus.c      | 276 ++++++++++++++++++++++++++++++++++++
 drivers/input/mouse/elan_i2c.h      |  12 ++
 drivers/input/mouse/elan_i2c_core.c |  99 ++++++++++++-
 drivers/input/mouse/elantech.c      |  54 +++++++
 drivers/input/mouse/elantech.h      |   3 +
 drivers/input/mouse/psmouse-base.c  |  34 +++++
 drivers/input/mouse/psmouse.h       |   3 +
 drivers/input/mouse/synaptics.c     | 134 +++++++++++++++--
 drivers/input/mouse/synaptics.h     |   5 +-
 drivers/input/rmi4/Kconfig          |   1 +
 drivers/input/rmi4/rmi_driver.c     |  13 ++
 drivers/input/rmi4/rmi_driver.h     |  15 ++
 drivers/input/rmi4/rmi_f03.c        |  30 ++++
 drivers/input/rmi4/rmi_f30.c        |  72 ++++++++--
 drivers/input/rmi4/rmi_smbus.c      |  47 ++++--
 include/linux/rmi.h                 |   2 +
 include/linux/serio.h               |  22 +++
 19 files changed, 795 insertions(+), 39 deletions(-)
 create mode 100644 drivers/input/misc/ps2_smbus.c

-- 
2.9.3

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 01/10] Input: serio - store the pt_buttons in the struct serio directly
  2017-01-10 16:11 [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
@ 2017-01-10 16:11 ` Benjamin Tissoires
  2017-01-10 16:11 ` [PATCH 02/10] Input: synaptics-rmi4 - Add rmi_find_function() Benjamin Tissoires
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 21+ messages in thread
From: Benjamin Tissoires @ 2017-01-10 16:11 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

With RMI4 over SMBus, the pass-through device can be instantiated
in a SMBus driver. However, compared to the psmouse-synaptics driver,
this pass-through PS/2 driver has no clue whether the current
serio_interrupt() is the beginning of the frame or not. Instead of
adding a protocol analysis in RMI4 function F03, we can add an extra
byte in struct serio to handle the extra data we want to append to the
first byte.

Convert the psmouse-synaptics device to use it too.

Partially reverts cdd9dc1 ("Input: synaptics - re-route tracksticks
buttons on the Lenovo 2015 series")

Acked-by: Andrew Duggan <aduggan@synaptics.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/input/mouse/psmouse-base.c |  3 +++
 drivers/input/mouse/synaptics.c    | 19 ++++++++++---------
 drivers/input/mouse/synaptics.h    |  1 -
 include/linux/serio.h              |  8 ++++++++
 4 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index bee2674..c822d73 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -365,6 +365,9 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
 		goto out;
 	}
 
+	if (psmouse->pktcnt == 1)
+		psmouse->packet[0] |= serio->extra_byte;
+
 	psmouse->last = jiffies;
 	psmouse_handle_byte(psmouse);
 
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index a41d832..8781e23 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -597,15 +597,13 @@ static int synaptics_is_pt_packet(unsigned char *buf)
 	return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
 }
 
-static void synaptics_pass_pt_packet(struct psmouse *psmouse,
-				     struct serio *ptport,
+static void synaptics_pass_pt_packet(struct serio *ptport,
 				     unsigned char *packet)
 {
-	struct synaptics_data *priv = psmouse->private;
 	struct psmouse *child = serio_get_drvdata(ptport);
 
 	if (child && child->state == PSMOUSE_ACTIVATED) {
-		serio_interrupt(ptport, packet[1] | priv->pt_buttons, 0);
+		serio_interrupt(ptport, packet[1], 0);
 		serio_interrupt(ptport, packet[4], 0);
 		serio_interrupt(ptport, packet[5], 0);
 		if (child->pktsize == 4)
@@ -857,6 +855,7 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse,
 	struct synaptics_data *priv = psmouse->private;
 	int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
 	char buf[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	int pt_buttons;
 	int i;
 
 	if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
@@ -887,11 +886,13 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse,
 		return;
 
 	/* The trackstick expects at most 3 buttons */
-	priv->pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons)      |
-			   SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
-			   SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
+	pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons)      |
+		     SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
+		     SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
+
+	priv->pt_port->extra_byte = pt_buttons;
 
-	synaptics_pass_pt_packet(psmouse, priv->pt_port, buf);
+	synaptics_pass_pt_packet(priv->pt_port, buf);
 }
 
 static void synaptics_report_buttons(struct psmouse *psmouse,
@@ -1132,7 +1133,7 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
 		if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
 		    synaptics_is_pt_packet(psmouse->packet)) {
 			if (priv->pt_port)
-				synaptics_pass_pt_packet(psmouse, priv->pt_port,
+				synaptics_pass_pt_packet(priv->pt_port,
 							 psmouse->packet);
 		} else
 			synaptics_process_packet(psmouse);
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 56faa7e..116ae25 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -183,7 +183,6 @@ struct synaptics_data {
 	bool disable_gesture;			/* disable gestures */
 
 	struct serio *pt_port;			/* Pass-through serio port */
-	unsigned char pt_buttons;		/* Pass-through buttons */
 
 	/*
 	 * Last received Advanced Gesture Mode (AGM) packet. An AGM packet
diff --git a/include/linux/serio.h b/include/linux/serio.h
index c733cff..f3b75c8 100644
--- a/include/linux/serio.h
+++ b/include/linux/serio.h
@@ -64,6 +64,14 @@ struct serio {
 	 * may get indigestion when exposed to concurrent access (i8042).
 	 */
 	struct mutex *ps2_cmd_mutex;
+
+	/*
+	 * For use with Synaptics devices that have the trackstick buttons
+	 * not actually wired to the trackstick PS/2 device.
+	 * This byte will be OR-ed with the first byte of the incoming packet
+	 * that contains actual data (not commands).
+	 */
+	u8 extra_byte;
 };
 #define to_serio_port(d)	container_of(d, struct serio, dev)
 
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 02/10] Input: synaptics-rmi4 - Add rmi_find_function()
  2017-01-10 16:11 [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
  2017-01-10 16:11 ` [PATCH 01/10] Input: serio - store the pt_buttons in the struct serio directly Benjamin Tissoires
@ 2017-01-10 16:11 ` Benjamin Tissoires
  2017-02-06 19:28   ` Dmitry Torokhov
  2017-01-10 16:11 ` [PATCH 03/10] Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on buttonpads to PS/2 guest Benjamin Tissoires
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 21+ messages in thread
From: Benjamin Tissoires @ 2017-01-10 16:11 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

If a function needs to communicate with an other, it's better to have
a way to retrieve this other.

Reviewed-by: Andrew Duggan <aduggan@synaptics.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/input/rmi4/rmi_driver.c | 13 +++++++++++++
 drivers/input/rmi4/rmi_driver.h |  1 +
 2 files changed, 14 insertions(+)

diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 11447ab..87dbb0c 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -265,6 +265,19 @@ static int rmi_irq_init(struct rmi_device *rmi_dev)
 	return 0;
 }
 
+struct rmi_function *rmi_find_function(struct rmi_device *rmi_dev, u8 number)
+{
+	struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+	struct rmi_function *entry;
+
+	list_for_each_entry(entry, &data->function_list, node) {
+		if (entry->fd.function_number == number)
+			return entry;
+	}
+
+	return NULL;
+}
+
 static int suspend_one_function(struct rmi_function *fn)
 {
 	struct rmi_function_handler *fh;
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 24f8f76..0506371 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -93,6 +93,7 @@ bool rmi_is_physical_driver(struct device_driver *);
 int rmi_register_physical_driver(void);
 void rmi_unregister_physical_driver(void);
 void rmi_free_function_list(struct rmi_device *rmi_dev);
+struct rmi_function *rmi_find_function(struct rmi_device *rmi_dev, u8 number);
 int rmi_enable_sensor(struct rmi_device *rmi_dev);
 int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
 		 int (*callback)(struct rmi_device *rmi_dev, void *ctx,
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 03/10] Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on buttonpads to PS/2 guest
  2017-01-10 16:11 [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
  2017-01-10 16:11 ` [PATCH 01/10] Input: serio - store the pt_buttons in the struct serio directly Benjamin Tissoires
  2017-01-10 16:11 ` [PATCH 02/10] Input: synaptics-rmi4 - Add rmi_find_function() Benjamin Tissoires
@ 2017-01-10 16:11 ` Benjamin Tissoires
  2017-02-06 19:23   ` Dmitry Torokhov
  2017-01-10 16:11 ` [PATCH 04/10] Input: psmouse - allow to deactivate a driver from the serio handle Benjamin Tissoires
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 21+ messages in thread
From: Benjamin Tissoires @ 2017-01-10 16:11 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

On the latest series of ThinkPads, the button events for the TrackPoint
are reported through the touchpad itself as opposed to the TrackPoint
device. In order to report these buttons properly, we need to forward
them to the TrackPoint device and send the button presses/releases
through there instead.

Signed-off-by: Lyude Paul <thatslyude@gmail.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/input/rmi4/rmi_driver.h | 14 ++++++++
 drivers/input/rmi4/rmi_f03.c    | 30 +++++++++++++++++
 drivers/input/rmi4/rmi_f30.c    | 72 ++++++++++++++++++++++++++++++++---------
 3 files changed, 101 insertions(+), 15 deletions(-)

diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 0506371..66b935f 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -107,7 +107,21 @@ int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx,
 
 char *rmi_f01_get_product_ID(struct rmi_function *fn);
 
+#ifdef CONFIG_RMI4_F03
+int rmi_f03_overwrite_button(struct rmi_function *fn, unsigned int button,
+			     int value);
+void rmi_f03_commit_buttons(struct rmi_function *fn);
+#else
+static inline int rmi_f03_overwrite_button(struct rmi_function *fn,
+					   unsigned int button, int value)
+{
+	return 0;
+}
+static inline void rmi_f03_commit_buttons(struct rmi_function *fn) {}
+#endif
+
 #ifdef CONFIG_RMI4_F34
+
 int rmi_f34_create_sysfs(struct rmi_device *rmi_dev);
 void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev);
 #else
diff --git a/drivers/input/rmi4/rmi_f03.c b/drivers/input/rmi4/rmi_f03.c
index 8a7ca3e..26f6edd 100644
--- a/drivers/input/rmi4/rmi_f03.c
+++ b/drivers/input/rmi4/rmi_f03.c
@@ -31,10 +31,40 @@ struct f03_data {
 
 	struct serio *serio;
 
+	unsigned int overwrite_buttons;
+
 	u8 device_count;
 	u8 rx_queue_length;
 };
 
+int rmi_f03_overwrite_button(struct rmi_function *fn, unsigned int button,
+			     int value)
+{
+	struct f03_data *f03 = dev_get_drvdata(&fn->dev);
+	unsigned int bit = BIT(button);
+
+	if (button > 2)
+		return -EINVAL;
+
+	if (value)
+		f03->overwrite_buttons |= bit;
+	else
+		f03->overwrite_buttons &= ~bit;
+
+	return 0;
+}
+
+void rmi_f03_commit_buttons(struct rmi_function *fn)
+{
+	struct f03_data *f03 = dev_get_drvdata(&fn->dev);
+	int i;
+
+	f03->serio->extra_byte = f03->overwrite_buttons;
+
+	for (i = 0; i < 3; i++)
+		serio_interrupt(f03->serio, 0x00, 0x00);
+}
+
 static int rmi_f03_pt_write(struct serio *id, unsigned char val)
 {
 	struct f03_data *f03 = id->port_data;
diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c
index f4b491e..608c5ee 100644
--- a/drivers/input/rmi4/rmi_f30.c
+++ b/drivers/input/rmi4/rmi_f30.c
@@ -74,8 +74,11 @@ struct f30_data {
 
 	u8 data_regs[RMI_F30_CTRL_MAX_BYTES];
 	u16 *gpioled_key_map;
+	u16 *gpio_passthrough_key_map;
 
 	struct input_dev *input;
+	bool trackstick_buttons;
+	struct rmi_function *f03;
 };
 
 static int rmi_f30_read_control_parameters(struct rmi_function *fn,
@@ -109,6 +112,13 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits)
 	if (!f30->input)
 		return 0;
 
+	if (f30->trackstick_buttons && !f30->f03) {
+		f30->f03 = rmi_find_function(rmi_dev, 3);
+
+		if (!f30->f03)
+			return -EBUSY;
+	}
+
 	/* Read the gpi led data. */
 	if (drvdata->attn_data.data) {
 		if (drvdata->attn_data.size < f30->register_count) {
@@ -133,23 +143,29 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits)
 	for (reg_num = 0; reg_num < f30->register_count; ++reg_num) {
 		for (i = 0; gpiled < f30->gpioled_count && i < 8; ++i,
 			++gpiled) {
-			if (f30->gpioled_key_map[gpiled] != 0) {
-				/* buttons have pull up resistors */
-				value = (((f30->data_regs[reg_num] >> i) & 0x01)
-									== 0);
+			/* buttons have pull up resistors */
+			value = (((f30->data_regs[reg_num] >> i) & 0x01) == 0);
 
+			if (f30->gpioled_key_map[gpiled] != 0) {
 				rmi_dbg(RMI_DEBUG_FN, &fn->dev,
 					"%s: call input report key (0x%04x) value (0x%02x)",
 					__func__,
 					f30->gpioled_key_map[gpiled], value);
+
 				input_report_key(f30->input,
 						 f30->gpioled_key_map[gpiled],
 						 value);
+			} else if (f30->gpio_passthrough_key_map[gpiled]) {
+				rmi_f03_overwrite_button(f30->f03,
+						f30->gpio_passthrough_key_map[gpiled] - BTN_LEFT,
+						value);
 			}
-
 		}
 	}
 
+	if (f30->trackstick_buttons)
+		rmi_f03_commit_buttons(f30->f03);
+
 	return 0;
 }
 
@@ -247,10 +263,11 @@ static inline int rmi_f30_initialize(struct rmi_function *fn)
 	int retval = 0;
 	int control_address;
 	int i;
-	int button;
+	int button, extra_button;
 	u8 buf[RMI_F30_QUERY_SIZE];
 	u8 *ctrl_reg;
-	u8 *map_memory;
+	u8 *map_memory, *pt_memory;
+	bool buttonpad;
 
 	f30 = devm_kzalloc(&fn->dev, sizeof(struct f30_data),
 			   GFP_KERNEL);
@@ -348,29 +365,54 @@ static inline int rmi_f30_initialize(struct rmi_function *fn)
 	map_memory = devm_kzalloc(&fn->dev,
 				  (f30->gpioled_count * (sizeof(u16))),
 				  GFP_KERNEL);
-	if (!map_memory) {
+	pt_memory = devm_kzalloc(&fn->dev,
+				 (f30->gpioled_count * (sizeof(u16))),
+				 GFP_KERNEL);
+	if (!map_memory || !pt_memory) {
 		dev_err(&fn->dev, "Failed to allocate gpioled map memory.\n");
 		return -ENOMEM;
 	}
 
 	f30->gpioled_key_map = (u16 *)map_memory;
+	f30->gpio_passthrough_key_map = (u16 *)pt_memory;
 
 	pdata = rmi_get_platform_data(rmi_dev);
 	if (f30->has_gpio) {
+		/*
+		 * buttonpad might be given by f30->has_mech_mouse_btns,
+		 * but I am not sure, so use only the pdata info
+		 */
+		buttonpad = pdata->f30_data.buttonpad;
+		f30->trackstick_buttons = pdata->f30_data.trackstick_buttons;
+
+		/*
+		 * For touchpads the buttons are mapped as:
+		 * - bit 0 = Left, bit 1 = right, bit 2 = middle / clickbutton
+		 * - 3, 4, 5 are extended buttons and
+		 * - 6 and 7 are other sorts of GPIOs
+		 */
 		button = BTN_LEFT;
-		for (i = 0; i < f30->gpioled_count; i++) {
+		extra_button = BTN_LEFT;
+		for (i = 0; i < f30->gpioled_count && i < 3; i++) {
 			if (rmi_f30_is_valid_button(i, f30->ctrl)) {
 				f30->gpioled_key_map[i] = button++;
 
-				/*
-				 * buttonpad might be given by
-				 * f30->has_mech_mouse_btns, but I am
-				 * not sure, so use only the pdata info
-				 */
-				if (pdata->f30_data.buttonpad)
+				if (buttonpad)
 					break;
 			}
 		}
+
+		if (f30->trackstick_buttons) {
+			for (i = 3; i < f30->gpioled_count && i < 6; i++) {
+				if (rmi_f30_is_valid_button(i, f30->ctrl))
+					f30->gpio_passthrough_key_map[i] = extra_button++;
+			}
+		} else if (!buttonpad) {
+			for (i = 3; i < f30->gpioled_count; i++) {
+				if (rmi_f30_is_valid_button(i, f30->ctrl))
+					f30->gpioled_key_map[i] = button++;
+			}
+		}
 	}
 
 	return 0;
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 04/10] Input: psmouse - allow to deactivate a driver from the serio handle
  2017-01-10 16:11 [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
                   ` (2 preceding siblings ...)
  2017-01-10 16:11 ` [PATCH 03/10] Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on buttonpads to PS/2 guest Benjamin Tissoires
@ 2017-01-10 16:11 ` Benjamin Tissoires
  2017-01-10 16:11 ` [PATCH 05/10] Input: synaptics - allocate a Synaptics Intertouch device Benjamin Tissoires
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 21+ messages in thread
From: Benjamin Tissoires @ 2017-01-10 16:11 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

The RMI4 over SMBus and PS/2 implementation of Synaptics touchpads are
mutually exclusive. We need a way to deactivate the touchpad from the PS/2
node and prevent it to not rebind itself during resume.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/input/mouse/psmouse-base.c | 31 +++++++++++++++++++++++++++++++
 drivers/input/mouse/psmouse.h      |  3 +++
 drivers/input/mouse/synaptics.c    | 15 +++++++++++++++
 include/linux/serio.h              | 14 ++++++++++++++
 4 files changed, 63 insertions(+)

diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index c822d73..7fe6f4e 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -936,6 +936,8 @@ static void psmouse_apply_defaults(struct psmouse *psmouse)
 	psmouse->reconnect = NULL;
 	psmouse->disconnect = NULL;
 	psmouse->cleanup = NULL;
+	psmouse->activate = NULL;
+	psmouse->deactivate = NULL;
 	psmouse->pt_activate = NULL;
 	psmouse->pt_deactivate = NULL;
 }
@@ -1603,6 +1605,9 @@ static int psmouse_reconnect(struct serio *serio)
 	unsigned char type;
 	int rc = -1;
 
+	if (psmouse->ignore_reconnect)
+		return 0;
+
 	mutex_lock(&psmouse_mutex);
 
 	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
@@ -1649,6 +1654,30 @@ static int psmouse_reconnect(struct serio *serio)
 	return rc;
 }
 
+static void psmouse_deactivate_cb(struct serio *serio)
+{
+	struct psmouse *psmouse = serio_get_drvdata(serio);
+
+	psmouse->ignore_reconnect = true;
+
+	if (psmouse->deactivate)
+		psmouse->deactivate(psmouse);
+
+	psmouse_deactivate(psmouse);
+}
+
+static void psmouse_activate_cb(struct serio *serio)
+{
+	struct psmouse *psmouse = serio_get_drvdata(serio);
+
+	psmouse->ignore_reconnect = false;
+
+	psmouse_activate(psmouse);
+
+	if (psmouse->activate)
+		psmouse->activate(psmouse);
+}
+
 static struct serio_device_id psmouse_serio_ids[] = {
 	{
 		.type	= SERIO_8042,
@@ -1678,6 +1707,8 @@ static struct serio_driver psmouse_drv = {
 	.reconnect	= psmouse_reconnect,
 	.disconnect	= psmouse_disconnect,
 	.cleanup	= psmouse_cleanup,
+	.deactivate	= psmouse_deactivate_cb,
+	.activate	= psmouse_activate_cb,
 };
 
 ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *devattr,
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index e0ca6cd..61d6277 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -78,9 +78,12 @@ struct psmouse {
 	void (*disconnect)(struct psmouse *psmouse);
 	void (*cleanup)(struct psmouse *psmouse);
 	int (*poll)(struct psmouse *psmouse);
+	void (*deactivate)(struct psmouse *psmouse);
+	void (*activate)(struct psmouse *psmouse);
 
 	void (*pt_activate)(struct psmouse *psmouse);
 	void (*pt_deactivate)(struct psmouse *psmouse);
+	bool ignore_reconnect;
 };
 
 enum psmouse_type {
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 8781e23..e977ab0 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -657,6 +657,19 @@ static void synaptics_pt_create(struct psmouse *psmouse)
 	serio_register_port(serio);
 }
 
+void synaptics_deactivate(struct psmouse *psmouse)
+{
+	serio_unregister_child_port(psmouse->ps2dev.serio);
+}
+
+void synaptics_activate(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+
+	if (SYN_CAP_PASS_THROUGH(priv->capabilities))
+		synaptics_pt_create(psmouse);
+}
+
 /*****************************************************************************
  *	Functions to interpret the absolute mode packets
  ****************************************************************************/
@@ -1518,6 +1531,8 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
 	psmouse->disconnect = synaptics_disconnect;
 	psmouse->reconnect = synaptics_reconnect;
 	psmouse->cleanup = synaptics_reset;
+	psmouse->activate = synaptics_activate;
+	psmouse->activate = synaptics_deactivate;
 	/* Synaptics can usually stay in sync without extra help */
 	psmouse->resync_time = 0;
 
diff --git a/include/linux/serio.h b/include/linux/serio.h
index f3b75c8..f07d663 100644
--- a/include/linux/serio.h
+++ b/include/linux/serio.h
@@ -87,6 +87,8 @@ struct serio_driver {
 	int  (*reconnect)(struct serio *);
 	void (*disconnect)(struct serio *);
 	void (*cleanup)(struct serio *);
+	void (*deactivate)(struct serio *);
+	void (*activate)(struct serio *);
 
 	struct device_driver driver;
 };
@@ -171,4 +173,16 @@ static inline void serio_continue_rx(struct serio *serio)
 	spin_unlock_irq(&serio->lock);
 }
 
+static inline void serio_activate(struct serio *serio)
+{
+	if (serio && serio->drv && serio->drv->activate)
+		serio->drv->activate(serio);
+}
+
+static inline void serio_deactivate(struct serio *serio)
+{
+	if (serio && serio->drv && serio->drv->deactivate)
+		serio->drv->deactivate(serio);
+}
+
 #endif
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 05/10] Input: synaptics - allocate a Synaptics Intertouch device
  2017-01-10 16:11 [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
                   ` (3 preceding siblings ...)
  2017-01-10 16:11 ` [PATCH 04/10] Input: psmouse - allow to deactivate a driver from the serio handle Benjamin Tissoires
@ 2017-01-10 16:11 ` Benjamin Tissoires
  2017-01-10 16:11 ` [PATCH 06/10] Input: synaptics-rmi4 - smbus: call psmouse_deactivate before binding/resume Benjamin Tissoires
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 21+ messages in thread
From: Benjamin Tissoires @ 2017-01-10 16:11 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

Most of the Synaptics devices are connected through PS/2 and a different
bus (SMBus or HID over I2C).
The secondary bus capability is indicated by the InterTouch bit in
extended capability 0x0C.

When we encounter such a device, we can create a platform device with
the information gathered through the PS/2 enumeration as some information
might be missing through the other bus. Using a platform device allows
to not add any dependency on the psmouse driver.

We only enable the InterTouch device to be created for the laptops
registered with the top software button property or those we know
that are functional.

In the future, we might change the default to always rely on the
InterTouch bus. Currently, users can enable/disable the feature
with the psmouse parameter synaptics_intertouch.

The SMBus devices keep their PS/2 connection alive. If the initialization
process goes too far (psmouse_activate called), the device disconnects
from the I2C bus and stays on the PS/2 bus. We need to be sure the psmouse
driver will stop communicating with the device (and the pass-through
trackstick too). This part is not addressed here but will be in a
following patch.

The HID over I2C devices are enumerated through the ACPI DSDT, and
their PS/2 device also exports the InterTouch bit in the extended
capability 0x0C. However, the firmware keeps its I2C connection open
even after going further in the PS/2 initialization. We don't need
to take extra precautions with those device, especially because they
block their PS/2 communication when HID over I2C is used.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/input/mouse/synaptics.c | 100 ++++++++++++++++++++++++++++++++++++++++
 drivers/input/mouse/synaptics.h |   4 ++
 2 files changed, 104 insertions(+)

diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index e977ab0..1f79593 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -29,6 +29,8 @@
 #include <linux/input/mt.h>
 #include <linux/serio.h>
 #include <linux/libps2.h>
+#include <linux/platform_device.h>
+#include <linux/rmi.h>
 #include <linux/slab.h>
 #include "psmouse.h"
 #include "synaptics.h"
@@ -70,6 +72,21 @@
 /* maximum ABS_MT_POSITION displacement (in mm) */
 #define DMAX 10
 
+/*
+ * The newest Synaptics device can use a secondary bus (called InterTouch) which
+ * provides a better bandwidth and allow a better control of the touchpads.
+ * This is used to decide if we need to use this bus or not.
+ */
+enum {
+	SYNAPTICS_INTERTOUCH_NOT_SET = -1,
+	SYNAPTICS_INTERTOUCH_OFF,
+	SYNAPTICS_INTERTOUCH_ON,
+};
+
+static int synaptics_intertouch = SYNAPTICS_INTERTOUCH_NOT_SET;
+module_param_named(synaptics_intertouch, synaptics_intertouch, int, 0644);
+MODULE_PARM_DESC(synaptics_intertouch, "Use a secondary bus for the Synaptics device.");
+
 /*****************************************************************************
  *	Stuff we need even when we do not want native Synaptics support
  ****************************************************************************/
@@ -218,6 +235,81 @@ static const char * const forcepad_pnp_ids[] = {
 	NULL
 };
 
+static const char * const smbus_pnp_ids[] = {
+	/* all of the topbuttonpad_pnp_ids are valid, we just add some extras */
+	"LEN0048", /* X1 Carbon 3 */
+	"LEN0046", /* X250 */
+	"LEN004a", /* W541 */
+	"LEN200f", /* T450s */
+};
+
+static int rmi4_id;
+
+static int synaptics_create_intertouch(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	struct platform_device_info pdevinfo;
+	struct platform_device *pdev;
+	struct rmi_device_platform_data pdata = {
+		.sensor_pdata = {
+			.sensor_type = rmi_sensor_touchpad,
+			.axis_align.flip_y = true,
+			/* to prevent cursors jumps: */
+			.kernel_tracking = true,
+		},
+		.f30_data = {
+			.buttonpad = SYN_CAP_CLICKPAD(priv->ext_cap_0c),
+			.trackstick_buttons =
+			  !!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10),
+		},
+	};
+
+	if (priv->intertouch)
+		return -EINVAL;
+
+	pdata.sensor_pdata.topbuttonpad =
+			psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
+			!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10);
+
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.name = "rmi4";
+	pdevinfo.id = rmi4_id++;
+	pdevinfo.data = &pdata;
+	pdevinfo.size_data = sizeof(pdata);
+	pdevinfo.parent = &psmouse->ps2dev.serio->dev;
+
+	pdev = platform_device_register_full(&pdevinfo);
+	if (IS_ERR(pdev))
+		return PTR_ERR(pdev);
+
+	priv->intertouch = pdev;
+
+	return 0;
+}
+
+/**
+ * synaptics_setup_intertouch - called once the PS/2 devices are enumerated
+ * and decides to instantiate a SMBus InterTouch device.
+ */
+static void synaptics_setup_intertouch(struct psmouse *psmouse)
+{
+	int ret;
+
+	if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_OFF)
+		return;
+
+	if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_NOT_SET) {
+		if (!psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
+		    !psmouse_matches_pnp_id(psmouse, smbus_pnp_ids))
+			return;
+	}
+
+	psmouse_info(psmouse, "device also supported by an other bus.\n");
+	ret = synaptics_create_intertouch(psmouse);
+	if (ret)
+		psmouse_info(psmouse,
+			     "unable to create intertouch device.\n");
+}
 /*****************************************************************************
  *	Synaptics communications functions
  ****************************************************************************/
@@ -1318,6 +1410,11 @@ static void synaptics_disconnect(struct psmouse *psmouse)
 		device_remove_file(&psmouse->ps2dev.serio->dev,
 				   &psmouse_attr_disable_gesture.dattr);
 
+	if (priv->intertouch) {
+		platform_device_unregister(priv->intertouch);
+		priv->intertouch = NULL;
+	}
+
 	synaptics_reset(psmouse);
 	kfree(priv);
 	psmouse->private = NULL;
@@ -1562,6 +1659,9 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
 		}
 	}
 
+	if (SYN_CAP_INTERTOUCH(priv->ext_cap_0c))
+		synaptics_setup_intertouch(psmouse);
+
 	return 0;
 
  init_fail:
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 116ae25..b88755d 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -90,6 +90,7 @@
 #define SYN_CAP_ADV_GESTURE(ex0c)	((ex0c) & 0x080000)
 #define SYN_CAP_REDUCED_FILTERING(ex0c)	((ex0c) & 0x000400)
 #define SYN_CAP_IMAGE_SENSOR(ex0c)	((ex0c) & 0x000800)
+#define SYN_CAP_INTERTOUCH(ex0c)	((ex0c) & 0x004000)
 
 /*
  * The following descibes response for the 0x10 query.
@@ -196,6 +197,9 @@ struct synaptics_data {
 	bool					press;
 	bool					report_press;
 	bool					is_forcepad;
+
+	/* Intertouch handling */
+	struct platform_device *intertouch;
 };
 
 void synaptics_module_init(void);
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 06/10] Input: synaptics-rmi4 - smbus: call psmouse_deactivate before binding/resume
  2017-01-10 16:11 [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
                   ` (4 preceding siblings ...)
  2017-01-10 16:11 ` [PATCH 05/10] Input: synaptics - allocate a Synaptics Intertouch device Benjamin Tissoires
@ 2017-01-10 16:11 ` Benjamin Tissoires
  2017-01-10 16:11 ` [PATCH 07/10] Input: synaptics-rmi4 - smbus: on resume, try 3 times if init fails Benjamin Tissoires
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 21+ messages in thread
From: Benjamin Tissoires @ 2017-01-10 16:11 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

The SMBus Synaptics devices enumerated as PS/2 devices have
the problem of being deaf to I2C if the touchpad has been
fully initialized over PS/2 (psmouse_activate being called).
A simple PS/2 deactivate command is enough to make it back alive.

We forward the serio parent node to rmi_smbus so it can manually
deactivate/reactivate the serio node (so we can fallback on PS/2
if a problem happens on SMBus).

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/input/rmi4/rmi_smbus.c | 28 ++++++++++++++++++----------
 include/linux/rmi.h            |  2 ++
 2 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/drivers/input/rmi4/rmi_smbus.c b/drivers/input/rmi4/rmi_smbus.c
index 7675255..3ebb2829 100644
--- a/drivers/input/rmi4/rmi_smbus.c
+++ b/drivers/input/rmi4/rmi_smbus.c
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/pm.h>
 #include <linux/rmi.h>
+#include <linux/serio.h>
 #include <linux/slab.h>
 #include "rmi_driver.h"
 
@@ -34,6 +35,7 @@ struct mapping_table_entry {
 struct rmi_smb_xport {
 	struct rmi_transport_dev xport;
 	struct i2c_client *client;
+	struct serio *serio_parent;
 
 	struct mutex page_mutex;
 	int page;
@@ -244,8 +246,13 @@ static void rmi_smb_clear_state(struct rmi_smb_xport *rmi_smb)
 
 static int rmi_smb_enable_smbus_mode(struct rmi_smb_xport *rmi_smb)
 {
+	struct rmi_device_platform_data *pdata;
 	int retval;
 
+	pdata = dev_get_platdata(&rmi_smb->client->dev);
+
+	serio_deactivate(rmi_smb->serio_parent);
+
 	/* we need to get the smbus version to activate the touchpad */
 	retval = rmi_smb_get_version(rmi_smb);
 	if (retval < 0)
@@ -261,13 +268,6 @@ static int rmi_smb_reset(struct rmi_transport_dev *xport, u16 reset_addr)
 
 	rmi_smb_clear_state(rmi_smb);
 
-	/*
-	 * we do not call the actual reset command, it has to be handled in
-	 * PS/2 or there will be races between PS/2 and SMBus.
-	 * PS/2 should ensure that a psmouse_reset is called before
-	 * intializing the device and after it has been removed to be in a known
-	 * state.
-	 */
 	return rmi_smb_enable_smbus_mode(rmi_smb);
 }
 
@@ -320,10 +320,13 @@ static int rmi_smb_probe(struct i2c_client *client,
 	rmi_smb->xport.pdata.irq = client->irq;
 	rmi_smb->xport.proto_name = "smb2";
 	rmi_smb->xport.ops = &rmi_smb_ops;
+	rmi_smb->serio_parent = pdata->parent;
+
+	serio_deactivate(rmi_smb->serio_parent);
 
 	retval = rmi_smb_get_version(rmi_smb);
 	if (retval < 0)
-		return retval;
+		goto fail;
 
 	smbus_version = retval;
 	rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Smbus version is %d",
@@ -332,7 +335,8 @@ static int rmi_smb_probe(struct i2c_client *client,
 	if (smbus_version != 2) {
 		dev_err(&client->dev, "Unrecognized SMB version %d.\n",
 				smbus_version);
-		return -ENODEV;
+		retval = -ENODEV;
+		goto fail;
 	}
 
 	i2c_set_clientdata(client, rmi_smb);
@@ -342,13 +346,16 @@ static int rmi_smb_probe(struct i2c_client *client,
 		dev_err(&client->dev, "Failed to register transport driver at 0x%.2X.\n",
 			client->addr);
 		i2c_set_clientdata(client, NULL);
-		return retval;
+		goto fail;
 	}
 
 	dev_info(&client->dev, "registered rmi smb driver at %#04x.\n",
 			client->addr);
 	return 0;
 
+fail:
+	serio_activate(rmi_smb->serio_parent);
+	return retval;
 }
 
 static int rmi_smb_remove(struct i2c_client *client)
@@ -356,6 +363,7 @@ static int rmi_smb_remove(struct i2c_client *client)
 	struct rmi_smb_xport *rmi_smb = i2c_get_clientdata(client);
 
 	rmi_unregister_transport_device(&rmi_smb->xport);
+	serio_activate(rmi_smb->serio_parent);
 
 	return 0;
 }
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
index 64125443..c47da15 100644
--- a/include/linux/rmi.h
+++ b/include/linux/rmi.h
@@ -222,6 +222,8 @@ struct rmi_device_platform_data {
 	struct rmi_2d_sensor_platform_data sensor_pdata;
 	struct rmi_f01_power_management power_management;
 	struct rmi_f30_data f30_data;
+
+	struct serio *parent;
 };
 
 /**
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 07/10] Input: synaptics-rmi4 - smbus: on resume, try 3 times if init fails
  2017-01-10 16:11 [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
                   ` (5 preceding siblings ...)
  2017-01-10 16:11 ` [PATCH 06/10] Input: synaptics-rmi4 - smbus: call psmouse_deactivate before binding/resume Benjamin Tissoires
@ 2017-01-10 16:11 ` Benjamin Tissoires
  2017-01-10 16:11 ` [PATCH 08/10] Input: add a PS/2 to SMBus platform module Benjamin Tissoires
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 21+ messages in thread
From: Benjamin Tissoires @ 2017-01-10 16:11 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

In some rare cases, we can't retrieve the SMBus version and so we fail
binding the touchpad back. Instead of leaving a touchpad dead, try again
to reinitialize it.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/input/rmi4/rmi_smbus.c | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/input/rmi4/rmi_smbus.c b/drivers/input/rmi4/rmi_smbus.c
index 3ebb2829..cbab388 100644
--- a/drivers/input/rmi4/rmi_smbus.c
+++ b/drivers/input/rmi4/rmi_smbus.c
@@ -265,10 +265,27 @@ static int rmi_smb_reset(struct rmi_transport_dev *xport, u16 reset_addr)
 {
 	struct rmi_smb_xport *rmi_smb =
 		container_of(xport, struct rmi_smb_xport, xport);
+	struct i2c_client *client = rmi_smb->client;
+	int tries, ret;
 
 	rmi_smb_clear_state(rmi_smb);
 
-	return rmi_smb_enable_smbus_mode(rmi_smb);
+	for (tries = 3; tries > 0; tries--) {
+		ret = rmi_smb_enable_smbus_mode(rmi_smb);
+		if (!ret)
+			break;
+
+		/* we failed enabling SMBus, try again later */
+		msleep(500);
+	}
+
+	if (ret < 0) {
+		dev_warn(&client->dev,
+			 "failed to enable SMBus mode, giving up.\n");
+		serio_activate(rmi_smb->serio_parent);
+	}
+
+	return ret;
 }
 
 static const struct rmi_transport_ops rmi_smb_ops = {
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 08/10] Input: add a PS/2 to SMBus platform module
  2017-01-10 16:11 [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
                   ` (6 preceding siblings ...)
  2017-01-10 16:11 ` [PATCH 07/10] Input: synaptics-rmi4 - smbus: on resume, try 3 times if init fails Benjamin Tissoires
@ 2017-01-10 16:11 ` Benjamin Tissoires
  2017-01-18  8:05   ` Benjamin Tissoires
  2017-01-10 16:11 ` [PATCH 09/10] Input: elan_i2c - add trackstick report Benjamin Tissoires
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 21+ messages in thread
From: Benjamin Tissoires @ 2017-01-10 16:11 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

This driver is a glue between PS/2 devices that enumerate
the RMI4 devices and Elan touchpads to the RMI4 (or Elan)
SMBus driver.

We use an intermediate platform device to not add a
dependency between psmouse and I2C. It also handles
the subtleties of going around the serio mutex lock by
deferring the i2c creation/destruction in a separate
thread.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/input/misc/Kconfig     |  11 ++
 drivers/input/misc/Makefile    |   1 +
 drivers/input/misc/ps2_smbus.c | 255 +++++++++++++++++++++++++++++++++++++++++
 drivers/input/rmi4/Kconfig     |   1 +
 4 files changed, 268 insertions(+)
 create mode 100644 drivers/input/misc/ps2_smbus.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 1ae4d96..e0dfefc 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -832,4 +832,15 @@ config INPUT_HISI_POWERKEY
 	  To compile this driver as a module, choose M here: the
 	  module will be called hisi_powerkey.
 
+config PS2_SMBUS
+	tristate "Platform Support for PS/2 Nodes also connected over SMBus"
+	depends on I2C
+	help
+	  Say Y here if you want to support RMI4 or Elan devices connected to
+	  an SMB bus but enumerated through PS/2.
+
+	  if unsure, say N.
+	  To compile this driver as a module, choose M here: the module will be
+	  called ps2_smbus.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 0b6d025..094a911 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_INPUT_PCSPKR)		+= pcspkr.o
 obj-$(CONFIG_INPUT_PM8941_PWRKEY)	+= pm8941-pwrkey.o
 obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR)	+= pm8xxx-vibrator.o
 obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY)	+= pmic8xxx-pwrkey.o
+obj-$(CONFIG_PS2_SMBUS)			+= ps2_smbus.o
 obj-$(CONFIG_INPUT_POWERMATE)		+= powermate.o
 obj-$(CONFIG_INPUT_PWM_BEEPER)		+= pwm-beeper.o
 obj-$(CONFIG_INPUT_RB532_BUTTON)	+= rb532_button.o
diff --git a/drivers/input/misc/ps2_smbus.c b/drivers/input/misc/ps2_smbus.c
new file mode 100644
index 0000000..d1f27ed
--- /dev/null
+++ b/drivers/input/misc/ps2_smbus.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2017 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/rmi.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@redhat.com>");
+MODULE_DESCRIPTION("Platform PS/2 - SMBus bridge driver");
+MODULE_LICENSE("GPL");
+
+static struct workqueue_struct *kps2smbus_wq;
+DEFINE_MUTEX(ps2smbus_mutex);
+
+enum ps2smbus_type {
+	PS2SMBUS_SYNAPTICS_RMI4,
+};
+
+struct ps2smbus {
+	struct i2c_client *smbus_client;
+	struct notifier_block i2c_notifier;
+	enum ps2smbus_type type;
+	void *pdata;
+};
+
+enum ps2smbus_event_type {
+	PS2SMBUS_REGISTER_DEVICE,
+	PS2SMBUS_UNREGISTER_DEVICE,
+};
+
+struct ps2smbus_work {
+	struct work_struct work;
+	enum ps2smbus_event_type type;
+	struct ps2smbus *ps2smbus;
+	struct i2c_adapter *adap;
+};
+
+static void ps2smbus_create_rmi4(struct ps2smbus *ps2smbus,
+				 struct i2c_adapter *adap)
+{
+	const struct i2c_board_info i2c_info = {
+		I2C_BOARD_INFO("rmi4_smbus", 0x2c),
+		.platform_data = ps2smbus->pdata,
+		.flags = I2C_CLIENT_HOST_NOTIFY,
+	};
+
+	ps2smbus->smbus_client = i2c_new_device(adap, &i2c_info);
+}
+
+static void ps2smbus_worker(struct work_struct *work)
+{
+	struct ps2smbus_work *ps2smbus_work;
+	struct i2c_client *client;
+
+	ps2smbus_work = container_of(work, struct ps2smbus_work, work);
+	client = ps2smbus_work->ps2smbus->smbus_client;
+
+	mutex_lock(&ps2smbus_mutex);
+
+	switch (ps2smbus_work->type) {
+	case PS2SMBUS_REGISTER_DEVICE:
+		if (ps2smbus_work->ps2smbus->type == PS2SMBUS_SYNAPTICS_RMI4)
+			ps2smbus_create_rmi4(ps2smbus_work->ps2smbus,
+					     ps2smbus_work->adap);
+		break;
+	case PS2SMBUS_UNREGISTER_DEVICE:
+		if (client)
+			i2c_unregister_device(client);
+		break;
+	}
+
+	kfree(ps2smbus_work);
+
+	mutex_unlock(&ps2smbus_mutex);
+}
+
+static int ps2smbus_schedule_work(enum ps2smbus_event_type type,
+				  struct ps2smbus *ps2smbus,
+				  struct i2c_adapter *adap)
+{
+	struct ps2smbus_work *ps2smbus_work;
+
+	ps2smbus_work = kzalloc(sizeof(*ps2smbus_work), GFP_KERNEL);
+	if (!ps2smbus_work)
+		return -ENOMEM;
+
+	ps2smbus_work->type = type;
+	ps2smbus_work->ps2smbus = ps2smbus;
+	ps2smbus_work->adap = adap;
+
+	INIT_WORK(&ps2smbus_work->work, ps2smbus_worker);
+
+	queue_work(kps2smbus_wq, &ps2smbus_work->work);
+
+	return 0;
+}
+
+static int ps2smbus_attach_i2c_device(struct device *dev, void *data)
+{
+	struct ps2smbus *ps2smbus = data;
+	struct i2c_adapter *adap;
+
+	if (dev->type != &i2c_adapter_type)
+		return 0;
+
+	adap = to_i2c_adapter(dev);
+
+	if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_HOST_NOTIFY))
+		return 0;
+
+	if (ps2smbus->smbus_client)
+		return 0;
+
+	ps2smbus_schedule_work(PS2SMBUS_REGISTER_DEVICE, ps2smbus, adap);
+
+	pr_debug("ps2smbus: adapter [%s] registered\n", adap->name);
+	return 0;
+}
+
+static int ps2smbus_detach_i2c_device(struct device *dev,
+				      struct ps2smbus *ps2smbus)
+{
+	struct i2c_client *client;
+
+	if (dev->type == &i2c_adapter_type)
+		return 0;
+
+	mutex_lock(&ps2smbus_mutex);
+
+	client = to_i2c_client(dev);
+	if (client == ps2smbus->smbus_client)
+		ps2smbus->smbus_client = NULL;
+
+	mutex_unlock(&ps2smbus_mutex);
+
+	pr_debug("ps2smbus: client [%s] unregistered\n", client->name);
+	return 0;
+}
+
+static int ps2smbus_notifier_call(struct notifier_block *nb,
+				  unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct ps2smbus *ps2smbus;
+
+	ps2smbus = container_of(nb, struct ps2smbus, i2c_notifier);
+
+	switch (action) {
+	case BUS_NOTIFY_ADD_DEVICE:
+		return ps2smbus_attach_i2c_device(dev, ps2smbus);
+	case BUS_NOTIFY_DEL_DEVICE:
+		return ps2smbus_detach_i2c_device(dev, ps2smbus);
+	}
+
+	return 0;
+}
+
+static int ps2smbus_probe(struct platform_device *pdev)
+{
+	struct rmi_device_platform_data *rmi_pdata = pdev->dev.platform_data;
+	struct serio *parent;
+	struct ps2smbus *ps2smbus;
+	int error;
+
+	ps2smbus = devm_kzalloc(&pdev->dev, sizeof(struct ps2smbus),
+				GFP_KERNEL);
+	if (!ps2smbus)
+		return -ENOMEM;
+
+	ps2smbus->i2c_notifier.notifier_call = ps2smbus_notifier_call;
+	ps2smbus->pdata = pdev->dev.platform_data;
+	ps2smbus->type = pdev->id_entry->driver_data;
+	if (pdev->dev.parent) {
+		parent = to_serio_port(pdev->dev.parent);
+		if (ps2smbus->type == PS2SMBUS_SYNAPTICS_RMI4)
+			rmi_pdata->parent = parent;
+	}
+
+	/* Keep track of adapters which will be added or removed later */
+	error = bus_register_notifier(&i2c_bus_type, &ps2smbus->i2c_notifier);
+	if (error)
+		return error;
+
+	/* Bind to already existing adapters right away */
+	i2c_for_each_dev(ps2smbus, ps2smbus_attach_i2c_device);
+
+	platform_set_drvdata(pdev, ps2smbus);
+
+	return 0;
+}
+
+static int ps2smbus_remove(struct platform_device *pdev)
+{
+	struct ps2smbus *ps2smbus = platform_get_drvdata(pdev);
+
+	bus_unregister_notifier(&i2c_bus_type, &ps2smbus->i2c_notifier);
+
+	if (ps2smbus->smbus_client)
+		ps2smbus_schedule_work(PS2SMBUS_UNREGISTER_DEVICE, ps2smbus,
+				       NULL);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static const struct platform_device_id ps2smbus_id_table[] = {
+	{ .name = "rmi4", .driver_data = PS2SMBUS_SYNAPTICS_RMI4 },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, ps2smbus_id_table);
+
+static struct platform_driver ps2smbus_drv = {
+	.driver		= {
+		.name	= "ps2smbus",
+	},
+	.probe		= ps2smbus_probe,
+	.remove		= ps2smbus_remove,
+	.id_table	= ps2smbus_id_table,
+};
+
+static int __init ps2smbus_init(void)
+{
+	int err;
+
+	kps2smbus_wq = alloc_ordered_workqueue("kps2smbusd", WQ_MEM_RECLAIM);
+	if (!kps2smbus_wq) {
+		pr_err("failed to create kps2smbusd workqueue\n");
+		return -ENOMEM;
+	}
+
+	err = platform_driver_register(&ps2smbus_drv);
+	if (err)
+		destroy_workqueue(kps2smbus_wq);
+
+	return err;
+}
+
+static void __exit ps2smbus_exit(void)
+{
+	platform_driver_unregister(&ps2smbus_drv);
+	destroy_workqueue(kps2smbus_wq);
+}
+
+module_init(ps2smbus_init);
+module_exit(ps2smbus_exit);
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
index 30cc627..eb74678 100644
--- a/drivers/input/rmi4/Kconfig
+++ b/drivers/input/rmi4/Kconfig
@@ -30,6 +30,7 @@ config RMI4_SPI
 config RMI4_SMB
 	tristate "RMI4 SMB Support"
 	depends on RMI4_CORE && I2C
+	select RMI4_PLATFORM
 	help
 	  Say Y here if you want to support RMI4 devices connected to an SMB
 	  bus.
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 09/10] Input: elan_i2c - add trackstick report
  2017-01-10 16:11 [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
                   ` (7 preceding siblings ...)
  2017-01-10 16:11 ` [PATCH 08/10] Input: add a PS/2 to SMBus platform module Benjamin Tissoires
@ 2017-01-10 16:11 ` Benjamin Tissoires
  2017-01-10 16:11 ` [PATCH 10/10] Input: elantech - automatically bind an SMBus device when acceptable Benjamin Tissoires
  2017-01-30 13:05 ` [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
  10 siblings, 0 replies; 21+ messages in thread
From: Benjamin Tissoires @ 2017-01-10 16:11 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

The Elan touchpads over I2C/SMBus also can handle a
trackstick. Unfortunately, nothing tells us if the
device supports trackstick (the information lies in
the PS/2 node), so rely on a platform data to enable
or not the trackstick node.

Link: https://bugzilla.redhat.com/show_bug.cgi?id=1313939

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- renamed ETP_PT_REPORT_ID into ETP_TP_REPORT_ID (pass-through to track-point)
  to stay more in line with the rest
- removed Host Notify support so amended to match parent
---
 drivers/input/mouse/elan_i2c.h      | 10 ++++
 drivers/input/mouse/elan_i2c_core.c | 91 +++++++++++++++++++++++++++++++++++--
 2 files changed, 98 insertions(+), 3 deletions(-)

diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h
index c0ec261..468763a 100644
--- a/drivers/input/mouse/elan_i2c.h
+++ b/drivers/input/mouse/elan_i2c.h
@@ -86,4 +86,14 @@ struct elan_transport_ops {
 
 extern const struct elan_transport_ops elan_smbus_ops, elan_i2c_ops;
 
+/**
+ * struct elan_platform_data - system specific configuration info.
+ *
+ * @trackpoint - specify whether or not you want the trackstick input node to
+ * be created and handled by the driver.
+ */
+struct elan_platform_data {
+	bool trackpoint;
+};
+
 #endif /* _ELAN_I2C_H */
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index fa598f7..ca9adf7 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -51,6 +51,7 @@
 #define ETP_MAX_FINGERS		5
 #define ETP_FINGER_DATA_LEN	5
 #define ETP_REPORT_ID		0x5D
+#define ETP_TP_REPORT_ID	0x5E
 #define ETP_REPORT_ID_OFFSET	2
 #define ETP_TOUCH_INFO_OFFSET	3
 #define ETP_FINGER_DATA_OFFSET	4
@@ -61,6 +62,7 @@
 struct elan_tp_data {
 	struct i2c_client	*client;
 	struct input_dev	*input;
+	struct input_dev	*tp_input; /* trackpoint input node */
 	struct regulator	*vcc;
 
 	const struct elan_transport_ops *ops;
@@ -912,6 +914,34 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
 	input_sync(input);
 }
 
+static void elan_report_trackpoint(struct elan_tp_data *data, u8 *report)
+{
+	struct input_dev *input = data->tp_input;
+	u8 *packet = &report[ETP_REPORT_ID_OFFSET + 1];
+	int x, y;
+
+	if (!data->tp_input) {
+		dev_warn_once(&data->client->dev,
+			      "received a trackpoint report while no trackpoint device has been created.\n"
+			      "Please report upstream.\n");
+		return;
+	}
+
+	input_report_key(input, BTN_LEFT, packet[0] & 0x01);
+	input_report_key(input, BTN_RIGHT, packet[0] & 0x02);
+	input_report_key(input, BTN_MIDDLE, packet[0] & 0x04);
+
+	if ((packet[3] & 0x0F) == 0x06) {
+		x = packet[4] - (int)((packet[1]^0x80) << 1);
+		y = (int)((packet[2]^0x80) << 1) - packet[5];
+
+		input_report_rel(input, REL_X, x);
+		input_report_rel(input, REL_Y, y);
+	}
+
+	input_sync(input);
+}
+
 static irqreturn_t elan_isr(int irq, void *dev_id)
 {
 	struct elan_tp_data *data = dev_id;
@@ -933,11 +963,17 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
 	if (error)
 		goto out;
 
-	if (report[ETP_REPORT_ID_OFFSET] != ETP_REPORT_ID)
+	switch (report[ETP_REPORT_ID_OFFSET]) {
+	case ETP_REPORT_ID:
+		elan_report_absolute(data, report);
+		break;
+	case ETP_TP_REPORT_ID:
+		elan_report_trackpoint(data, report);
+		break;
+	default:
 		dev_err(dev, "invalid report id data (%x)\n",
 			report[ETP_REPORT_ID_OFFSET]);
-	else
-		elan_report_absolute(data, report);
+	}
 
 out:
 	return IRQ_HANDLED;
@@ -948,6 +984,37 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
  * Elan initialization functions
  ******************************************************************
  */
+
+static int elan_setup_trackpoint_input_device(struct elan_tp_data *data)
+{
+	struct device *dev = &data->client->dev;
+	struct input_dev *input;
+
+	input = devm_input_allocate_device(dev);
+	if (!input)
+		return -ENOMEM;
+
+	input->name = "Elan TrackPoint";
+	input->id.bustype = BUS_I2C;
+	input->id.vendor = ELAN_VENDOR_ID;
+	input->id.product = data->product_id;
+	input_set_drvdata(input, data);
+
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+	input->relbit[BIT_WORD(REL_X)] =
+		BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+	input->keybit[BIT_WORD(BTN_LEFT)] =
+		BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) |
+		BIT_MASK(BTN_RIGHT);
+
+	__set_bit(INPUT_PROP_POINTER, input->propbit);
+	__set_bit(INPUT_PROP_POINTING_STICK, input->propbit);
+
+	data->tp_input = input;
+
+	return 0;
+}
+
 static int elan_setup_input_device(struct elan_tp_data *data)
 {
 	struct device *dev = &data->client->dev;
@@ -1021,10 +1088,12 @@ static void elan_remove_sysfs_groups(void *_data)
 static int elan_probe(struct i2c_client *client,
 		      const struct i2c_device_id *dev_id)
 {
+	struct elan_platform_data *pdata = dev_get_platdata(&client->dev);
 	const struct elan_transport_ops *transport_ops;
 	struct device *dev = &client->dev;
 	struct elan_tp_data *data;
 	unsigned long irqflags;
+	bool has_trackpoint = pdata && pdata->trackpoint;
 	int error;
 
 	if (IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_I2C) &&
@@ -1114,6 +1183,12 @@ static int elan_probe(struct i2c_client *client,
 	if (error)
 		return error;
 
+	if (has_trackpoint) {
+		error = elan_setup_trackpoint_input_device(data);
+		if (error)
+			return error;
+	}
+
 	/*
 	 * Systems using device tree should set up interrupt via DTS,
 	 * the rest will use the default falling edge interrupts.
@@ -1153,6 +1228,16 @@ static int elan_probe(struct i2c_client *client,
 		return error;
 	}
 
+	if (data->tp_input) {
+		error = input_register_device(data->tp_input);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to register TrackPoint input device: %d\n",
+				error);
+			return error;
+		}
+	}
+
 	/*
 	 * Systems using device tree should set up wakeup via DTS,
 	 * the rest will configure device as wakeup source by default.
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 10/10] Input: elantech - automatically bind an SMBus device when acceptable
  2017-01-10 16:11 [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
                   ` (8 preceding siblings ...)
  2017-01-10 16:11 ` [PATCH 09/10] Input: elan_i2c - add trackstick report Benjamin Tissoires
@ 2017-01-10 16:11 ` Benjamin Tissoires
  2017-01-30 13:05 ` [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
  10 siblings, 0 replies; 21+ messages in thread
From: Benjamin Tissoires @ 2017-01-10 16:11 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

In the same way Synaptics devices can use a secondary bus with a better
bandwidth, Elantech touchpads can also be talked over SMBus.

It's unclear right now which devices have this capability, so use a module
parameter to enable/disable this. We can then whitelist them until we find
a more reliable way of detecting them.

When provided, elan_i2c also prevents the PS/2 node from resuming by
calling serio_deactivate().

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/input/misc/ps2_smbus.c      | 23 +++++++++++++++-
 drivers/input/mouse/elan_i2c.h      |  2 ++
 drivers/input/mouse/elan_i2c_core.c |  8 ++++++
 drivers/input/mouse/elantech.c      | 54 +++++++++++++++++++++++++++++++++++++
 drivers/input/mouse/elantech.h      |  3 +++
 5 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/drivers/input/misc/ps2_smbus.c b/drivers/input/misc/ps2_smbus.c
index d1f27ed..2463afd 100644
--- a/drivers/input/misc/ps2_smbus.c
+++ b/drivers/input/misc/ps2_smbus.c
@@ -23,6 +23,7 @@ DEFINE_MUTEX(ps2smbus_mutex);
 
 enum ps2smbus_type {
 	PS2SMBUS_SYNAPTICS_RMI4,
+	PS2SMBUS_ELAN_SMBUS,
 };
 
 struct ps2smbus {
@@ -56,6 +57,18 @@ static void ps2smbus_create_rmi4(struct ps2smbus *ps2smbus,
 	ps2smbus->smbus_client = i2c_new_device(adap, &i2c_info);
 }
 
+static void ps2smbus_create_elan(struct ps2smbus *ps2smbus,
+				 struct i2c_adapter *adap)
+{
+	const struct i2c_board_info i2c_info = {
+		I2C_BOARD_INFO("elan_i2c", 0x15),
+		.platform_data = ps2smbus->pdata,
+		.flags = I2C_CLIENT_HOST_NOTIFY,
+	};
+
+	ps2smbus->smbus_client = i2c_new_device(adap, &i2c_info);
+}
+
 static void ps2smbus_worker(struct work_struct *work)
 {
 	struct ps2smbus_work *ps2smbus_work;
@@ -68,9 +81,16 @@ static void ps2smbus_worker(struct work_struct *work)
 
 	switch (ps2smbus_work->type) {
 	case PS2SMBUS_REGISTER_DEVICE:
-		if (ps2smbus_work->ps2smbus->type == PS2SMBUS_SYNAPTICS_RMI4)
+		switch (ps2smbus_work->ps2smbus->type) {
+		case PS2SMBUS_SYNAPTICS_RMI4:
 			ps2smbus_create_rmi4(ps2smbus_work->ps2smbus,
 					     ps2smbus_work->adap);
+			break;
+		case PS2SMBUS_ELAN_SMBUS:
+			ps2smbus_create_elan(ps2smbus_work->ps2smbus,
+					     ps2smbus_work->adap);
+			break;
+		}
 		break;
 	case PS2SMBUS_UNREGISTER_DEVICE:
 		if (client)
@@ -215,6 +235,7 @@ static int ps2smbus_remove(struct platform_device *pdev)
 
 static const struct platform_device_id ps2smbus_id_table[] = {
 	{ .name = "rmi4", .driver_data = PS2SMBUS_SYNAPTICS_RMI4 },
+	{ .name = "elan_smbus", .driver_data = PS2SMBUS_ELAN_SMBUS },
 	{ }
 };
 MODULE_DEVICE_TABLE(platform, ps2smbus_id_table);
diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h
index 468763a..d555015 100644
--- a/drivers/input/mouse/elan_i2c.h
+++ b/drivers/input/mouse/elan_i2c.h
@@ -37,6 +37,7 @@
 #define ETP_FW_SIGNATURE_SIZE	6
 
 struct i2c_client;
+struct serio;
 struct completion;
 
 enum tp_mode {
@@ -93,6 +94,7 @@ extern const struct elan_transport_ops elan_smbus_ops, elan_i2c_ops;
  * be created and handled by the driver.
  */
 struct elan_platform_data {
+	struct serio *parent;
 	bool trackpoint;
 };
 
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index ca9adf7..97ff61d 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -30,6 +30,7 @@
 #include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/serio.h>
 #include <linux/input.h>
 #include <linux/uaccess.h>
 #include <linux/jiffies.h>
@@ -1092,6 +1093,7 @@ static int elan_probe(struct i2c_client *client,
 	const struct elan_transport_ops *transport_ops;
 	struct device *dev = &client->dev;
 	struct elan_tp_data *data;
+	struct serio *parent = NULL;
 	unsigned long irqflags;
 	bool has_trackpoint = pdata && pdata->trackpoint;
 	int error;
@@ -1122,6 +1124,12 @@ static int elan_probe(struct i2c_client *client,
 	init_completion(&data->fw_completion);
 	mutex_init(&data->sysfs_mutex);
 
+	if (pdata)
+		parent = pdata->parent;
+
+	/* Make sure the driver stays here on resume */
+	serio_deactivate(parent);
+
 	data->vcc = devm_regulator_get(&client->dev, "vcc");
 	if (IS_ERR(data->vcc)) {
 		error = PTR_ERR(data->vcc);
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index db7d1d6..f83940d 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -16,11 +16,28 @@
 #include <linux/module.h>
 #include <linux/input.h>
 #include <linux/input/mt.h>
+#include <linux/platform_device.h>
 #include <linux/serio.h>
 #include <linux/libps2.h>
 #include <asm/unaligned.h>
 #include "psmouse.h"
 #include "elantech.h"
+#include "elan_i2c.h"
+
+/*
+ * The newest Elan devices can use a secondary bus (over SMBus) which
+ * provides a better bandwidth and allow a better control of the touchpads.
+ * This is used to decide if we need to use this bus or not.
+ */
+enum {
+	ELAN_SMBUS_NOT_SET = -1,
+	ELAN_SMBUS_OFF,
+	ELAN_SMBUS_ON,
+};
+
+static int elan_smbus = ELAN_SMBUS_OFF;
+module_param_named(elan_smbus, elan_smbus, int, 0644);
+MODULE_PARM_DESC(elan_smbus, "Use a secondary bus for the Elantech device.");
 
 #define elantech_debug(fmt, ...)					\
 	do {								\
@@ -1470,6 +1487,11 @@ static void elantech_disconnect(struct psmouse *psmouse)
 {
 	struct elantech_data *etd = psmouse->private;
 
+	if (etd->smbus) {
+		platform_device_unregister(etd->smbus);
+		etd->smbus = NULL;
+	}
+
 	if (etd->tp_dev)
 		input_unregister_device(etd->tp_dev);
 	sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
@@ -1629,6 +1651,35 @@ static int elantech_set_properties(struct elantech_data *etd)
 	return 0;
 }
 
+static int smbus_id;
+
+static int elan_create_smbus(struct psmouse *psmouse, struct elantech_data *etd)
+{
+	struct platform_device *pdev;
+	struct platform_device_info pdevinfo;
+	struct elan_platform_data pdata = {
+		.trackpoint = !!etd->tp_dev,
+	};
+
+	if (etd->smbus)
+		return -EINVAL;
+
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.name = "elan_smbus";
+	pdevinfo.id = smbus_id++;
+	pdevinfo.data = &pdata;
+	pdevinfo.size_data = sizeof(pdata);
+	pdevinfo.parent = &psmouse->ps2dev.serio->dev;
+
+	pdev = platform_device_register_full(&pdevinfo);
+	if (IS_ERR(pdev))
+		return PTR_ERR(pdev);
+
+	etd->smbus = pdev;
+
+	return 0;
+}
+
 /*
  * Initialize the touchpad and create sysfs entries
  */
@@ -1751,6 +1802,9 @@ int elantech_init(struct psmouse *psmouse)
 	psmouse->reconnect = elantech_reconnect;
 	psmouse->pktsize = etd->hw_version > 1 ? 6 : 4;
 
+	if (etd->hw_version == 4 && elan_smbus == ELAN_SMBUS_ON)
+		elan_create_smbus(psmouse, etd);
+
 	return 0;
  init_fail_tp_reg:
 	input_free_device(tp_dev);
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index e1cbf40..299f2e3d 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -144,6 +144,9 @@ struct elantech_data {
 	unsigned char parity[256];
 	int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param);
 	void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate);
+
+	/* SMBus handling */
+	struct platform_device *smbus;
 };
 
 #ifdef CONFIG_MOUSE_PS2_ELANTECH
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH 08/10] Input: add a PS/2 to SMBus platform module
  2017-01-10 16:11 ` [PATCH 08/10] Input: add a PS/2 to SMBus platform module Benjamin Tissoires
@ 2017-01-18  8:05   ` Benjamin Tissoires
  0 siblings, 0 replies; 21+ messages in thread
From: Benjamin Tissoires @ 2017-01-18  8:05 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: KT Liao, Andrew Duggan, Adrian Alves, linux-kernel, linux-input

On Tue, Jan 10, 2017 at 5:11 PM, Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
> This driver is a glue between PS/2 devices that enumerate
> the RMI4 devices and Elan touchpads to the RMI4 (or Elan)
> SMBus driver.
>
> We use an intermediate platform device to not add a
> dependency between psmouse and I2C. It also handles
> the subtleties of going around the serio mutex lock by
> deferring the i2c creation/destruction in a separate
> thread.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
>  drivers/input/misc/Kconfig     |  11 ++
>  drivers/input/misc/Makefile    |   1 +
>  drivers/input/misc/ps2_smbus.c | 255 +++++++++++++++++++++++++++++++++++++++++
>  drivers/input/rmi4/Kconfig     |   1 +
>  4 files changed, 268 insertions(+)
>  create mode 100644 drivers/input/misc/ps2_smbus.c
>
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index 1ae4d96..e0dfefc 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -832,4 +832,15 @@ config INPUT_HISI_POWERKEY
>           To compile this driver as a module, choose M here: the
>           module will be called hisi_powerkey.
>
> +config PS2_SMBUS
> +       tristate "Platform Support for PS/2 Nodes also connected over SMBus"
> +       depends on I2C
> +       help
> +         Say Y here if you want to support RMI4 or Elan devices connected to
> +         an SMB bus but enumerated through PS/2.
> +
> +         if unsure, say N.
> +         To compile this driver as a module, choose M here: the module will be
> +         called ps2_smbus.
> +
>  endif
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index 0b6d025..094a911 100644
> --- a/drivers/input/misc/Makefile
> +++ b/drivers/input/misc/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_INPUT_PCSPKR)            += pcspkr.o
>  obj-$(CONFIG_INPUT_PM8941_PWRKEY)      += pm8941-pwrkey.o
>  obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR)    += pm8xxx-vibrator.o
>  obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY)    += pmic8xxx-pwrkey.o
> +obj-$(CONFIG_PS2_SMBUS)                        += ps2_smbus.o
>  obj-$(CONFIG_INPUT_POWERMATE)          += powermate.o
>  obj-$(CONFIG_INPUT_PWM_BEEPER)         += pwm-beeper.o
>  obj-$(CONFIG_INPUT_RB532_BUTTON)       += rb532_button.o
> diff --git a/drivers/input/misc/ps2_smbus.c b/drivers/input/misc/ps2_smbus.c
> new file mode 100644
> index 0000000..d1f27ed
> --- /dev/null
> +++ b/drivers/input/misc/ps2_smbus.c
> @@ -0,0 +1,255 @@
> +/*
> + * Copyright (c) 2017 Red Hat, Inc
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/platform_device.h>
> +#include <linux/rmi.h>
> +#include <linux/serio.h>
> +#include <linux/slab.h>
> +
> +MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@redhat.com>");
> +MODULE_DESCRIPTION("Platform PS/2 - SMBus bridge driver");
> +MODULE_LICENSE("GPL");
> +
> +static struct workqueue_struct *kps2smbus_wq;
> +DEFINE_MUTEX(ps2smbus_mutex);
> +
> +enum ps2smbus_type {
> +       PS2SMBUS_SYNAPTICS_RMI4,
> +};
> +
> +struct ps2smbus {
> +       struct i2c_client *smbus_client;
> +       struct notifier_block i2c_notifier;
> +       enum ps2smbus_type type;
> +       void *pdata;
> +};
> +
> +enum ps2smbus_event_type {
> +       PS2SMBUS_REGISTER_DEVICE,
> +       PS2SMBUS_UNREGISTER_DEVICE,
> +};
> +
> +struct ps2smbus_work {
> +       struct work_struct work;
> +       enum ps2smbus_event_type type;
> +       struct ps2smbus *ps2smbus;
> +       struct i2c_adapter *adap;
> +};
> +
> +static void ps2smbus_create_rmi4(struct ps2smbus *ps2smbus,
> +                                struct i2c_adapter *adap)
> +{
> +       const struct i2c_board_info i2c_info = {
> +               I2C_BOARD_INFO("rmi4_smbus", 0x2c),
> +               .platform_data = ps2smbus->pdata,
> +               .flags = I2C_CLIENT_HOST_NOTIFY,
> +       };
> +
> +       ps2smbus->smbus_client = i2c_new_device(adap, &i2c_info);
> +}
> +
> +static void ps2smbus_worker(struct work_struct *work)
> +{
> +       struct ps2smbus_work *ps2smbus_work;
> +       struct i2c_client *client;
> +
> +       ps2smbus_work = container_of(work, struct ps2smbus_work, work);
> +       client = ps2smbus_work->ps2smbus->smbus_client;
> +
> +       mutex_lock(&ps2smbus_mutex);
> +
> +       switch (ps2smbus_work->type) {
> +       case PS2SMBUS_REGISTER_DEVICE:
> +               if (ps2smbus_work->ps2smbus->type == PS2SMBUS_SYNAPTICS_RMI4)
> +                       ps2smbus_create_rmi4(ps2smbus_work->ps2smbus,
> +                                            ps2smbus_work->adap);
> +               break;
> +       case PS2SMBUS_UNREGISTER_DEVICE:
> +               if (client)
> +                       i2c_unregister_device(client);
> +               break;
> +       }
> +
> +       kfree(ps2smbus_work);
> +
> +       mutex_unlock(&ps2smbus_mutex);
> +}
> +
> +static int ps2smbus_schedule_work(enum ps2smbus_event_type type,
> +                                 struct ps2smbus *ps2smbus,
> +                                 struct i2c_adapter *adap)
> +{
> +       struct ps2smbus_work *ps2smbus_work;
> +
> +       ps2smbus_work = kzalloc(sizeof(*ps2smbus_work), GFP_KERNEL);
> +       if (!ps2smbus_work)
> +               return -ENOMEM;
> +
> +       ps2smbus_work->type = type;
> +       ps2smbus_work->ps2smbus = ps2smbus;
> +       ps2smbus_work->adap = adap;
> +
> +       INIT_WORK(&ps2smbus_work->work, ps2smbus_worker);
> +
> +       queue_work(kps2smbus_wq, &ps2smbus_work->work);
> +
> +       return 0;
> +}
> +
> +static int ps2smbus_attach_i2c_device(struct device *dev, void *data)
> +{
> +       struct ps2smbus *ps2smbus = data;
> +       struct i2c_adapter *adap;
> +
> +       if (dev->type != &i2c_adapter_type)
> +               return 0;
> +
> +       adap = to_i2c_adapter(dev);
> +
> +       if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_HOST_NOTIFY))
> +               return 0;
> +
> +       if (ps2smbus->smbus_client)
> +               return 0;
> +
> +       ps2smbus_schedule_work(PS2SMBUS_REGISTER_DEVICE, ps2smbus, adap);
> +
> +       pr_debug("ps2smbus: adapter [%s] registered\n", adap->name);
> +       return 0;
> +}
> +
> +static int ps2smbus_detach_i2c_device(struct device *dev,
> +                                     struct ps2smbus *ps2smbus)
> +{
> +       struct i2c_client *client;
> +
> +       if (dev->type == &i2c_adapter_type)
> +               return 0;
> +
> +       mutex_lock(&ps2smbus_mutex);
> +
> +       client = to_i2c_client(dev);
> +       if (client == ps2smbus->smbus_client)
> +               ps2smbus->smbus_client = NULL;
> +
> +       mutex_unlock(&ps2smbus_mutex);
> +
> +       pr_debug("ps2smbus: client [%s] unregistered\n", client->name);
> +       return 0;
> +}
> +
> +static int ps2smbus_notifier_call(struct notifier_block *nb,
> +                                 unsigned long action, void *data)
> +{
> +       struct device *dev = data;
> +       struct ps2smbus *ps2smbus;
> +
> +       ps2smbus = container_of(nb, struct ps2smbus, i2c_notifier);
> +
> +       switch (action) {
> +       case BUS_NOTIFY_ADD_DEVICE:
> +               return ps2smbus_attach_i2c_device(dev, ps2smbus);
> +       case BUS_NOTIFY_DEL_DEVICE:
> +               return ps2smbus_detach_i2c_device(dev, ps2smbus);
> +       }
> +
> +       return 0;
> +}
> +
> +static int ps2smbus_probe(struct platform_device *pdev)
> +{
> +       struct rmi_device_platform_data *rmi_pdata = pdev->dev.platform_data;
> +       struct serio *parent;
> +       struct ps2smbus *ps2smbus;
> +       int error;
> +
> +       ps2smbus = devm_kzalloc(&pdev->dev, sizeof(struct ps2smbus),
> +                               GFP_KERNEL);
> +       if (!ps2smbus)
> +               return -ENOMEM;
> +
> +       ps2smbus->i2c_notifier.notifier_call = ps2smbus_notifier_call;
> +       ps2smbus->pdata = pdev->dev.platform_data;
> +       ps2smbus->type = pdev->id_entry->driver_data;
> +       if (pdev->dev.parent) {
> +               parent = to_serio_port(pdev->dev.parent);
> +               if (ps2smbus->type == PS2SMBUS_SYNAPTICS_RMI4)
> +                       rmi_pdata->parent = parent;
> +       }
> +
> +       /* Keep track of adapters which will be added or removed later */
> +       error = bus_register_notifier(&i2c_bus_type, &ps2smbus->i2c_notifier);
> +       if (error)
> +               return error;
> +
> +       /* Bind to already existing adapters right away */
> +       i2c_for_each_dev(ps2smbus, ps2smbus_attach_i2c_device);
> +
> +       platform_set_drvdata(pdev, ps2smbus);
> +
> +       return 0;
> +}
> +
> +static int ps2smbus_remove(struct platform_device *pdev)
> +{
> +       struct ps2smbus *ps2smbus = platform_get_drvdata(pdev);
> +
> +       bus_unregister_notifier(&i2c_bus_type, &ps2smbus->i2c_notifier);
> +
> +       if (ps2smbus->smbus_client)
> +               ps2smbus_schedule_work(PS2SMBUS_UNREGISTER_DEVICE, ps2smbus,
> +                                      NULL);
> +
> +       platform_set_drvdata(pdev, NULL);
> +
> +       return 0;
> +}
> +
> +static const struct platform_device_id ps2smbus_id_table[] = {
> +       { .name = "rmi4", .driver_data = PS2SMBUS_SYNAPTICS_RMI4 },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(platform, ps2smbus_id_table);
> +
> +static struct platform_driver ps2smbus_drv = {
> +       .driver         = {
> +               .name   = "ps2smbus",
> +       },
> +       .probe          = ps2smbus_probe,
> +       .remove         = ps2smbus_remove,
> +       .id_table       = ps2smbus_id_table,
> +};
> +
> +static int __init ps2smbus_init(void)
> +{
> +       int err;
> +
> +       kps2smbus_wq = alloc_ordered_workqueue("kps2smbusd", WQ_MEM_RECLAIM);
> +       if (!kps2smbus_wq) {
> +               pr_err("failed to create kps2smbusd workqueue\n");
> +               return -ENOMEM;
> +       }
> +
> +       err = platform_driver_register(&ps2smbus_drv);
> +       if (err)
> +               destroy_workqueue(kps2smbus_wq);
> +
> +       return err;
> +}
> +
> +static void __exit ps2smbus_exit(void)
> +{
> +       platform_driver_unregister(&ps2smbus_drv);
> +       destroy_workqueue(kps2smbus_wq);
> +}
> +
> +module_init(ps2smbus_init);
> +module_exit(ps2smbus_exit);
> diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
> index 30cc627..eb74678 100644
> --- a/drivers/input/rmi4/Kconfig
> +++ b/drivers/input/rmi4/Kconfig
> @@ -30,6 +30,7 @@ config RMI4_SPI
>  config RMI4_SMB
>         tristate "RMI4 SMB Support"
>         depends on RMI4_CORE && I2C
> +       select RMI4_PLATFORM

I realized yesterday that it should be "select PS2_SMBUS" here :/

Other than that, any comments on the series?

Cheers,
Benjamin

>         help
>           Say Y here if you want to support RMI4 devices connected to an SMB
>           bus.
> --
> 2.9.3
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan
  2017-01-10 16:11 [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
                   ` (9 preceding siblings ...)
  2017-01-10 16:11 ` [PATCH 10/10] Input: elantech - automatically bind an SMBus device when acceptable Benjamin Tissoires
@ 2017-01-30 13:05 ` Benjamin Tissoires
  2017-02-06 19:27   ` Dmitry Torokhov
  10 siblings, 1 reply; 21+ messages in thread
From: Benjamin Tissoires @ 2017-01-30 13:05 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: KT Liao, Andrew Duggan, Adrian Alves, linux-kernel, linux-input

Hi Dmitri,

On Tue, Jan 10, 2017 at 5:11 PM, Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
> Hi,
>
> Well, this is the last series which enables RMI4 over SMBus for the Thinkpad
> t*40, t*50, t*60 series. Few comments on the patches:
>
> patches 1 to 3 allows the re-routing of the trackstick buttons from the touchpad
>   to the trackstick
> patch 4 to 7 allow rmi_smbus to actually control the PS/2 node to not resume and
>   mess up the configuration after a resume
> patch 8 was originally sent as a RMI4 specific driver, but I came to realise
>   that Elan could also benefit from the same driver

Any comments on the first 8 patches?

Cheers,
Benjamin

> patch 9 has already been sent once, but there was no clear way of enabling the
>   trackstick, so it was not merged
> patch 10 allows the elantech driver to create an instance of elan_i2c and
>   forwards the trackstick information from PS/2
>
> Note that patch 10 has only been tested on a machine without a functional
> elan_i2c touchpad, so only the binding has been tested.
>
> This series requires the Host Notify flag patch from Dmitry:
> http://patchwork.ozlabs.org/patch/711229/
>
> Cheers,
> Benjamin
>
> Benjamin Tissoires (10):
>   Input: serio - store the pt_buttons in the struct serio directly
>   Input: synaptics-rmi4 - Add rmi_find_function()
>   Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on
>     buttonpads to PS/2 guest
>   Input: psmouse - allow to deactivate a driver from the serio handle
>   Input: synaptics - allocate a Synaptics Intertouch device
>   Input: synaptics-rmi4 - smbus: call psmouse_deactivate before
>     binding/resume
>   Input: synaptics-rmi4 - smbus: on resume, try 3 times if init fails
>   Input: add a PS/2 to SMBus platform module
>   Input: elan_i2c - add trackstick report
>   Input: elantech - automatically bind an SMBus device when acceptable
>
>  drivers/input/misc/Kconfig          |  11 ++
>  drivers/input/misc/Makefile         |   1 +
>  drivers/input/misc/ps2_smbus.c      | 276 ++++++++++++++++++++++++++++++++++++
>  drivers/input/mouse/elan_i2c.h      |  12 ++
>  drivers/input/mouse/elan_i2c_core.c |  99 ++++++++++++-
>  drivers/input/mouse/elantech.c      |  54 +++++++
>  drivers/input/mouse/elantech.h      |   3 +
>  drivers/input/mouse/psmouse-base.c  |  34 +++++
>  drivers/input/mouse/psmouse.h       |   3 +
>  drivers/input/mouse/synaptics.c     | 134 +++++++++++++++--
>  drivers/input/mouse/synaptics.h     |   5 +-
>  drivers/input/rmi4/Kconfig          |   1 +
>  drivers/input/rmi4/rmi_driver.c     |  13 ++
>  drivers/input/rmi4/rmi_driver.h     |  15 ++
>  drivers/input/rmi4/rmi_f03.c        |  30 ++++
>  drivers/input/rmi4/rmi_f30.c        |  72 ++++++++--
>  drivers/input/rmi4/rmi_smbus.c      |  47 ++++--
>  include/linux/rmi.h                 |   2 +
>  include/linux/serio.h               |  22 +++
>  19 files changed, 795 insertions(+), 39 deletions(-)
>  create mode 100644 drivers/input/misc/ps2_smbus.c
>
> --
> 2.9.3
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 03/10] Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on buttonpads to PS/2 guest
  2017-01-10 16:11 ` [PATCH 03/10] Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on buttonpads to PS/2 guest Benjamin Tissoires
@ 2017-02-06 19:23   ` Dmitry Torokhov
  2017-02-07 16:25     ` Benjamin Tissoires
  0 siblings, 1 reply; 21+ messages in thread
From: Dmitry Torokhov @ 2017-02-06 19:23 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: KT Liao, Andrew Duggan, Adrian Alves, linux-kernel, linux-input

Hi Benjamin,

On Tue, Jan 10, 2017 at 05:11:21PM +0100, Benjamin Tissoires wrote:
> +void rmi_f03_commit_buttons(struct rmi_function *fn)
> +{
> +	struct f03_data *f03 = dev_get_drvdata(&fn->dev);
> +	int i;
> +
> +	f03->serio->extra_byte = f03->overwrite_buttons;
> +
> +	for (i = 0; i < 3; i++)
> +		serio_interrupt(f03->serio, 0x00, 0x00);

How does this work with protocols larger than 3 bytes (i.e. anything but
bare PS/2)? Or F03 limits the kind of devices we connect to RMI4 device?

Overall, I am very uncomfortable with the concept of "extra byte" at
serio level. Maybe we should define a new flag, something like
SERIO_SYNTHETIC or SERIO_OOB_DATA, and have something like:

void rmi_f03_commit_buttons(struct rmi_function *fn)
{
	struct serio *serio = f03->serio;

	serio_pause_rx(serio);
	if (serio->drv) {
		serio->drv->interrupt(serio, PSMOUSE_EXTRA_BTNS,
				      SERIO_OOB_DATA);
		serio->drv->interrupt(serio, f03->overwrite_buttons.
				      SERIO_OOB_DATA);
	}
	serio_pause_rx(serio);
}

and have psmouse driver store and use it as it sees fit.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan
  2017-01-30 13:05 ` [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
@ 2017-02-06 19:27   ` Dmitry Torokhov
  0 siblings, 0 replies; 21+ messages in thread
From: Dmitry Torokhov @ 2017-02-06 19:27 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: KT Liao, Andrew Duggan, Adrian Alves, linux-kernel, linux-input

Hi Benjamin,

On Mon, Jan 30, 2017 at 02:05:39PM +0100, Benjamin Tissoires wrote:
> Hi Dmitri,
> 
> On Tue, Jan 10, 2017 at 5:11 PM, Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
> > Hi,
> >
> > Well, this is the last series which enables RMI4 over SMBus for the Thinkpad
> > t*40, t*50, t*60 series. Few comments on the patches:
> >
> > patches 1 to 3 allows the re-routing of the trackstick buttons from the touchpad
> >   to the trackstick
> > patch 4 to 7 allow rmi_smbus to actually control the PS/2 node to not resume and
> >   mess up the configuration after a resume
> > patch 8 was originally sent as a RMI4 specific driver, but I came to realise
> >   that Elan could also benefit from the same driver
> 
> Any comments on the first 8 patches?

I think that by trying to "unwind" unsuccessful SMbus initialization you
make the code much more complicated and fragile. I think we should
select a path (PS/2 or SMbus) and commit to it. If we commit to SMBus
then we need to communicate that fact to psmouse core so that it does
not create input devices or psmouse attributes, and "short-circuit" the
reconnect() routines to simply ignore requests and always report
success.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 02/10] Input: synaptics-rmi4 - Add rmi_find_function()
  2017-01-10 16:11 ` [PATCH 02/10] Input: synaptics-rmi4 - Add rmi_find_function() Benjamin Tissoires
@ 2017-02-06 19:28   ` Dmitry Torokhov
  0 siblings, 0 replies; 21+ messages in thread
From: Dmitry Torokhov @ 2017-02-06 19:28 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: KT Liao, Andrew Duggan, Adrian Alves, linux-kernel, linux-input

On Tue, Jan 10, 2017 at 05:11:20PM +0100, Benjamin Tissoires wrote:
> If a function needs to communicate with an other, it's better to have
> a way to retrieve this other.
> 
> Reviewed-by: Andrew Duggan <aduggan@synaptics.com>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

Applied, thank you.

> ---
>  drivers/input/rmi4/rmi_driver.c | 13 +++++++++++++
>  drivers/input/rmi4/rmi_driver.h |  1 +
>  2 files changed, 14 insertions(+)
> 
> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
> index 11447ab..87dbb0c 100644
> --- a/drivers/input/rmi4/rmi_driver.c
> +++ b/drivers/input/rmi4/rmi_driver.c
> @@ -265,6 +265,19 @@ static int rmi_irq_init(struct rmi_device *rmi_dev)
>  	return 0;
>  }
>  
> +struct rmi_function *rmi_find_function(struct rmi_device *rmi_dev, u8 number)
> +{
> +	struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
> +	struct rmi_function *entry;
> +
> +	list_for_each_entry(entry, &data->function_list, node) {
> +		if (entry->fd.function_number == number)
> +			return entry;
> +	}
> +
> +	return NULL;
> +}
> +
>  static int suspend_one_function(struct rmi_function *fn)
>  {
>  	struct rmi_function_handler *fh;
> diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
> index 24f8f76..0506371 100644
> --- a/drivers/input/rmi4/rmi_driver.h
> +++ b/drivers/input/rmi4/rmi_driver.h
> @@ -93,6 +93,7 @@ bool rmi_is_physical_driver(struct device_driver *);
>  int rmi_register_physical_driver(void);
>  void rmi_unregister_physical_driver(void);
>  void rmi_free_function_list(struct rmi_device *rmi_dev);
> +struct rmi_function *rmi_find_function(struct rmi_device *rmi_dev, u8 number);
>  int rmi_enable_sensor(struct rmi_device *rmi_dev);
>  int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
>  		 int (*callback)(struct rmi_device *rmi_dev, void *ctx,
> -- 
> 2.9.3
> 

-- 
Dmitry

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 03/10] Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on buttonpads to PS/2 guest
  2017-02-06 19:23   ` Dmitry Torokhov
@ 2017-02-07 16:25     ` Benjamin Tissoires
  2017-02-07 16:25       ` [PATCH v2 1/2] Input: psmouse - add a custom serio protocol to send extra information Benjamin Tissoires
  2017-02-07 16:25       ` [PATCH v2 2/2] Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on buttonpads to PS/2 guest Benjamin Tissoires
  0 siblings, 2 replies; 21+ messages in thread
From: Benjamin Tissoires @ 2017-02-07 16:25 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

Hi Dmitry,

On Feb 06 2017 or thereabouts, Dmitry Torokhov wrote:
> Hi Benjamin,
> 
> On Tue, Jan 10, 2017 at 05:11:21PM +0100, Benjamin Tissoires wrote:
> > +void rmi_f03_commit_buttons(struct rmi_function *fn)
> > +{
> > +   struct f03_data *f03 = dev_get_drvdata(&fn->dev);
> > +   int i;
> > +
> > +   f03->serio->extra_byte = f03->overwrite_buttons;
> > +
> > +   for (i = 0; i < 3; i++)
> > +           serio_interrupt(f03->serio, 0x00, 0x00);
> 
> How does this work with protocols larger than 3 bytes (i.e. anything but
> bare PS/2)? Or F03 limits the kind of devices we connect to RMI4 device?

Right, there is no such constraint.

> 
> Overall, I am very uncomfortable with the concept of "extra byte" at
> serio level. Maybe we should define a new flag, something like
> SERIO_SYNTHETIC or SERIO_OOB_DATA, and have something like:
> 
> void rmi_f03_commit_buttons(struct rmi_function *fn)
> {
>       struct serio *serio = f03->serio;
> 
>       serio_pause_rx(serio);
>       if (serio->drv) {
>               serio->drv->interrupt(serio, PSMOUSE_EXTRA_BTNS,
>                                     SERIO_OOB_DATA);
>               serio->drv->interrupt(serio, f03->overwrite_buttons.
>                                     SERIO_OOB_DATA);
>       }
>       serio_pause_rx(serio);
> }
> 
> and have psmouse driver store and use it as it sees fit.

Sounds good to me. Would the 2 following patches be OK?

Cheers,
Benjamin


Benjamin Tissoires (2):
  Input: psmouse - add a custom serio protocol to send extra information
  Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on
    buttonpads to PS/2 guest

 drivers/input/mouse/psmouse-base.c | 39 +++++++++++++++++++--
 drivers/input/mouse/psmouse.h      |  5 +++
 drivers/input/rmi4/rmi_driver.h    | 14 ++++++++
 drivers/input/rmi4/rmi_f03.c       | 39 +++++++++++++++++++++
 drivers/input/rmi4/rmi_f30.c       | 72 ++++++++++++++++++++++++++++++--------
 include/uapi/linux/serio.h         |  7 ++--
 6 files changed, 155 insertions(+), 21 deletions(-)

-- 
2.9.3

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 1/2] Input: psmouse - add a custom serio protocol to send extra information
  2017-02-07 16:25     ` Benjamin Tissoires
@ 2017-02-07 16:25       ` Benjamin Tissoires
  2017-02-08  8:26         ` Dmitry Torokhov
  2017-02-07 16:25       ` [PATCH v2 2/2] Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on buttonpads to PS/2 guest Benjamin Tissoires
  1 sibling, 1 reply; 21+ messages in thread
From: Benjamin Tissoires @ 2017-02-07 16:25 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

The tracksticks on the Lenovo thinkpads have their buttons connected
through the touchpad device. We already fixed that in synaptics.c, but
when we switch the device into RMI4 mode to have proper support, the
pass-through functionality can't deal with them easily.

We add a new PS/2 flag and protocol designed for psmouse.
The RMI4 F03 pass-through can then emit a special set of commands
to notify psmouse the state of the buttons.

This patch implements the protocol in psmouse, while an other will
do the same for rmi4-f03.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/input/mouse/psmouse-base.c | 39 +++++++++++++++++++++++++++++++++++---
 drivers/input/mouse/psmouse.h      |  5 +++++
 include/uapi/linux/serio.h         |  7 ++++---
 3 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index bee2674..d37b04a 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -199,9 +199,12 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
 	}
 
 	/* Generic PS/2 Mouse */
-	input_report_key(dev, BTN_LEFT,    packet[0]       & 1);
-	input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
-	input_report_key(dev, BTN_RIGHT,  (packet[0] >> 1) & 1);
+	input_report_key(dev, BTN_LEFT,
+			 (packet[0] | psmouse->extra_buttons)       & 1);
+	input_report_key(dev, BTN_MIDDLE,
+			 ((packet[0] | psmouse->extra_buttons) >> 2) & 1);
+	input_report_key(dev, BTN_RIGHT,
+			 ((packet[0] | psmouse->extra_buttons) >> 1) & 1);
 
 	input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
 	input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
@@ -282,6 +285,19 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
 	return 0;
 }
 
+static void psmouse_handle_extra_buttons(struct psmouse *psmouse,
+					 unsigned char data)
+{
+	struct input_dev *dev = psmouse->dev;
+
+	input_report_key(dev, BTN_LEFT,    data       & 1);
+	input_report_key(dev, BTN_MIDDLE, (data >> 2) & 1);
+	input_report_key(dev, BTN_RIGHT,  (data >> 1) & 1);
+	input_sync(dev);
+
+	psmouse->extra_buttons = data;
+}
+
 /*
  * psmouse_interrupt() handles incoming characters, either passing them
  * for normal processing or gathering them as command response.
@@ -306,6 +322,23 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
 		goto out;
 	}
 
+	if (unlikely(flags & SERIO_OOB_DATA)) {
+		switch (psmouse->oob_data_type) {
+		case PSMOUSE_OOB_NONE:
+			psmouse->oob_data_type = data;
+			goto out;
+		case PSMOUSE_OOB_EXTRA_BTNS:
+			psmouse_handle_extra_buttons(psmouse, data);
+			psmouse->oob_data_type = PSMOUSE_OOB_NONE;
+			goto out;
+		default:
+			psmouse_warn(psmouse,
+				     "unknown OOB_DATA type: 0x%02x\n",
+				     psmouse->oob_data_type);
+			psmouse->oob_data_type = PSMOUSE_OOB_NONE;
+		}
+	}
+
 	if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_ACK))
 		if  (ps2_handle_ack(&psmouse->ps2dev, data))
 			goto out;
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index e0ca6cd..8c83b8e 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -1,6 +1,9 @@
 #ifndef _PSMOUSE_H
 #define _PSMOUSE_H
 
+#define PSMOUSE_OOB_NONE	0x00
+#define PSMOUSE_OOB_EXTRA_BTNS	0x01
+
 #define PSMOUSE_CMD_SETSCALE11	0x00e6
 #define PSMOUSE_CMD_SETSCALE21	0x00e7
 #define PSMOUSE_CMD_SETRES	0x10e8
@@ -53,6 +56,8 @@ struct psmouse {
 	unsigned char pktcnt;
 	unsigned char pktsize;
 	unsigned char type;
+	unsigned char oob_data_type;
+	unsigned char extra_buttons;
 	bool ignore_parity;
 	bool acks_disable_command;
 	unsigned int model;
diff --git a/include/uapi/linux/serio.h b/include/uapi/linux/serio.h
index f2447a8..ccd0ccd 100644
--- a/include/uapi/linux/serio.h
+++ b/include/uapi/linux/serio.h
@@ -17,9 +17,10 @@
 /*
  * bit masks for use in "interrupt" flags (3rd argument)
  */
-#define SERIO_TIMEOUT	1
-#define SERIO_PARITY	2
-#define SERIO_FRAME	4
+#define SERIO_TIMEOUT	BIT(0)
+#define SERIO_PARITY	BIT(1)
+#define SERIO_FRAME	BIT(2)
+#define SERIO_OOB_DATA	BIT(3)
 
 /*
  * Serio types
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 2/2] Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on buttonpads to PS/2 guest
  2017-02-07 16:25     ` Benjamin Tissoires
  2017-02-07 16:25       ` [PATCH v2 1/2] Input: psmouse - add a custom serio protocol to send extra information Benjamin Tissoires
@ 2017-02-07 16:25       ` Benjamin Tissoires
  1 sibling, 0 replies; 21+ messages in thread
From: Benjamin Tissoires @ 2017-02-07 16:25 UTC (permalink / raw)
  To: Dmitry Torokhov, KT Liao, Andrew Duggan
  Cc: Adrian Alves, linux-kernel, linux-input

On the latest series of ThinkPads, the button events for the TrackPoint
are reported through the touchpad itself as opposed to the TrackPoint
device. In order to report these buttons properly, we need to forward
them to the TrackPoint device and notify psmouse to send the button
presses/releases.

Signed-off-by: Lyude Paul <thatslyude@gmail.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---

Changes in v2:
- use of custom psmouse protocol with flag SERIO_OOB_DATA

 drivers/input/rmi4/rmi_driver.h | 14 ++++++++
 drivers/input/rmi4/rmi_f03.c    | 39 ++++++++++++++++++++++
 drivers/input/rmi4/rmi_f30.c    | 72 ++++++++++++++++++++++++++++++++---------
 3 files changed, 110 insertions(+), 15 deletions(-)

diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 6e0449c..d2a89ed 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -107,7 +107,21 @@ int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx,
 
 const char *rmi_f01_get_product_ID(struct rmi_function *fn);
 
+#ifdef CONFIG_RMI4_F03
+int rmi_f03_overwrite_button(struct rmi_function *fn, unsigned int button,
+			     int value);
+void rmi_f03_commit_buttons(struct rmi_function *fn);
+#else
+static inline int rmi_f03_overwrite_button(struct rmi_function *fn,
+					   unsigned int button, int value)
+{
+	return 0;
+}
+static inline void rmi_f03_commit_buttons(struct rmi_function *fn) {}
+#endif
+
 #ifdef CONFIG_RMI4_F34
+
 int rmi_f34_create_sysfs(struct rmi_device *rmi_dev);
 void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev);
 #else
diff --git a/drivers/input/rmi4/rmi_f03.c b/drivers/input/rmi4/rmi_f03.c
index 9a1b099..c89137a 100644
--- a/drivers/input/rmi4/rmi_f03.c
+++ b/drivers/input/rmi4/rmi_f03.c
@@ -26,15 +26,54 @@
 #define RMI_F03_BYTES_PER_DEVICE_SHIFT	4
 #define RMI_F03_QUEUE_LENGTH		0x0F
 
+#define PSMOUSE_OOB_EXTRA_BTNS		0x01
+
 struct f03_data {
 	struct rmi_function *fn;
 
 	struct serio *serio;
 
+	unsigned int overwrite_buttons;
+
 	u8 device_count;
 	u8 rx_queue_length;
 };
 
+int rmi_f03_overwrite_button(struct rmi_function *fn, unsigned int button,
+			     int value)
+{
+	struct f03_data *f03 = dev_get_drvdata(&fn->dev);
+	unsigned int bit = BIT(button);
+
+	if (button > 2)
+		return -EINVAL;
+
+	if (value)
+		f03->overwrite_buttons |= bit;
+	else
+		f03->overwrite_buttons &= ~bit;
+
+	return 0;
+}
+
+void rmi_f03_commit_buttons(struct rmi_function *fn)
+{
+	struct f03_data *f03 = dev_get_drvdata(&fn->dev);
+	struct serio *serio = f03->serio;
+
+	if (!serio)
+		return;
+
+	serio_pause_rx(serio);
+	if (serio->drv) {
+		serio->drv->interrupt(serio, PSMOUSE_OOB_EXTRA_BTNS,
+				      SERIO_OOB_DATA);
+		serio->drv->interrupt(serio, f03->overwrite_buttons,
+				      SERIO_OOB_DATA);
+	}
+	serio_continue_rx(serio);
+}
+
 static int rmi_f03_pt_write(struct serio *id, unsigned char val)
 {
 	struct f03_data *f03 = id->port_data;
diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c
index f4b491e..608c5ee 100644
--- a/drivers/input/rmi4/rmi_f30.c
+++ b/drivers/input/rmi4/rmi_f30.c
@@ -74,8 +74,11 @@ struct f30_data {
 
 	u8 data_regs[RMI_F30_CTRL_MAX_BYTES];
 	u16 *gpioled_key_map;
+	u16 *gpio_passthrough_key_map;
 
 	struct input_dev *input;
+	bool trackstick_buttons;
+	struct rmi_function *f03;
 };
 
 static int rmi_f30_read_control_parameters(struct rmi_function *fn,
@@ -109,6 +112,13 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits)
 	if (!f30->input)
 		return 0;
 
+	if (f30->trackstick_buttons && !f30->f03) {
+		f30->f03 = rmi_find_function(rmi_dev, 3);
+
+		if (!f30->f03)
+			return -EBUSY;
+	}
+
 	/* Read the gpi led data. */
 	if (drvdata->attn_data.data) {
 		if (drvdata->attn_data.size < f30->register_count) {
@@ -133,23 +143,29 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits)
 	for (reg_num = 0; reg_num < f30->register_count; ++reg_num) {
 		for (i = 0; gpiled < f30->gpioled_count && i < 8; ++i,
 			++gpiled) {
-			if (f30->gpioled_key_map[gpiled] != 0) {
-				/* buttons have pull up resistors */
-				value = (((f30->data_regs[reg_num] >> i) & 0x01)
-									== 0);
+			/* buttons have pull up resistors */
+			value = (((f30->data_regs[reg_num] >> i) & 0x01) == 0);
 
+			if (f30->gpioled_key_map[gpiled] != 0) {
 				rmi_dbg(RMI_DEBUG_FN, &fn->dev,
 					"%s: call input report key (0x%04x) value (0x%02x)",
 					__func__,
 					f30->gpioled_key_map[gpiled], value);
+
 				input_report_key(f30->input,
 						 f30->gpioled_key_map[gpiled],
 						 value);
+			} else if (f30->gpio_passthrough_key_map[gpiled]) {
+				rmi_f03_overwrite_button(f30->f03,
+						f30->gpio_passthrough_key_map[gpiled] - BTN_LEFT,
+						value);
 			}
-
 		}
 	}
 
+	if (f30->trackstick_buttons)
+		rmi_f03_commit_buttons(f30->f03);
+
 	return 0;
 }
 
@@ -247,10 +263,11 @@ static inline int rmi_f30_initialize(struct rmi_function *fn)
 	int retval = 0;
 	int control_address;
 	int i;
-	int button;
+	int button, extra_button;
 	u8 buf[RMI_F30_QUERY_SIZE];
 	u8 *ctrl_reg;
-	u8 *map_memory;
+	u8 *map_memory, *pt_memory;
+	bool buttonpad;
 
 	f30 = devm_kzalloc(&fn->dev, sizeof(struct f30_data),
 			   GFP_KERNEL);
@@ -348,29 +365,54 @@ static inline int rmi_f30_initialize(struct rmi_function *fn)
 	map_memory = devm_kzalloc(&fn->dev,
 				  (f30->gpioled_count * (sizeof(u16))),
 				  GFP_KERNEL);
-	if (!map_memory) {
+	pt_memory = devm_kzalloc(&fn->dev,
+				 (f30->gpioled_count * (sizeof(u16))),
+				 GFP_KERNEL);
+	if (!map_memory || !pt_memory) {
 		dev_err(&fn->dev, "Failed to allocate gpioled map memory.\n");
 		return -ENOMEM;
 	}
 
 	f30->gpioled_key_map = (u16 *)map_memory;
+	f30->gpio_passthrough_key_map = (u16 *)pt_memory;
 
 	pdata = rmi_get_platform_data(rmi_dev);
 	if (f30->has_gpio) {
+		/*
+		 * buttonpad might be given by f30->has_mech_mouse_btns,
+		 * but I am not sure, so use only the pdata info
+		 */
+		buttonpad = pdata->f30_data.buttonpad;
+		f30->trackstick_buttons = pdata->f30_data.trackstick_buttons;
+
+		/*
+		 * For touchpads the buttons are mapped as:
+		 * - bit 0 = Left, bit 1 = right, bit 2 = middle / clickbutton
+		 * - 3, 4, 5 are extended buttons and
+		 * - 6 and 7 are other sorts of GPIOs
+		 */
 		button = BTN_LEFT;
-		for (i = 0; i < f30->gpioled_count; i++) {
+		extra_button = BTN_LEFT;
+		for (i = 0; i < f30->gpioled_count && i < 3; i++) {
 			if (rmi_f30_is_valid_button(i, f30->ctrl)) {
 				f30->gpioled_key_map[i] = button++;
 
-				/*
-				 * buttonpad might be given by
-				 * f30->has_mech_mouse_btns, but I am
-				 * not sure, so use only the pdata info
-				 */
-				if (pdata->f30_data.buttonpad)
+				if (buttonpad)
 					break;
 			}
 		}
+
+		if (f30->trackstick_buttons) {
+			for (i = 3; i < f30->gpioled_count && i < 6; i++) {
+				if (rmi_f30_is_valid_button(i, f30->ctrl))
+					f30->gpio_passthrough_key_map[i] = extra_button++;
+			}
+		} else if (!buttonpad) {
+			for (i = 3; i < f30->gpioled_count; i++) {
+				if (rmi_f30_is_valid_button(i, f30->ctrl))
+					f30->gpioled_key_map[i] = button++;
+			}
+		}
 	}
 
 	return 0;
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 1/2] Input: psmouse - add a custom serio protocol to send extra information
  2017-02-07 16:25       ` [PATCH v2 1/2] Input: psmouse - add a custom serio protocol to send extra information Benjamin Tissoires
@ 2017-02-08  8:26         ` Dmitry Torokhov
  2017-02-08 17:51           ` Benjamin Tissoires
  0 siblings, 1 reply; 21+ messages in thread
From: Dmitry Torokhov @ 2017-02-08  8:26 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: KT Liao, Andrew Duggan, Adrian Alves, linux-kernel, linux-input

On Tue, Feb 07, 2017 at 05:25:38PM +0100, Benjamin Tissoires wrote:
> The tracksticks on the Lenovo thinkpads have their buttons connected
> through the touchpad device. We already fixed that in synaptics.c, but
> when we switch the device into RMI4 mode to have proper support, the
> pass-through functionality can't deal with them easily.
> 
> We add a new PS/2 flag and protocol designed for psmouse.
> The RMI4 F03 pass-through can then emit a special set of commands
> to notify psmouse the state of the buttons.
> 
> This patch implements the protocol in psmouse, while an other will
> do the same for rmi4-f03.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

Looks mostly good, but how about this one?

Thanks.

-- 
Dmitry


Input: psmouse - add a custom serio protocol to send extra information

From: Benjamin Tissoires <benjamin.tissoires@redhat.com>

The tracksticks on the Lenovo thinkpads have their buttons connected
through the touchpad device. We already fixed that in synaptics.c, but
when we switch the device into RMI4 mode to have proper support, the
pass-through functionality can't deal with them easily.

We add a new PS/2 flag and protocol designed for psmouse.  The RMI4 F03
pass-through can then emit a special set of commands to notify psmouse the
state of the buttons.

This patch implements the protocol in psmouse, while an other will
do the same for rmi4-f03.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Patchwork-Id: 9560567
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/mouse/psmouse-base.c |   39 +++++++++++++++++++++++++++++++++---
 drivers/input/mouse/psmouse.h      |    5 +++++
 include/uapi/linux/serio.h         |    7 ++++--
 3 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index bee267424972..98a34105d5b0 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -127,6 +127,13 @@ struct psmouse_protocol {
 	int (*init)(struct psmouse *);
 };
 
+static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
+{
+	input_report_key(dev, BTN_LEFT,   buttons & BIT(0));
+	input_report_key(dev, BTN_MIDDLE, buttons & BIT(2));
+	input_report_key(dev, BTN_RIGHT,  buttons & BIT(1));
+}
+
 /*
  * psmouse_process_byte() analyzes the PS/2 data stream and reports
  * relevant events to the input module once full packet has arrived.
@@ -199,9 +206,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
 	}
 
 	/* Generic PS/2 Mouse */
-	input_report_key(dev, BTN_LEFT,    packet[0]       & 1);
-	input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
-	input_report_key(dev, BTN_RIGHT,  (packet[0] >> 1) & 1);
+	psmouse_report_standard_buttons(dev,
+					packet[0] | psmouse->extra_buttons);
 
 	input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
 	input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
@@ -282,6 +288,28 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
 	return 0;
 }
 
+static void psmouse_handle_oob_data(struct psmouse *psmouse, u8 data)
+{
+	switch (psmouse->oob_data_type) {
+	case PSMOUSE_OOB_NONE:
+		psmouse->oob_data_type = data;
+		break;
+
+	case PSMOUSE_OOB_EXTRA_BTNS:
+		psmouse_report_standard_buttons(psmouse->dev, data);
+		psmouse->extra_buttons = data;
+		psmouse->oob_data_type = PSMOUSE_OOB_NONE;
+		break;
+
+	default:
+		psmouse_warn(psmouse,
+			     "unknown OOB_DATA type: 0x%02x\n",
+			     psmouse->oob_data_type);
+		psmouse->oob_data_type = PSMOUSE_OOB_NONE;
+		break;
+	}
+}
+
 /*
  * psmouse_interrupt() handles incoming characters, either passing them
  * for normal processing or gathering them as command response.
@@ -306,6 +334,11 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
 		goto out;
 	}
 
+	if (flags & SERIO_OOB_DATA) {
+		psmouse_handle_oob_data(psmouse, data);
+		goto out;
+	}
+
 	if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_ACK))
 		if  (ps2_handle_ack(&psmouse->ps2dev, data))
 			goto out;
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index e0ca6cda3d16..8c83b8e2505c 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -1,6 +1,9 @@
 #ifndef _PSMOUSE_H
 #define _PSMOUSE_H
 
+#define PSMOUSE_OOB_NONE	0x00
+#define PSMOUSE_OOB_EXTRA_BTNS	0x01
+
 #define PSMOUSE_CMD_SETSCALE11	0x00e6
 #define PSMOUSE_CMD_SETSCALE21	0x00e7
 #define PSMOUSE_CMD_SETRES	0x10e8
@@ -53,6 +56,8 @@ struct psmouse {
 	unsigned char pktcnt;
 	unsigned char pktsize;
 	unsigned char type;
+	unsigned char oob_data_type;
+	unsigned char extra_buttons;
 	bool ignore_parity;
 	bool acks_disable_command;
 	unsigned int model;
diff --git a/include/uapi/linux/serio.h b/include/uapi/linux/serio.h
index f2447a83ac8d..ccd0ccd00f47 100644
--- a/include/uapi/linux/serio.h
+++ b/include/uapi/linux/serio.h
@@ -17,9 +17,10 @@
 /*
  * bit masks for use in "interrupt" flags (3rd argument)
  */
-#define SERIO_TIMEOUT	1
-#define SERIO_PARITY	2
-#define SERIO_FRAME	4
+#define SERIO_TIMEOUT	BIT(0)
+#define SERIO_PARITY	BIT(1)
+#define SERIO_FRAME	BIT(2)
+#define SERIO_OOB_DATA	BIT(3)
 
 /*
  * Serio types

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 1/2] Input: psmouse - add a custom serio protocol to send extra information
  2017-02-08  8:26         ` Dmitry Torokhov
@ 2017-02-08 17:51           ` Benjamin Tissoires
  0 siblings, 0 replies; 21+ messages in thread
From: Benjamin Tissoires @ 2017-02-08 17:51 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: KT Liao, Andrew Duggan, Adrian Alves, linux-kernel, linux-input

On Feb 08 2017 or thereabouts, Dmitry Torokhov wrote:
> On Tue, Feb 07, 2017 at 05:25:38PM +0100, Benjamin Tissoires wrote:
> > The tracksticks on the Lenovo thinkpads have their buttons connected
> > through the touchpad device. We already fixed that in synaptics.c, but
> > when we switch the device into RMI4 mode to have proper support, the
> > pass-through functionality can't deal with them easily.
> > 
> > We add a new PS/2 flag and protocol designed for psmouse.
> > The RMI4 F03 pass-through can then emit a special set of commands
> > to notify psmouse the state of the buttons.
> > 
> > This patch implements the protocol in psmouse, while an other will
> > do the same for rmi4-f03.
> > 
> > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 
> Looks mostly good, but how about this one?

Thanks for the fix. There is one missing input_sync() in the OOB_DATA
case:

> 
> Thanks.
> 
> -- 
> Dmitry
> 
> 
> Input: psmouse - add a custom serio protocol to send extra information
> 
> From: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 
> The tracksticks on the Lenovo thinkpads have their buttons connected
> through the touchpad device. We already fixed that in synaptics.c, but
> when we switch the device into RMI4 mode to have proper support, the
> pass-through functionality can't deal with them easily.
> 
> We add a new PS/2 flag and protocol designed for psmouse.  The RMI4 F03
> pass-through can then emit a special set of commands to notify psmouse the
> state of the buttons.
> 
> This patch implements the protocol in psmouse, while an other will
> do the same for rmi4-f03.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> Patchwork-Id: 9560567
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> ---
>  drivers/input/mouse/psmouse-base.c |   39 +++++++++++++++++++++++++++++++++---
>  drivers/input/mouse/psmouse.h      |    5 +++++
>  include/uapi/linux/serio.h         |    7 ++++--
>  3 files changed, 45 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
> index bee267424972..98a34105d5b0 100644
> --- a/drivers/input/mouse/psmouse-base.c
> +++ b/drivers/input/mouse/psmouse-base.c
> @@ -127,6 +127,13 @@ struct psmouse_protocol {
>  	int (*init)(struct psmouse *);
>  };
>  
> +static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
> +{
> +	input_report_key(dev, BTN_LEFT,   buttons & BIT(0));
> +	input_report_key(dev, BTN_MIDDLE, buttons & BIT(2));
> +	input_report_key(dev, BTN_RIGHT,  buttons & BIT(1));
> +}
> +
>  /*
>   * psmouse_process_byte() analyzes the PS/2 data stream and reports
>   * relevant events to the input module once full packet has arrived.
> @@ -199,9 +206,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
>  	}
>  
>  	/* Generic PS/2 Mouse */
> -	input_report_key(dev, BTN_LEFT,    packet[0]       & 1);
> -	input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
> -	input_report_key(dev, BTN_RIGHT,  (packet[0] >> 1) & 1);
> +	psmouse_report_standard_buttons(dev,
> +					packet[0] | psmouse->extra_buttons);
>  
>  	input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
>  	input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
> @@ -282,6 +288,28 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
>  	return 0;
>  }
>  
> +static void psmouse_handle_oob_data(struct psmouse *psmouse, u8 data)
> +{
> +	switch (psmouse->oob_data_type) {
> +	case PSMOUSE_OOB_NONE:
> +		psmouse->oob_data_type = data;
> +		break;
> +
> +	case PSMOUSE_OOB_EXTRA_BTNS:
> +		psmouse_report_standard_buttons(psmouse->dev, data);

We should probably call input_sync(psmouse->dev) here or the buttons are
not forwarded until the trackstick gets touched.

With that fix:
Tested-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

Cheers,
Benjamin

PS: I won't have the time to work on the other issues of the series this
week unfortunately :(

> +		psmouse->extra_buttons = data;
> +		psmouse->oob_data_type = PSMOUSE_OOB_NONE;
> +		break;
> +
> +	default:
> +		psmouse_warn(psmouse,
> +			     "unknown OOB_DATA type: 0x%02x\n",
> +			     psmouse->oob_data_type);
> +		psmouse->oob_data_type = PSMOUSE_OOB_NONE;
> +		break;
> +	}
> +}
> +
>  /*
>   * psmouse_interrupt() handles incoming characters, either passing them
>   * for normal processing or gathering them as command response.
> @@ -306,6 +334,11 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
>  		goto out;
>  	}
>  
> +	if (flags & SERIO_OOB_DATA) {
> +		psmouse_handle_oob_data(psmouse, data);
> +		goto out;
> +	}
> +
>  	if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_ACK))
>  		if  (ps2_handle_ack(&psmouse->ps2dev, data))
>  			goto out;
> diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
> index e0ca6cda3d16..8c83b8e2505c 100644
> --- a/drivers/input/mouse/psmouse.h
> +++ b/drivers/input/mouse/psmouse.h
> @@ -1,6 +1,9 @@
>  #ifndef _PSMOUSE_H
>  #define _PSMOUSE_H
>  
> +#define PSMOUSE_OOB_NONE	0x00
> +#define PSMOUSE_OOB_EXTRA_BTNS	0x01
> +
>  #define PSMOUSE_CMD_SETSCALE11	0x00e6
>  #define PSMOUSE_CMD_SETSCALE21	0x00e7
>  #define PSMOUSE_CMD_SETRES	0x10e8
> @@ -53,6 +56,8 @@ struct psmouse {
>  	unsigned char pktcnt;
>  	unsigned char pktsize;
>  	unsigned char type;
> +	unsigned char oob_data_type;
> +	unsigned char extra_buttons;
>  	bool ignore_parity;
>  	bool acks_disable_command;
>  	unsigned int model;
> diff --git a/include/uapi/linux/serio.h b/include/uapi/linux/serio.h
> index f2447a83ac8d..ccd0ccd00f47 100644
> --- a/include/uapi/linux/serio.h
> +++ b/include/uapi/linux/serio.h
> @@ -17,9 +17,10 @@
>  /*
>   * bit masks for use in "interrupt" flags (3rd argument)
>   */
> -#define SERIO_TIMEOUT	1
> -#define SERIO_PARITY	2
> -#define SERIO_FRAME	4
> +#define SERIO_TIMEOUT	BIT(0)
> +#define SERIO_PARITY	BIT(1)
> +#define SERIO_FRAME	BIT(2)
> +#define SERIO_OOB_DATA	BIT(3)
>  
>  /*
>   * Serio types

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2017-02-08 17:53 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-10 16:11 [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
2017-01-10 16:11 ` [PATCH 01/10] Input: serio - store the pt_buttons in the struct serio directly Benjamin Tissoires
2017-01-10 16:11 ` [PATCH 02/10] Input: synaptics-rmi4 - Add rmi_find_function() Benjamin Tissoires
2017-02-06 19:28   ` Dmitry Torokhov
2017-01-10 16:11 ` [PATCH 03/10] Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on buttonpads to PS/2 guest Benjamin Tissoires
2017-02-06 19:23   ` Dmitry Torokhov
2017-02-07 16:25     ` Benjamin Tissoires
2017-02-07 16:25       ` [PATCH v2 1/2] Input: psmouse - add a custom serio protocol to send extra information Benjamin Tissoires
2017-02-08  8:26         ` Dmitry Torokhov
2017-02-08 17:51           ` Benjamin Tissoires
2017-02-07 16:25       ` [PATCH v2 2/2] Input: synaptics-rmi4 - f30/f03: Forward mechanical buttons on buttonpads to PS/2 guest Benjamin Tissoires
2017-01-10 16:11 ` [PATCH 04/10] Input: psmouse - allow to deactivate a driver from the serio handle Benjamin Tissoires
2017-01-10 16:11 ` [PATCH 05/10] Input: synaptics - allocate a Synaptics Intertouch device Benjamin Tissoires
2017-01-10 16:11 ` [PATCH 06/10] Input: synaptics-rmi4 - smbus: call psmouse_deactivate before binding/resume Benjamin Tissoires
2017-01-10 16:11 ` [PATCH 07/10] Input: synaptics-rmi4 - smbus: on resume, try 3 times if init fails Benjamin Tissoires
2017-01-10 16:11 ` [PATCH 08/10] Input: add a PS/2 to SMBus platform module Benjamin Tissoires
2017-01-18  8:05   ` Benjamin Tissoires
2017-01-10 16:11 ` [PATCH 09/10] Input: elan_i2c - add trackstick report Benjamin Tissoires
2017-01-10 16:11 ` [PATCH 10/10] Input: elantech - automatically bind an SMBus device when acceptable Benjamin Tissoires
2017-01-30 13:05 ` [PATCH 00/10] Add binding from PS/2 to SMBus for Synaptics and Elan Benjamin Tissoires
2017-02-06 19:27   ` Dmitry Torokhov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).