All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/9] Goodix touchscreen enhancements
@ 2015-09-07 14:36 Irina Tirdea
  2015-09-07 14:36 ` [PATCH v5 1/9] Input: goodix - sort includes alphabetically Irina Tirdea
                   ` (8 more replies)
  0 siblings, 9 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input
  Cc: Mark Rutland, Octavian Purdila, linux-kernel, devicetree, Irina Tirdea

Add several enhancements to the Goodix touchscreen driver.
This version adds runtime power management and includes some cleanup.

Thanks,
Irina

Changes in v5:
 - add some more style cleanup (reorder includes, use error instead
of ret for return values)
 - add runtime power management patch

Changes in v4:
 - use dmi quirk to determine the order of irq and reset pins
 - use actual config length depending on device
 - add sysfs interface to dump config
 - initialize esd timeout from ACPI/DT propery

Changes in v3:
 - dropped the first 3 patches that got merged
 - handle -EPROBE_DEFER and -ENOENT for gpio pins
 - skip functionality depending on the gpio pins if the pins are not
properly initialized from ACPI/DT (reset, write config, power management,
ESD)
 - dropped #ifdef CONFIG_PM_SLEEP and annotated with __maybe_unused instead
 - use sysfs property to set ESD timeout instead of ACPI/DT property
 - use request_firmware_nowait to read configuration firmware and use defaults
if firmware is not found
 - use ACPI IDs to determine the order of the GPIO pins in the ACPI tables
(interrupt pin first or reset pin first)

Changes in v2:
 - use request_firmware instead of ACPI/DT property for config
 - dropped "input: goodix: add ACPI IDs for GT911 and GT9271" patch
 - add ACPI DSDT excerpt in commit message where necessary
 - add comments for suspend/resume sleep values
 - dropped the checkpatch fixes that did not make sense
 - added Bastien's ack to the first patch

Irina Tirdea (9):
  Input: goodix - sort includes alphabetically
  Input: goodix - use actual config length for each device type
  Input: goodix - reset device at init
  Input: goodix - write configuration data to device
  Input: goodix - add power management support
  Input: goodix - use goodix_i2c_write_u8 instead of i2c_master_send
  Input: goodix - add support for ESD
  Input: goodix - add sysfs interface to dump config
  Input: goodix - add runtime power management support

 .../bindings/input/touchscreen/goodix.txt          |  11 +
 drivers/input/touchscreen/goodix.c                 | 709 +++++++++++++++++++--
 2 files changed, 679 insertions(+), 41 deletions(-)

-- 
1.9.1


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

* [PATCH v5 1/9] Input: goodix - sort includes alphabetically
  2015-09-07 14:36 [PATCH v5 0/9] Goodix touchscreen enhancements Irina Tirdea
@ 2015-09-07 14:36 ` Irina Tirdea
  2015-09-09 16:58   ` Bastien Nocera
  2015-10-04 19:46   ` Pavel Machek
  2015-09-07 14:36   ` Irina Tirdea
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input
  Cc: Mark Rutland, Octavian Purdila, linux-kernel, devicetree, Irina Tirdea

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 drivers/input/touchscreen/goodix.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index e36162b..6ae28c5 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -14,18 +14,18 @@
  * Software Foundation; version 2 of the License.
  */
 
-#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
 #include <linux/dmi.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/input/mt.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/irq.h>
 #include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/acpi.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/of.h>
+#include <linux/slab.h>
 #include <asm/unaligned.h>
 
 struct goodix_ts_data {
-- 
1.9.1


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

* [PATCH v5 2/9] Input: goodix - use actual config length for each device type
@ 2015-09-07 14:36   ` Irina Tirdea
  0 siblings, 0 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input
  Cc: Mark Rutland, Octavian Purdila, linux-kernel, devicetree, Irina Tirdea

Each of the Goodix devices supported by this driver has a fixed size for
the configuration information registers. The size varies depending on the
device and is specified in the datasheet.

Use the proper configuration length as specified in the datasheet for
each device model, so we do not read more than the actual size of the
configuration registers.

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 drivers/input/touchscreen/goodix.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 6ae28c5..7be6eab 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -36,6 +36,7 @@ struct goodix_ts_data {
 	unsigned int max_touch_num;
 	unsigned int int_trigger_type;
 	bool rotated_screen;
+	int cfg_len;
 };
 
 #define GOODIX_MAX_HEIGHT		4096
@@ -45,6 +46,8 @@ struct goodix_ts_data {
 #define GOODIX_MAX_CONTACTS		10
 
 #define GOODIX_CONFIG_MAX_LENGTH	240
+#define GOODIX_CONFIG_911_LENGTH	186
+#define GOODIX_CONFIG_967_LENGTH	228
 
 /* Register defines */
 #define GOODIX_READ_COOR_ADDR		0x814E
@@ -115,6 +118,23 @@ static int goodix_i2c_read(struct i2c_client *client,
 	return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
 }
 
+static int goodix_get_cfg_len(u16 id)
+{
+	switch (id) {
+	case 911:
+	case 9271:
+	case 9110:
+	case 927:
+	case 928:
+		return GOODIX_CONFIG_911_LENGTH;
+	case 912:
+	case 967:
+		return GOODIX_CONFIG_967_LENGTH;
+	default:
+		return GOODIX_CONFIG_MAX_LENGTH;
+	}
+}
+
 static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
 {
 	int touch_num;
@@ -230,8 +250,7 @@ static void goodix_read_config(struct goodix_ts_data *ts)
 	int error;
 
 	error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
-				config,
-				GOODIX_CONFIG_MAX_LENGTH);
+				config, ts->cfg_len);
 	if (error) {
 		dev_warn(&ts->client->dev,
 			 "Error reading config (%d), using defaults\n",
@@ -398,6 +417,8 @@ static int goodix_ts_probe(struct i2c_client *client,
 		return error;
 	}
 
+	ts->cfg_len = goodix_get_cfg_len(id_info);
+
 	goodix_read_config(ts);
 
 	error = goodix_request_input_dev(ts, version_info, id_info);
-- 
1.9.1


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

* [PATCH v5 2/9] Input: goodix - use actual config length for each device type
@ 2015-09-07 14:36   ` Irina Tirdea
  0 siblings, 0 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin,
	linux-input-u79uwXL29TY76Z2rM5mHXA
  Cc: Mark Rutland, Octavian Purdila,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Irina Tirdea

Each of the Goodix devices supported by this driver has a fixed size for
the configuration information registers. The size varies depending on the
device and is specified in the datasheet.

Use the proper configuration length as specified in the datasheet for
each device model, so we do not read more than the actual size of the
configuration registers.

Signed-off-by: Irina Tirdea <irina.tirdea-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
 drivers/input/touchscreen/goodix.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 6ae28c5..7be6eab 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -36,6 +36,7 @@ struct goodix_ts_data {
 	unsigned int max_touch_num;
 	unsigned int int_trigger_type;
 	bool rotated_screen;
+	int cfg_len;
 };
 
 #define GOODIX_MAX_HEIGHT		4096
@@ -45,6 +46,8 @@ struct goodix_ts_data {
 #define GOODIX_MAX_CONTACTS		10
 
 #define GOODIX_CONFIG_MAX_LENGTH	240
+#define GOODIX_CONFIG_911_LENGTH	186
+#define GOODIX_CONFIG_967_LENGTH	228
 
 /* Register defines */
 #define GOODIX_READ_COOR_ADDR		0x814E
@@ -115,6 +118,23 @@ static int goodix_i2c_read(struct i2c_client *client,
 	return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
 }
 
+static int goodix_get_cfg_len(u16 id)
+{
+	switch (id) {
+	case 911:
+	case 9271:
+	case 9110:
+	case 927:
+	case 928:
+		return GOODIX_CONFIG_911_LENGTH;
+	case 912:
+	case 967:
+		return GOODIX_CONFIG_967_LENGTH;
+	default:
+		return GOODIX_CONFIG_MAX_LENGTH;
+	}
+}
+
 static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
 {
 	int touch_num;
@@ -230,8 +250,7 @@ static void goodix_read_config(struct goodix_ts_data *ts)
 	int error;
 
 	error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
-				config,
-				GOODIX_CONFIG_MAX_LENGTH);
+				config, ts->cfg_len);
 	if (error) {
 		dev_warn(&ts->client->dev,
 			 "Error reading config (%d), using defaults\n",
@@ -398,6 +417,8 @@ static int goodix_ts_probe(struct i2c_client *client,
 		return error;
 	}
 
+	ts->cfg_len = goodix_get_cfg_len(id_info);
+
 	goodix_read_config(ts);
 
 	error = goodix_request_input_dev(ts, version_info, id_info);
-- 
1.9.1

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

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

* [PATCH v5 3/9] Input: goodix - reset device at init
@ 2015-09-07 14:36   ` Irina Tirdea
  0 siblings, 0 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input
  Cc: Mark Rutland, Octavian Purdila, linux-kernel, devicetree, Irina Tirdea

After power on, it is recommended that the driver resets the device.
The reset procedure timing is described in the datasheet and is used
at device init (before writing device configuration) and
for power management. It is a sequence of setting the interrupt
and reset pins high/low at specific timing intervals. This procedure
also includes setting the slave address to the one specified in the
ACPI/device tree.

This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
driver gt9xx.c for Android (publicly available in Android kernel
trees for various devices).

For reset the driver needs to control the interrupt and
reset gpio pins (configured through ACPI/device tree). For devices
that do not have the gpio pins declared, the functionality depending
on these pins will not be available, but the device can still be used
with basic functionality.

Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 .../bindings/input/touchscreen/goodix.txt          |   5 +
 drivers/input/touchscreen/goodix.c                 | 136 +++++++++++++++++++++
 2 files changed, 141 insertions(+)

diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
index 8ba98ee..c0715f8 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
@@ -12,6 +12,8 @@ Required properties:
  - reg			: I2C address of the chip. Should be 0x5d or 0x14
  - interrupt-parent	: Interrupt controller to which the chip is connected
  - interrupts		: Interrupt to which the chip is connected
+ - gpios		: GPIOS the chip is connected to: first one is the
+			  interrupt gpio and second one the reset gpio.
 
 Example:
 
@@ -23,6 +25,9 @@ Example:
 			reg = <0x5d>;
 			interrupt-parent = <&gpio>;
 			interrupts = <0 0>;
+
+			gpios = <&gpio1 0 0>, /* INT */
+				<&gpio1 1 0>; /* RST */
 		};
 
 		/* ... */
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 7be6eab..8edfc06 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -17,6 +17,7 @@
 #include <linux/acpi.h>
 #include <linux/delay.h>
 #include <linux/dmi.h>
+#include <linux/gpio.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/input/mt.h>
@@ -37,6 +38,8 @@ struct goodix_ts_data {
 	unsigned int int_trigger_type;
 	bool rotated_screen;
 	int cfg_len;
+	struct gpio_desc *gpiod_int;
+	struct gpio_desc *gpiod_rst;
 };
 
 #define GOODIX_MAX_HEIGHT		4096
@@ -89,6 +92,30 @@ static const struct dmi_system_id rotated_screen[] = {
 	{}
 };
 
+/*
+ * ACPI table specifies gpio pins in this order: first rst pin and
+ * then interrupt pin.
+ */
+static const struct dmi_system_id goodix_rst_pin_first[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+	{
+		.ident = "WinBook TW100",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TW100")
+		}
+	},
+	{
+		.ident = "WinBook TW700",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TW700")
+		},
+	},
+#endif
+	{}
+};
+
 /**
  * goodix_i2c_read - read data from a register of the i2c slave device.
  *
@@ -237,6 +264,102 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static int goodix_int_sync(struct goodix_ts_data *ts)
+{
+	int error;
+
+	error = gpiod_direction_output(ts->gpiod_int, 0);
+	if (error)
+		return error;
+	msleep(50);				/* T5: 50ms */
+
+	return gpiod_direction_input(ts->gpiod_int);
+}
+
+/**
+ * goodix_reset - Reset device during power on
+ *
+ * @ts: goodix_ts_data pointer
+ */
+static int goodix_reset(struct goodix_ts_data *ts)
+{
+	int error;
+
+	/* begin select I2C slave addr */
+	error = gpiod_direction_output(ts->gpiod_rst, 0);
+	if (error)
+		return error;
+	msleep(20);				/* T2: > 10ms */
+	/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
+	error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14);
+	if (error)
+		return error;
+	usleep_range(100, 2000);		/* T3: > 100us */
+	error = gpiod_direction_output(ts->gpiod_rst, 1);
+	if (error)
+		return error;
+	usleep_range(6000, 10000);		/* T4: > 5ms */
+	/* end select I2C slave addr */
+	error = gpiod_direction_input(ts->gpiod_rst);
+	if (error)
+		return error;
+	return goodix_int_sync(ts);
+}
+
+/**
+ * goodix_get_gpio_config - Get GPIO config from ACPI/DT
+ *
+ * @ts: goodix_ts_data pointer
+ */
+static int goodix_get_gpio_config(struct goodix_ts_data *ts,
+				  const struct i2c_device_id *i2c_id)
+{
+	int error;
+	struct device *dev;
+	struct gpio_desc *gpiod;
+	/* Default gpio pin order: irq, rst */
+	int irq_idx = 0, rst_idx = 1;
+
+	if (!ts->client)
+		return -EINVAL;
+	dev = &ts->client->dev;
+
+	if (dmi_check_system(goodix_rst_pin_first)) {
+		dev_dbg(&ts->client->dev,
+			"Applying 'reverse gpio pin order' quirk\n");
+		rst_idx = 0;
+		irq_idx = 1;
+	}
+
+	/* Get interrupt GPIO pin number */
+	gpiod = devm_gpiod_get_index(dev, NULL, irq_idx, GPIOD_IN);
+	if (IS_ERR(gpiod)) {
+		error = PTR_ERR(gpiod);
+		if (error != -EPROBE_DEFER)
+			dev_warn(dev, "Failed to get GPIO %d: %d\n",
+				 irq_idx, error);
+		if (error == -ENOENT)
+			return 0;
+		return error;
+	}
+	ts->gpiod_int = gpiod;
+
+	/* Get the reset line GPIO pin number */
+	gpiod = devm_gpiod_get_index(dev, NULL, rst_idx, GPIOD_IN);
+	if (IS_ERR(gpiod)) {
+		error = PTR_ERR(gpiod);
+		if (error != -EPROBE_DEFER)
+			dev_warn(dev, "Failed to get GPIO %d: %d\n",
+				 rst_idx, error);
+		if (error == -ENOENT)
+			return 0;
+		return error;
+	}
+	ts->gpiod_rst = gpiod;
+
+	return 0;
+}
+
 /**
  * goodix_read_config - Read the embedded configuration of the panel
  *
@@ -419,6 +542,19 @@ static int goodix_ts_probe(struct i2c_client *client,
 
 	ts->cfg_len = goodix_get_cfg_len(id_info);
 
+	error = goodix_get_gpio_config(ts, id);
+	if (error)
+		return error;
+
+	if (ts->gpiod_int && ts->gpiod_rst) {
+		/* reset the controller */
+		error = goodix_reset(ts);
+		if (error) {
+			dev_err(&client->dev, "Controller reset failed.\n");
+			return error;
+		}
+	}
+
 	goodix_read_config(ts);
 
 	error = goodix_request_input_dev(ts, version_info, id_info);
-- 
1.9.1


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

* [PATCH v5 3/9] Input: goodix - reset device at init
@ 2015-09-07 14:36   ` Irina Tirdea
  0 siblings, 0 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin,
	linux-input-u79uwXL29TY76Z2rM5mHXA
  Cc: Mark Rutland, Octavian Purdila,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Irina Tirdea

After power on, it is recommended that the driver resets the device.
The reset procedure timing is described in the datasheet and is used
at device init (before writing device configuration) and
for power management. It is a sequence of setting the interrupt
and reset pins high/low at specific timing intervals. This procedure
also includes setting the slave address to the one specified in the
ACPI/device tree.

This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
driver gt9xx.c for Android (publicly available in Android kernel
trees for various devices).

For reset the driver needs to control the interrupt and
reset gpio pins (configured through ACPI/device tree). For devices
that do not have the gpio pins declared, the functionality depending
on these pins will not be available, but the device can still be used
with basic functionality.

Signed-off-by: Octavian Purdila <octavian.purdila-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Signed-off-by: Irina Tirdea <irina.tirdea-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
 .../bindings/input/touchscreen/goodix.txt          |   5 +
 drivers/input/touchscreen/goodix.c                 | 136 +++++++++++++++++++++
 2 files changed, 141 insertions(+)

diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
index 8ba98ee..c0715f8 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
@@ -12,6 +12,8 @@ Required properties:
  - reg			: I2C address of the chip. Should be 0x5d or 0x14
  - interrupt-parent	: Interrupt controller to which the chip is connected
  - interrupts		: Interrupt to which the chip is connected
+ - gpios		: GPIOS the chip is connected to: first one is the
+			  interrupt gpio and second one the reset gpio.
 
 Example:
 
@@ -23,6 +25,9 @@ Example:
 			reg = <0x5d>;
 			interrupt-parent = <&gpio>;
 			interrupts = <0 0>;
+
+			gpios = <&gpio1 0 0>, /* INT */
+				<&gpio1 1 0>; /* RST */
 		};
 
 		/* ... */
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 7be6eab..8edfc06 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -17,6 +17,7 @@
 #include <linux/acpi.h>
 #include <linux/delay.h>
 #include <linux/dmi.h>
+#include <linux/gpio.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/input/mt.h>
@@ -37,6 +38,8 @@ struct goodix_ts_data {
 	unsigned int int_trigger_type;
 	bool rotated_screen;
 	int cfg_len;
+	struct gpio_desc *gpiod_int;
+	struct gpio_desc *gpiod_rst;
 };
 
 #define GOODIX_MAX_HEIGHT		4096
@@ -89,6 +92,30 @@ static const struct dmi_system_id rotated_screen[] = {
 	{}
 };
 
+/*
+ * ACPI table specifies gpio pins in this order: first rst pin and
+ * then interrupt pin.
+ */
+static const struct dmi_system_id goodix_rst_pin_first[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+	{
+		.ident = "WinBook TW100",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TW100")
+		}
+	},
+	{
+		.ident = "WinBook TW700",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TW700")
+		},
+	},
+#endif
+	{}
+};
+
 /**
  * goodix_i2c_read - read data from a register of the i2c slave device.
  *
@@ -237,6 +264,102 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static int goodix_int_sync(struct goodix_ts_data *ts)
+{
+	int error;
+
+	error = gpiod_direction_output(ts->gpiod_int, 0);
+	if (error)
+		return error;
+	msleep(50);				/* T5: 50ms */
+
+	return gpiod_direction_input(ts->gpiod_int);
+}
+
+/**
+ * goodix_reset - Reset device during power on
+ *
+ * @ts: goodix_ts_data pointer
+ */
+static int goodix_reset(struct goodix_ts_data *ts)
+{
+	int error;
+
+	/* begin select I2C slave addr */
+	error = gpiod_direction_output(ts->gpiod_rst, 0);
+	if (error)
+		return error;
+	msleep(20);				/* T2: > 10ms */
+	/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
+	error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14);
+	if (error)
+		return error;
+	usleep_range(100, 2000);		/* T3: > 100us */
+	error = gpiod_direction_output(ts->gpiod_rst, 1);
+	if (error)
+		return error;
+	usleep_range(6000, 10000);		/* T4: > 5ms */
+	/* end select I2C slave addr */
+	error = gpiod_direction_input(ts->gpiod_rst);
+	if (error)
+		return error;
+	return goodix_int_sync(ts);
+}
+
+/**
+ * goodix_get_gpio_config - Get GPIO config from ACPI/DT
+ *
+ * @ts: goodix_ts_data pointer
+ */
+static int goodix_get_gpio_config(struct goodix_ts_data *ts,
+				  const struct i2c_device_id *i2c_id)
+{
+	int error;
+	struct device *dev;
+	struct gpio_desc *gpiod;
+	/* Default gpio pin order: irq, rst */
+	int irq_idx = 0, rst_idx = 1;
+
+	if (!ts->client)
+		return -EINVAL;
+	dev = &ts->client->dev;
+
+	if (dmi_check_system(goodix_rst_pin_first)) {
+		dev_dbg(&ts->client->dev,
+			"Applying 'reverse gpio pin order' quirk\n");
+		rst_idx = 0;
+		irq_idx = 1;
+	}
+
+	/* Get interrupt GPIO pin number */
+	gpiod = devm_gpiod_get_index(dev, NULL, irq_idx, GPIOD_IN);
+	if (IS_ERR(gpiod)) {
+		error = PTR_ERR(gpiod);
+		if (error != -EPROBE_DEFER)
+			dev_warn(dev, "Failed to get GPIO %d: %d\n",
+				 irq_idx, error);
+		if (error == -ENOENT)
+			return 0;
+		return error;
+	}
+	ts->gpiod_int = gpiod;
+
+	/* Get the reset line GPIO pin number */
+	gpiod = devm_gpiod_get_index(dev, NULL, rst_idx, GPIOD_IN);
+	if (IS_ERR(gpiod)) {
+		error = PTR_ERR(gpiod);
+		if (error != -EPROBE_DEFER)
+			dev_warn(dev, "Failed to get GPIO %d: %d\n",
+				 rst_idx, error);
+		if (error == -ENOENT)
+			return 0;
+		return error;
+	}
+	ts->gpiod_rst = gpiod;
+
+	return 0;
+}
+
 /**
  * goodix_read_config - Read the embedded configuration of the panel
  *
@@ -419,6 +542,19 @@ static int goodix_ts_probe(struct i2c_client *client,
 
 	ts->cfg_len = goodix_get_cfg_len(id_info);
 
+	error = goodix_get_gpio_config(ts, id);
+	if (error)
+		return error;
+
+	if (ts->gpiod_int && ts->gpiod_rst) {
+		/* reset the controller */
+		error = goodix_reset(ts);
+		if (error) {
+			dev_err(&client->dev, "Controller reset failed.\n");
+			return error;
+		}
+	}
+
 	goodix_read_config(ts);
 
 	error = goodix_request_input_dev(ts, version_info, id_info);
-- 
1.9.1

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

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

* [PATCH v5 4/9] Input: goodix - write configuration data to device
@ 2015-09-07 14:36   ` Irina Tirdea
  0 siblings, 0 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input
  Cc: Mark Rutland, Octavian Purdila, linux-kernel, devicetree, Irina Tirdea

Goodix devices can be configured by writing custom data to the device at
init. The configuration data is read with request_firmware from
"goodix_<id>_cfg.bin", where <id> is the product id read from the device
(e.g.: goodix_911_cfg.bin for Goodix GT911, goodix_9271_cfg.bin for
GT9271).

The configuration information has a specific format described in the Goodix
datasheet. It includes X/Y resolution, maximum supported touch points,
interrupt flags, various sesitivity factors and settings for advanced
features (like gesture recognition).

Before writing the firmware, it is necessary to reset the device. If
the device ACPI/DT information does not declare gpio pins (needed for
reset), writing the firmware will not be available for these devices.

This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
driver gt9xx.c for Android (publicly available in Android kernel
trees for various devices).

Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 drivers/input/touchscreen/goodix.c | 225 +++++++++++++++++++++++++++++++------
 1 file changed, 192 insertions(+), 33 deletions(-)

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 8edfc06..9cf16ff7 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -17,6 +17,7 @@
 #include <linux/acpi.h>
 #include <linux/delay.h>
 #include <linux/dmi.h>
+#include <linux/firmware.h>
 #include <linux/gpio.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
@@ -40,6 +41,9 @@ struct goodix_ts_data {
 	int cfg_len;
 	struct gpio_desc *gpiod_int;
 	struct gpio_desc *gpiod_rst;
+	u16 id;
+	u16 version;
+	char *cfg_name;
 };
 
 #define GOODIX_MAX_HEIGHT		4096
@@ -145,6 +149,39 @@ static int goodix_i2c_read(struct i2c_client *client,
 	return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
 }
 
+/**
+ * goodix_i2c_write - write data to a register of the i2c slave device.
+ *
+ * @client: i2c device.
+ * @reg: the register to write to.
+ * @buf: raw data buffer to write.
+ * @len: length of the buffer to write
+ */
+static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
+			    unsigned len)
+{
+	u8 *addr_buf;
+	struct i2c_msg msg;
+	int ret;
+
+	addr_buf = kmalloc(len + 2, GFP_KERNEL);
+	if (!addr_buf)
+		return -ENOMEM;
+
+	addr_buf[0] = reg >> 8;
+	addr_buf[1] = reg & 0xFF;
+	memcpy(&addr_buf[2], buf, len);
+
+	msg.flags = 0;
+	msg.addr = client->addr;
+	msg.buf = addr_buf;
+	msg.len = len + 2;
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	kfree(addr_buf);
+	return ret < 0 ? ret : (ret != 1 ? -EIO : 0);
+}
+
 static int goodix_get_cfg_len(u16 id)
 {
 	switch (id) {
@@ -264,6 +301,73 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+/**
+ * goodix_check_cfg - Checks if config fw is valid
+ *
+ * @ts: goodix_ts_data pointer
+ * @cfg: firmware config data
+ */
+static int goodix_check_cfg(struct goodix_ts_data *ts,
+			    const struct firmware *cfg)
+{
+	int i, raw_cfg_len;
+	u8 check_sum = 0;
+
+	if (cfg->size > GOODIX_CONFIG_MAX_LENGTH) {
+		dev_err(&ts->client->dev,
+			"The length of the config fw is not correct");
+		return -EINVAL;
+	}
+
+	raw_cfg_len = cfg->size - 2;
+	for (i = 0; i < raw_cfg_len; i++)
+		check_sum += cfg->data[i];
+	check_sum = (~check_sum) + 1;
+	if (check_sum != cfg->data[raw_cfg_len]) {
+		dev_err(&ts->client->dev,
+			"The checksum of the config fw is not correct");
+		return -EINVAL;
+	}
+
+	if (cfg->data[raw_cfg_len + 1] != 1) {
+		dev_err(&ts->client->dev,
+			"Config fw must have Config_Fresh register set");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * goodix_send_cfg - Write fw config to device
+ *
+ * @ts: goodix_ts_data pointer
+ * @cfg: config firmware to write to device
+ */
+static int goodix_send_cfg(struct goodix_ts_data *ts,
+			   const struct firmware *cfg)
+{
+	int error;
+
+	error = goodix_check_cfg(ts, cfg);
+	if (error)
+		return error;
+
+	error = goodix_i2c_write(ts->client, GOODIX_REG_CONFIG_DATA, cfg->data,
+				 cfg->size);
+	if (error) {
+		dev_err(&ts->client->dev, "Failed to write config data: %d",
+			error);
+		return error;
+	}
+	dev_dbg(&ts->client->dev, "Config sent successfully.");
+
+	/* Let the firmware reconfigure itself, so sleep for 10ms */
+	usleep_range(10000, 11000);
+
+	return 0;
+}
+
 static int goodix_int_sync(struct goodix_ts_data *ts)
 {
 	int error;
@@ -406,30 +510,29 @@ static void goodix_read_config(struct goodix_ts_data *ts)
 /**
  * goodix_read_version - Read goodix touchscreen version
  *
- * @client: the i2c client
- * @version: output buffer containing the version on success
- * @id: output buffer containing the id on success
+ * @ts: our goodix_ts_data pointer
  */
-static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id)
+static int goodix_read_version(struct goodix_ts_data *ts)
 {
 	int error;
 	u8 buf[6];
 	char id_str[5];
 
-	error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf));
+	error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf));
 	if (error) {
-		dev_err(&client->dev, "read version failed: %d\n", error);
+		dev_err(&ts->client->dev, "read version failed: %d\n", error);
 		return error;
 	}
 
 	memcpy(id_str, buf, 4);
 	id_str[4] = 0;
-	if (kstrtou16(id_str, 10, id))
-		*id = 0x1001;
+	if (kstrtou16(id_str, 10, &ts->id))
+		ts->id = 0x1001;
 
-	*version = get_unaligned_le16(&buf[4]);
+	ts->version = get_unaligned_le16(&buf[4]);
 
-	dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version);
+	dev_info(&ts->client->dev, "ID %d, version: %04x\n", ts->id,
+		 ts->version);
 
 	return 0;
 }
@@ -463,13 +566,10 @@ static int goodix_i2c_test(struct i2c_client *client)
  * goodix_request_input_dev - Allocate, populate and register the input device
  *
  * @ts: our goodix_ts_data pointer
- * @version: device firmware version
- * @id: device ID
  *
  * Must be called during probe
  */
-static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
-				    u16 id)
+static int goodix_request_input_dev(struct goodix_ts_data *ts)
 {
 	int error;
 
@@ -493,8 +593,8 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
 	ts->input_dev->phys = "input/ts";
 	ts->input_dev->id.bustype = BUS_I2C;
 	ts->input_dev->id.vendor = 0x0416;
-	ts->input_dev->id.product = id;
-	ts->input_dev->id.version = version;
+	ts->input_dev->id.product = ts->id;
+	ts->input_dev->id.version = ts->version;
 
 	error = input_register_device(ts->input_dev);
 	if (error) {
@@ -506,13 +606,68 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
 	return 0;
 }
 
+/**
+ * goodix_configure_dev - Finish device initialization
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * Must be called from request_firmware_wait callback to
+ * finish initialization of the device.
+ */
+static int goodix_configure_dev(struct goodix_ts_data *ts)
+{
+	int error;
+	unsigned long irq_flags;
+
+	goodix_read_config(ts);
+
+	error = goodix_request_input_dev(ts);
+	if (error)
+		return error;
+
+	irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
+	error = devm_request_threaded_irq(&ts->client->dev, ts->client->irq,
+					  NULL, goodix_ts_irq_handler,
+					  irq_flags, ts->client->name, ts);
+	if (error) {
+		dev_err(&ts->client->dev, "request IRQ failed: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+/**
+ * goodix_config_cb - Callback to finish device init
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * request_firmware_wait callback that finishes
+ * initialization of the device.
+ */
+static void goodix_config_cb(const struct firmware *cfg, void *ctx)
+{
+	struct goodix_ts_data *ts = (struct goodix_ts_data *)ctx;
+	int error;
+
+	if (cfg) {
+		/* send device configuration to the firmware */
+		error = goodix_send_cfg(ts, cfg);
+		if (error)
+			goto err_release_cfg;
+	}
+	goodix_configure_dev(ts);
+
+err_release_cfg:
+	kfree(ts->cfg_name);
+	release_firmware(cfg);
+}
+
 static int goodix_ts_probe(struct i2c_client *client,
 			   const struct i2c_device_id *id)
 {
 	struct goodix_ts_data *ts;
-	unsigned long irq_flags;
 	int error;
-	u16 version_info, id_info;
 
 	dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
 
@@ -534,13 +689,13 @@ static int goodix_ts_probe(struct i2c_client *client,
 		return error;
 	}
 
-	error = goodix_read_version(client, &version_info, &id_info);
+	error = goodix_read_version(ts);
 	if (error) {
 		dev_err(&client->dev, "Read version failed.\n");
 		return error;
 	}
 
-	ts->cfg_len = goodix_get_cfg_len(id_info);
+	ts->cfg_len = goodix_get_cfg_len(ts->id);
 
 	error = goodix_get_gpio_config(ts, id);
 	if (error)
@@ -553,24 +708,28 @@ static int goodix_ts_probe(struct i2c_client *client,
 			dev_err(&client->dev, "Controller reset failed.\n");
 			return error;
 		}
-	}
 
-	goodix_read_config(ts);
+		/* update device config */
+		ts->cfg_name = kasprintf(GFP_KERNEL, "goodix_%d_cfg.bin",
+					 ts->id);
+		if (!ts->cfg_name)
+			return -ENOMEM;
 
-	error = goodix_request_input_dev(ts, version_info, id_info);
-	if (error)
-		return error;
+		error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name,
+						&client->dev, GFP_KERNEL, ts,
+						goodix_config_cb);
+		if (error) {
+			dev_err(&client->dev,
+				"Failed to invoke firmware loader: %d\n",
+				error);
+			kfree(ts->cfg_name);
+			return error;
+		}
 
-	irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
-	error = devm_request_threaded_irq(&ts->client->dev, client->irq,
-					  NULL, goodix_ts_irq_handler,
-					  irq_flags, client->name, ts);
-	if (error) {
-		dev_err(&client->dev, "request IRQ failed: %d\n", error);
-		return error;
+		return 0;
 	}
 
-	return 0;
+	return goodix_configure_dev(ts);
 }
 
 static const struct i2c_device_id goodix_ts_id[] = {
-- 
1.9.1


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

* [PATCH v5 4/9] Input: goodix - write configuration data to device
@ 2015-09-07 14:36   ` Irina Tirdea
  0 siblings, 0 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin,
	linux-input-u79uwXL29TY76Z2rM5mHXA
  Cc: Mark Rutland, Octavian Purdila,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Irina Tirdea

Goodix devices can be configured by writing custom data to the device at
init. The configuration data is read with request_firmware from
"goodix_<id>_cfg.bin", where <id> is the product id read from the device
(e.g.: goodix_911_cfg.bin for Goodix GT911, goodix_9271_cfg.bin for
GT9271).

The configuration information has a specific format described in the Goodix
datasheet. It includes X/Y resolution, maximum supported touch points,
interrupt flags, various sesitivity factors and settings for advanced
features (like gesture recognition).

Before writing the firmware, it is necessary to reset the device. If
the device ACPI/DT information does not declare gpio pins (needed for
reset), writing the firmware will not be available for these devices.

This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
driver gt9xx.c for Android (publicly available in Android kernel
trees for various devices).

Signed-off-by: Octavian Purdila <octavian.purdila-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Signed-off-by: Irina Tirdea <irina.tirdea-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
 drivers/input/touchscreen/goodix.c | 225 +++++++++++++++++++++++++++++++------
 1 file changed, 192 insertions(+), 33 deletions(-)

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 8edfc06..9cf16ff7 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -17,6 +17,7 @@
 #include <linux/acpi.h>
 #include <linux/delay.h>
 #include <linux/dmi.h>
+#include <linux/firmware.h>
 #include <linux/gpio.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
@@ -40,6 +41,9 @@ struct goodix_ts_data {
 	int cfg_len;
 	struct gpio_desc *gpiod_int;
 	struct gpio_desc *gpiod_rst;
+	u16 id;
+	u16 version;
+	char *cfg_name;
 };
 
 #define GOODIX_MAX_HEIGHT		4096
@@ -145,6 +149,39 @@ static int goodix_i2c_read(struct i2c_client *client,
 	return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
 }
 
+/**
+ * goodix_i2c_write - write data to a register of the i2c slave device.
+ *
+ * @client: i2c device.
+ * @reg: the register to write to.
+ * @buf: raw data buffer to write.
+ * @len: length of the buffer to write
+ */
+static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
+			    unsigned len)
+{
+	u8 *addr_buf;
+	struct i2c_msg msg;
+	int ret;
+
+	addr_buf = kmalloc(len + 2, GFP_KERNEL);
+	if (!addr_buf)
+		return -ENOMEM;
+
+	addr_buf[0] = reg >> 8;
+	addr_buf[1] = reg & 0xFF;
+	memcpy(&addr_buf[2], buf, len);
+
+	msg.flags = 0;
+	msg.addr = client->addr;
+	msg.buf = addr_buf;
+	msg.len = len + 2;
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	kfree(addr_buf);
+	return ret < 0 ? ret : (ret != 1 ? -EIO : 0);
+}
+
 static int goodix_get_cfg_len(u16 id)
 {
 	switch (id) {
@@ -264,6 +301,73 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+/**
+ * goodix_check_cfg - Checks if config fw is valid
+ *
+ * @ts: goodix_ts_data pointer
+ * @cfg: firmware config data
+ */
+static int goodix_check_cfg(struct goodix_ts_data *ts,
+			    const struct firmware *cfg)
+{
+	int i, raw_cfg_len;
+	u8 check_sum = 0;
+
+	if (cfg->size > GOODIX_CONFIG_MAX_LENGTH) {
+		dev_err(&ts->client->dev,
+			"The length of the config fw is not correct");
+		return -EINVAL;
+	}
+
+	raw_cfg_len = cfg->size - 2;
+	for (i = 0; i < raw_cfg_len; i++)
+		check_sum += cfg->data[i];
+	check_sum = (~check_sum) + 1;
+	if (check_sum != cfg->data[raw_cfg_len]) {
+		dev_err(&ts->client->dev,
+			"The checksum of the config fw is not correct");
+		return -EINVAL;
+	}
+
+	if (cfg->data[raw_cfg_len + 1] != 1) {
+		dev_err(&ts->client->dev,
+			"Config fw must have Config_Fresh register set");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * goodix_send_cfg - Write fw config to device
+ *
+ * @ts: goodix_ts_data pointer
+ * @cfg: config firmware to write to device
+ */
+static int goodix_send_cfg(struct goodix_ts_data *ts,
+			   const struct firmware *cfg)
+{
+	int error;
+
+	error = goodix_check_cfg(ts, cfg);
+	if (error)
+		return error;
+
+	error = goodix_i2c_write(ts->client, GOODIX_REG_CONFIG_DATA, cfg->data,
+				 cfg->size);
+	if (error) {
+		dev_err(&ts->client->dev, "Failed to write config data: %d",
+			error);
+		return error;
+	}
+	dev_dbg(&ts->client->dev, "Config sent successfully.");
+
+	/* Let the firmware reconfigure itself, so sleep for 10ms */
+	usleep_range(10000, 11000);
+
+	return 0;
+}
+
 static int goodix_int_sync(struct goodix_ts_data *ts)
 {
 	int error;
@@ -406,30 +510,29 @@ static void goodix_read_config(struct goodix_ts_data *ts)
 /**
  * goodix_read_version - Read goodix touchscreen version
  *
- * @client: the i2c client
- * @version: output buffer containing the version on success
- * @id: output buffer containing the id on success
+ * @ts: our goodix_ts_data pointer
  */
-static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id)
+static int goodix_read_version(struct goodix_ts_data *ts)
 {
 	int error;
 	u8 buf[6];
 	char id_str[5];
 
-	error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf));
+	error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf));
 	if (error) {
-		dev_err(&client->dev, "read version failed: %d\n", error);
+		dev_err(&ts->client->dev, "read version failed: %d\n", error);
 		return error;
 	}
 
 	memcpy(id_str, buf, 4);
 	id_str[4] = 0;
-	if (kstrtou16(id_str, 10, id))
-		*id = 0x1001;
+	if (kstrtou16(id_str, 10, &ts->id))
+		ts->id = 0x1001;
 
-	*version = get_unaligned_le16(&buf[4]);
+	ts->version = get_unaligned_le16(&buf[4]);
 
-	dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version);
+	dev_info(&ts->client->dev, "ID %d, version: %04x\n", ts->id,
+		 ts->version);
 
 	return 0;
 }
@@ -463,13 +566,10 @@ static int goodix_i2c_test(struct i2c_client *client)
  * goodix_request_input_dev - Allocate, populate and register the input device
  *
  * @ts: our goodix_ts_data pointer
- * @version: device firmware version
- * @id: device ID
  *
  * Must be called during probe
  */
-static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
-				    u16 id)
+static int goodix_request_input_dev(struct goodix_ts_data *ts)
 {
 	int error;
 
@@ -493,8 +593,8 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
 	ts->input_dev->phys = "input/ts";
 	ts->input_dev->id.bustype = BUS_I2C;
 	ts->input_dev->id.vendor = 0x0416;
-	ts->input_dev->id.product = id;
-	ts->input_dev->id.version = version;
+	ts->input_dev->id.product = ts->id;
+	ts->input_dev->id.version = ts->version;
 
 	error = input_register_device(ts->input_dev);
 	if (error) {
@@ -506,13 +606,68 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
 	return 0;
 }
 
+/**
+ * goodix_configure_dev - Finish device initialization
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * Must be called from request_firmware_wait callback to
+ * finish initialization of the device.
+ */
+static int goodix_configure_dev(struct goodix_ts_data *ts)
+{
+	int error;
+	unsigned long irq_flags;
+
+	goodix_read_config(ts);
+
+	error = goodix_request_input_dev(ts);
+	if (error)
+		return error;
+
+	irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
+	error = devm_request_threaded_irq(&ts->client->dev, ts->client->irq,
+					  NULL, goodix_ts_irq_handler,
+					  irq_flags, ts->client->name, ts);
+	if (error) {
+		dev_err(&ts->client->dev, "request IRQ failed: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+/**
+ * goodix_config_cb - Callback to finish device init
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * request_firmware_wait callback that finishes
+ * initialization of the device.
+ */
+static void goodix_config_cb(const struct firmware *cfg, void *ctx)
+{
+	struct goodix_ts_data *ts = (struct goodix_ts_data *)ctx;
+	int error;
+
+	if (cfg) {
+		/* send device configuration to the firmware */
+		error = goodix_send_cfg(ts, cfg);
+		if (error)
+			goto err_release_cfg;
+	}
+	goodix_configure_dev(ts);
+
+err_release_cfg:
+	kfree(ts->cfg_name);
+	release_firmware(cfg);
+}
+
 static int goodix_ts_probe(struct i2c_client *client,
 			   const struct i2c_device_id *id)
 {
 	struct goodix_ts_data *ts;
-	unsigned long irq_flags;
 	int error;
-	u16 version_info, id_info;
 
 	dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
 
@@ -534,13 +689,13 @@ static int goodix_ts_probe(struct i2c_client *client,
 		return error;
 	}
 
-	error = goodix_read_version(client, &version_info, &id_info);
+	error = goodix_read_version(ts);
 	if (error) {
 		dev_err(&client->dev, "Read version failed.\n");
 		return error;
 	}
 
-	ts->cfg_len = goodix_get_cfg_len(id_info);
+	ts->cfg_len = goodix_get_cfg_len(ts->id);
 
 	error = goodix_get_gpio_config(ts, id);
 	if (error)
@@ -553,24 +708,28 @@ static int goodix_ts_probe(struct i2c_client *client,
 			dev_err(&client->dev, "Controller reset failed.\n");
 			return error;
 		}
-	}
 
-	goodix_read_config(ts);
+		/* update device config */
+		ts->cfg_name = kasprintf(GFP_KERNEL, "goodix_%d_cfg.bin",
+					 ts->id);
+		if (!ts->cfg_name)
+			return -ENOMEM;
 
-	error = goodix_request_input_dev(ts, version_info, id_info);
-	if (error)
-		return error;
+		error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name,
+						&client->dev, GFP_KERNEL, ts,
+						goodix_config_cb);
+		if (error) {
+			dev_err(&client->dev,
+				"Failed to invoke firmware loader: %d\n",
+				error);
+			kfree(ts->cfg_name);
+			return error;
+		}
 
-	irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
-	error = devm_request_threaded_irq(&ts->client->dev, client->irq,
-					  NULL, goodix_ts_irq_handler,
-					  irq_flags, client->name, ts);
-	if (error) {
-		dev_err(&client->dev, "request IRQ failed: %d\n", error);
-		return error;
+		return 0;
 	}
 
-	return 0;
+	return goodix_configure_dev(ts);
 }
 
 static const struct i2c_device_id goodix_ts_id[] = {
-- 
1.9.1

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

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

* [PATCH v5 5/9] Input: goodix - add power management support
  2015-09-07 14:36 [PATCH v5 0/9] Goodix touchscreen enhancements Irina Tirdea
                   ` (3 preceding siblings ...)
  2015-09-07 14:36   ` Irina Tirdea
@ 2015-09-07 14:36 ` Irina Tirdea
  2015-09-07 14:36 ` [PATCH v5 6/9] Input: goodix - use goodix_i2c_write_u8 instead of i2c_master_send Irina Tirdea
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input
  Cc: Mark Rutland, Octavian Purdila, linux-kernel, devicetree, Irina Tirdea

Implement suspend/resume for goodix driver.

The suspend and resume process uses the gpio pins.
If the device ACPI/DT information does not declare gpio pins,
suspend/resume will not be available for these devices.

This is based on Goodix datasheets for GT911 and GT9271
and on Goodix driver gt9xx.c for Android (publicly available
in Android kernel trees for various devices).

Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 drivers/input/touchscreen/goodix.c | 94 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 89 insertions(+), 5 deletions(-)

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 9cf16ff7..3d4a004 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -44,6 +44,7 @@ struct goodix_ts_data {
 	u16 id;
 	u16 version;
 	char *cfg_name;
+	unsigned long irq_flags;
 };
 
 #define GOODIX_MAX_HEIGHT		4096
@@ -57,6 +58,9 @@ struct goodix_ts_data {
 #define GOODIX_CONFIG_967_LENGTH	228
 
 /* Register defines */
+#define GOODIX_REG_COMMAND		0x8040
+#define GOODIX_CMD_SCREEN_OFF		0x05
+
 #define GOODIX_READ_COOR_ADDR		0x814E
 #define GOODIX_REG_CONFIG_DATA		0x8047
 #define GOODIX_REG_ID			0x8140
@@ -182,6 +186,11 @@ static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
 	return ret < 0 ? ret : (ret != 1 ? -EIO : 0);
 }
 
+static int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value)
+{
+	return goodix_i2c_write(client, reg, &value, sizeof(value));
+}
+
 static int goodix_get_cfg_len(u16 id)
 {
 	switch (id) {
@@ -301,6 +310,18 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static void goodix_free_irq(struct goodix_ts_data *ts)
+{
+	devm_free_irq(&ts->client->dev, ts->client->irq, ts);
+}
+
+static int goodix_request_irq(struct goodix_ts_data *ts)
+{
+	return devm_request_threaded_irq(&ts->client->dev, ts->client->irq,
+					 NULL, goodix_ts_irq_handler,
+					 ts->irq_flags, ts->client->name, ts);
+}
+
 /**
  * goodix_check_cfg - Checks if config fw is valid
  *
@@ -617,7 +638,6 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts)
 static int goodix_configure_dev(struct goodix_ts_data *ts)
 {
 	int error;
-	unsigned long irq_flags;
 
 	goodix_read_config(ts);
 
@@ -625,10 +645,8 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
 	if (error)
 		return error;
 
-	irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
-	error = devm_request_threaded_irq(&ts->client->dev, ts->client->irq,
-					  NULL, goodix_ts_irq_handler,
-					  irq_flags, ts->client->name, ts);
+	ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
+	error = goodix_request_irq(ts);
 	if (error) {
 		dev_err(&ts->client->dev, "request IRQ failed: %d\n", error);
 		return error;
@@ -732,6 +750,71 @@ static int goodix_ts_probe(struct i2c_client *client,
 	return goodix_configure_dev(ts);
 }
 
+static int __maybe_unused goodix_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct goodix_ts_data *ts = i2c_get_clientdata(client);
+	int error;
+
+	/* We need gpio pins to suspend/resume */
+	if (!ts->gpiod_int || !ts->gpiod_rst)
+		return 0;
+
+	/* Free IRQ as IRQ pin is used as output in the suspend sequence */
+	goodix_free_irq(ts);
+	/* Output LOW on the INT pin for 5 ms */
+	error = gpiod_direction_output(ts->gpiod_int, 0);
+	if (error) {
+		goodix_request_irq(ts);
+		return error;
+	}
+	usleep_range(5000, 6000);
+
+	error = goodix_i2c_write_u8(ts->client, GOODIX_REG_COMMAND,
+				    GOODIX_CMD_SCREEN_OFF);
+	if (error) {
+		dev_err(&ts->client->dev, "Screen off command failed\n");
+		gpiod_direction_input(ts->gpiod_int);
+		goodix_request_irq(ts);
+		return -EAGAIN;
+	}
+
+	/*
+	 * The datasheet specifies that the interval between sending screen-off
+	 * command and wake-up should be longer than 58 ms. To avoid waking up
+	 * sooner, delay 58ms here.
+	 */
+	msleep(58);
+	return 0;
+}
+
+static int __maybe_unused goodix_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct goodix_ts_data *ts = i2c_get_clientdata(client);
+	int error;
+
+	if (!ts->gpiod_int || !ts->gpiod_rst)
+		return 0;
+
+	/*
+	 * Exit sleep mode by outputting HIGH level to INT pin
+	 * for 2ms~5ms.
+	 */
+	error = gpiod_direction_output(ts->gpiod_int, 1);
+	if (error)
+		return error;
+	usleep_range(2000, 5000);
+
+	error = goodix_int_sync(ts);
+	if (error)
+		return error;
+
+	return goodix_request_irq(ts);
+}
+
+static SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume);
+
 static const struct i2c_device_id goodix_ts_id[] = {
 	{ "GDIX1001:00", 0 },
 	{ }
@@ -767,6 +850,7 @@ static struct i2c_driver goodix_ts_driver = {
 		.owner = THIS_MODULE,
 		.acpi_match_table = ACPI_PTR(goodix_acpi_match),
 		.of_match_table = of_match_ptr(goodix_of_match),
+		.pm = &goodix_pm_ops,
 	},
 };
 module_i2c_driver(goodix_ts_driver);
-- 
1.9.1


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

* [PATCH v5 6/9] Input: goodix - use goodix_i2c_write_u8 instead of i2c_master_send
  2015-09-07 14:36 [PATCH v5 0/9] Goodix touchscreen enhancements Irina Tirdea
                   ` (4 preceding siblings ...)
  2015-09-07 14:36 ` [PATCH v5 5/9] Input: goodix - add power management support Irina Tirdea
@ 2015-09-07 14:36 ` Irina Tirdea
  2015-09-07 14:36   ` Irina Tirdea
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input
  Cc: Mark Rutland, Octavian Purdila, linux-kernel, devicetree, Irina Tirdea

Use goodix_i2c_write_u8 instead of i2c_master_send to simplify code.

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 drivers/input/touchscreen/goodix.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 3d4a004..03f3968 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -295,16 +295,11 @@ static void goodix_process_events(struct goodix_ts_data *ts)
  */
 static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
 {
-	static const u8 end_cmd[] = {
-		GOODIX_READ_COOR_ADDR >> 8,
-		GOODIX_READ_COOR_ADDR & 0xff,
-		0
-	};
 	struct goodix_ts_data *ts = dev_id;
 
 	goodix_process_events(ts);
 
-	if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0)
+	if (goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0) < 0)
 		dev_err(&ts->client->dev, "I2C write end_cmd error\n");
 
 	return IRQ_HANDLED;
-- 
1.9.1


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

* [PATCH v5 7/9] Input: goodix - add support for ESD
@ 2015-09-07 14:36   ` Irina Tirdea
  0 siblings, 0 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input
  Cc: Mark Rutland, Octavian Purdila, linux-kernel, devicetree, Irina Tirdea

Add ESD (Electrostatic Discharge) protection mechanism.

The driver enables ESD protection in HW and checks a register
to determine if ESD occurred. If ESD is signalled by the HW,
the driver will reset the device.

The ESD poll time (in ms) can be set through the sysfs property
esd_timeout. If it is set to 0, ESD protection is disabled.
Recommended value is 2000 ms. The initial value for ESD timeout
can be set through esd-recovery-timeout-ms ACPI/DT property.
If there is no such property defined, ESD protection is disabled.
For ACPI 5.1, the property can be specified using _DSD properties:
 Device (STAC)
 {
     Name (_HID, "GDIX1001")
     ...

     Name (_DSD,  Package ()
     {
         ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
         Package ()
         {
             Package (2) { "esd-recovery-timeout-ms", Package(1) { 2000 }},
             ...
         }
     })
 }

The ESD protection mechanism is only available if the gpio pins
are properly initialized from ACPI/DT.

This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
driver gt9xx.c for Android (publicly available in Android kernel
trees for various devices).

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 .../bindings/input/touchscreen/goodix.txt          |   6 +
 drivers/input/touchscreen/goodix.c                 | 174 ++++++++++++++++++++-
 2 files changed, 173 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
index c0715f8..5891ad1 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
@@ -14,6 +14,12 @@ Required properties:
  - interrupts		: Interrupt to which the chip is connected
  - gpios		: GPIOS the chip is connected to: first one is the
 			  interrupt gpio and second one the reset gpio.
+Optional properties:
+
+ - esd-recovery-timeout-ms : ESD poll time (in milli seconds) for the driver to
+                            check if ESD occurred and in that case reset the
+                            device. ESD is disabled if this property is not set
+                            or is set to 0.
 
 Example:
 
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 03f3968..33a7b81 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -45,8 +45,12 @@ struct goodix_ts_data {
 	u16 version;
 	char *cfg_name;
 	unsigned long irq_flags;
+	atomic_t esd_timeout;
+	struct delayed_work esd_work;
 };
 
+#define GOODIX_DEVICE_ESD_TIMEOUT_PROPERTY     "esd-recovery-timeout-ms"
+
 #define GOODIX_MAX_HEIGHT		4096
 #define GOODIX_MAX_WIDTH		4096
 #define GOODIX_INT_TRIGGER		1
@@ -60,6 +64,8 @@ struct goodix_ts_data {
 /* Register defines */
 #define GOODIX_REG_COMMAND		0x8040
 #define GOODIX_CMD_SCREEN_OFF		0x05
+#define GOODIX_CMD_ESD_ENABLED		0xAA
+#define GOODIX_REG_ESD_CHECK		0x8041
 
 #define GOODIX_READ_COOR_ADDR		0x814E
 #define GOODIX_REG_CONFIG_DATA		0x8047
@@ -426,6 +432,117 @@ static int goodix_reset(struct goodix_ts_data *ts)
 	return goodix_int_sync(ts);
 }
 
+static void goodix_disable_esd(struct goodix_ts_data *ts)
+{
+	if (!atomic_read(&ts->esd_timeout))
+		return;
+	cancel_delayed_work_sync(&ts->esd_work);
+}
+
+static int goodix_enable_esd(struct goodix_ts_data *ts)
+{
+	int error, esd_timeout;
+
+	esd_timeout = atomic_read(&ts->esd_timeout);
+	if (!esd_timeout)
+		return 0;
+
+	error = goodix_i2c_write_u8(ts->client, GOODIX_REG_ESD_CHECK,
+				    GOODIX_CMD_ESD_ENABLED);
+	if (error) {
+		dev_err(&ts->client->dev, "Failed to enable ESD: %d\n", error);
+		return error;
+	}
+
+	schedule_delayed_work(&ts->esd_work, round_jiffies_relative(
+			      msecs_to_jiffies(esd_timeout)));
+	return 0;
+}
+
+static void goodix_esd_work(struct work_struct *work)
+{
+	struct goodix_ts_data *ts = container_of(work, struct goodix_ts_data,
+						 esd_work.work);
+	int retries = 3, error;
+	u8 esd_data[2];
+	const struct firmware *cfg = NULL;
+
+	while (--retries) {
+		error = goodix_i2c_read(ts->client, GOODIX_REG_COMMAND,
+					esd_data, sizeof(esd_data));
+		if (error)
+			continue;
+		if (esd_data[0] != GOODIX_CMD_ESD_ENABLED &&
+		    esd_data[1] == GOODIX_CMD_ESD_ENABLED) {
+			/* feed the watchdog */
+			goodix_i2c_write_u8(ts->client,
+					    GOODIX_REG_COMMAND,
+					    GOODIX_CMD_ESD_ENABLED);
+			break;
+		}
+	}
+
+	if (!retries) {
+		dev_dbg(&ts->client->dev, "Performing ESD recovery.\n");
+		goodix_free_irq(ts);
+		goodix_reset(ts);
+		error = request_firmware(&cfg, ts->cfg_name, &ts->client->dev);
+		if (!error) {
+			goodix_send_cfg(ts, cfg);
+			release_firmware(cfg);
+		}
+		goodix_request_irq(ts);
+		goodix_enable_esd(ts);
+		return;
+	}
+
+	schedule_delayed_work(&ts->esd_work, round_jiffies_relative(
+			      msecs_to_jiffies(atomic_read(&ts->esd_timeout))));
+}
+
+static ssize_t goodix_esd_timeout_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct goodix_ts_data *ts = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&ts->esd_timeout));
+}
+
+static ssize_t goodix_esd_timeout_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct goodix_ts_data *ts = dev_get_drvdata(dev);
+	int error, esd_timeout, new_esd_timeout;
+
+	error = kstrtouint(buf, 10, &new_esd_timeout);
+	if (error)
+		return error;
+
+	esd_timeout = atomic_read(&ts->esd_timeout);
+	if (esd_timeout && !new_esd_timeout)
+		goodix_disable_esd(ts);
+
+	atomic_set(&ts->esd_timeout, new_esd_timeout);
+	if (!esd_timeout && new_esd_timeout)
+		goodix_enable_esd(ts);
+
+	return count;
+}
+
+/* ESD timeout in ms. Default disabled (0). Recommended 2000 ms. */
+static DEVICE_ATTR(esd_timeout, S_IRUGO | S_IWUSR, goodix_esd_timeout_show,
+		   goodix_esd_timeout_store);
+
+static struct attribute *goodix_attrs[] = {
+	&dev_attr_esd_timeout.attr,
+	NULL
+};
+
+static const struct attribute_group goodix_attr_group = {
+	.attrs = goodix_attrs,
+};
+
 /**
  * goodix_get_gpio_config - Get GPIO config from ACPI/DT
  *
@@ -647,6 +764,8 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
 		return error;
 	}
 
+	goodix_enable_esd(ts);
+
 	return 0;
 }
 
@@ -672,7 +791,6 @@ static void goodix_config_cb(const struct firmware *cfg, void *ctx)
 	goodix_configure_dev(ts);
 
 err_release_cfg:
-	kfree(ts->cfg_name);
 	release_firmware(cfg);
 }
 
@@ -680,7 +798,7 @@ static int goodix_ts_probe(struct i2c_client *client,
 			   const struct i2c_device_id *id)
 {
 	struct goodix_ts_data *ts;
-	int error;
+	int error, esd_timeout;
 
 	dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
 
@@ -695,6 +813,7 @@ static int goodix_ts_probe(struct i2c_client *client,
 
 	ts->client = client;
 	i2c_set_clientdata(client, ts);
+	INIT_DELAYED_WORK(&ts->esd_work, goodix_esd_work);
 
 	error = goodix_i2c_test(client);
 	if (error) {
@@ -722,11 +841,28 @@ static int goodix_ts_probe(struct i2c_client *client,
 			return error;
 		}
 
+		error = device_property_read_u32(&ts->client->dev,
+					GOODIX_DEVICE_ESD_TIMEOUT_PROPERTY,
+					&esd_timeout);
+		if (!error)
+			atomic_set(&ts->esd_timeout, esd_timeout);
+
+		error = sysfs_create_group(&client->dev.kobj,
+					   &goodix_attr_group);
+		if (error) {
+			dev_err(&client->dev,
+				"Failed to create sysfs group: %d\n",
+				error);
+			return error;
+		}
+
 		/* update device config */
 		ts->cfg_name = kasprintf(GFP_KERNEL, "goodix_%d_cfg.bin",
 					 ts->id);
-		if (!ts->cfg_name)
-			return -ENOMEM;
+		if (!ts->cfg_name) {
+			error = -ENOMEM;
+			goto err_sysfs_remove_group;
+		}
 
 		error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name,
 						&client->dev, GFP_KERNEL, ts,
@@ -735,14 +871,32 @@ static int goodix_ts_probe(struct i2c_client *client,
 			dev_err(&client->dev,
 				"Failed to invoke firmware loader: %d\n",
 				error);
-			kfree(ts->cfg_name);
-			return error;
+			goto err_free_cfg_name;
 		}
 
 		return 0;
 	}
 
 	return goodix_configure_dev(ts);
+
+err_free_cfg_name:
+	if (ts->gpiod_int && ts->gpiod_rst)
+		kfree(ts->cfg_name);
+err_sysfs_remove_group:
+	if (ts->gpiod_int && ts->gpiod_rst)
+		sysfs_remove_group(&client->dev.kobj, &goodix_attr_group);
+	return error;
+}
+
+static int goodix_ts_remove(struct i2c_client *client)
+{
+	struct goodix_ts_data *ts = i2c_get_clientdata(client);
+
+	if (ts->gpiod_int && ts->gpiod_rst)
+		sysfs_remove_group(&client->dev.kobj, &goodix_attr_group);
+	goodix_disable_esd(ts);
+	kfree(ts->cfg_name);
+	return 0;
 }
 
 static int __maybe_unused goodix_suspend(struct device *dev)
@@ -755,6 +909,7 @@ static int __maybe_unused goodix_suspend(struct device *dev)
 	if (!ts->gpiod_int || !ts->gpiod_rst)
 		return 0;
 
+	goodix_disable_esd(ts);
 	/* Free IRQ as IRQ pin is used as output in the suspend sequence */
 	goodix_free_irq(ts);
 	/* Output LOW on the INT pin for 5 ms */
@@ -805,7 +960,11 @@ static int __maybe_unused goodix_resume(struct device *dev)
 	if (error)
 		return error;
 
-	return goodix_request_irq(ts);
+	error = goodix_request_irq(ts);
+	if (error)
+		return error;
+
+	return goodix_enable_esd(ts);
 }
 
 static SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume);
@@ -839,6 +998,7 @@ MODULE_DEVICE_TABLE(of, goodix_of_match);
 
 static struct i2c_driver goodix_ts_driver = {
 	.probe = goodix_ts_probe,
+	.remove = goodix_ts_remove,
 	.id_table = goodix_ts_id,
 	.driver = {
 		.name = "Goodix-TS",
-- 
1.9.1


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

* [PATCH v5 7/9] Input: goodix - add support for ESD
@ 2015-09-07 14:36   ` Irina Tirdea
  0 siblings, 0 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin,
	linux-input-u79uwXL29TY76Z2rM5mHXA
  Cc: Mark Rutland, Octavian Purdila,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Irina Tirdea

Add ESD (Electrostatic Discharge) protection mechanism.

The driver enables ESD protection in HW and checks a register
to determine if ESD occurred. If ESD is signalled by the HW,
the driver will reset the device.

The ESD poll time (in ms) can be set through the sysfs property
esd_timeout. If it is set to 0, ESD protection is disabled.
Recommended value is 2000 ms. The initial value for ESD timeout
can be set through esd-recovery-timeout-ms ACPI/DT property.
If there is no such property defined, ESD protection is disabled.
For ACPI 5.1, the property can be specified using _DSD properties:
 Device (STAC)
 {
     Name (_HID, "GDIX1001")
     ...

     Name (_DSD,  Package ()
     {
         ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
         Package ()
         {
             Package (2) { "esd-recovery-timeout-ms", Package(1) { 2000 }},
             ...
         }
     })
 }

The ESD protection mechanism is only available if the gpio pins
are properly initialized from ACPI/DT.

This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
driver gt9xx.c for Android (publicly available in Android kernel
trees for various devices).

Signed-off-by: Irina Tirdea <irina.tirdea-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
 .../bindings/input/touchscreen/goodix.txt          |   6 +
 drivers/input/touchscreen/goodix.c                 | 174 ++++++++++++++++++++-
 2 files changed, 173 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
index c0715f8..5891ad1 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
@@ -14,6 +14,12 @@ Required properties:
  - interrupts		: Interrupt to which the chip is connected
  - gpios		: GPIOS the chip is connected to: first one is the
 			  interrupt gpio and second one the reset gpio.
+Optional properties:
+
+ - esd-recovery-timeout-ms : ESD poll time (in milli seconds) for the driver to
+                            check if ESD occurred and in that case reset the
+                            device. ESD is disabled if this property is not set
+                            or is set to 0.
 
 Example:
 
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 03f3968..33a7b81 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -45,8 +45,12 @@ struct goodix_ts_data {
 	u16 version;
 	char *cfg_name;
 	unsigned long irq_flags;
+	atomic_t esd_timeout;
+	struct delayed_work esd_work;
 };
 
+#define GOODIX_DEVICE_ESD_TIMEOUT_PROPERTY     "esd-recovery-timeout-ms"
+
 #define GOODIX_MAX_HEIGHT		4096
 #define GOODIX_MAX_WIDTH		4096
 #define GOODIX_INT_TRIGGER		1
@@ -60,6 +64,8 @@ struct goodix_ts_data {
 /* Register defines */
 #define GOODIX_REG_COMMAND		0x8040
 #define GOODIX_CMD_SCREEN_OFF		0x05
+#define GOODIX_CMD_ESD_ENABLED		0xAA
+#define GOODIX_REG_ESD_CHECK		0x8041
 
 #define GOODIX_READ_COOR_ADDR		0x814E
 #define GOODIX_REG_CONFIG_DATA		0x8047
@@ -426,6 +432,117 @@ static int goodix_reset(struct goodix_ts_data *ts)
 	return goodix_int_sync(ts);
 }
 
+static void goodix_disable_esd(struct goodix_ts_data *ts)
+{
+	if (!atomic_read(&ts->esd_timeout))
+		return;
+	cancel_delayed_work_sync(&ts->esd_work);
+}
+
+static int goodix_enable_esd(struct goodix_ts_data *ts)
+{
+	int error, esd_timeout;
+
+	esd_timeout = atomic_read(&ts->esd_timeout);
+	if (!esd_timeout)
+		return 0;
+
+	error = goodix_i2c_write_u8(ts->client, GOODIX_REG_ESD_CHECK,
+				    GOODIX_CMD_ESD_ENABLED);
+	if (error) {
+		dev_err(&ts->client->dev, "Failed to enable ESD: %d\n", error);
+		return error;
+	}
+
+	schedule_delayed_work(&ts->esd_work, round_jiffies_relative(
+			      msecs_to_jiffies(esd_timeout)));
+	return 0;
+}
+
+static void goodix_esd_work(struct work_struct *work)
+{
+	struct goodix_ts_data *ts = container_of(work, struct goodix_ts_data,
+						 esd_work.work);
+	int retries = 3, error;
+	u8 esd_data[2];
+	const struct firmware *cfg = NULL;
+
+	while (--retries) {
+		error = goodix_i2c_read(ts->client, GOODIX_REG_COMMAND,
+					esd_data, sizeof(esd_data));
+		if (error)
+			continue;
+		if (esd_data[0] != GOODIX_CMD_ESD_ENABLED &&
+		    esd_data[1] == GOODIX_CMD_ESD_ENABLED) {
+			/* feed the watchdog */
+			goodix_i2c_write_u8(ts->client,
+					    GOODIX_REG_COMMAND,
+					    GOODIX_CMD_ESD_ENABLED);
+			break;
+		}
+	}
+
+	if (!retries) {
+		dev_dbg(&ts->client->dev, "Performing ESD recovery.\n");
+		goodix_free_irq(ts);
+		goodix_reset(ts);
+		error = request_firmware(&cfg, ts->cfg_name, &ts->client->dev);
+		if (!error) {
+			goodix_send_cfg(ts, cfg);
+			release_firmware(cfg);
+		}
+		goodix_request_irq(ts);
+		goodix_enable_esd(ts);
+		return;
+	}
+
+	schedule_delayed_work(&ts->esd_work, round_jiffies_relative(
+			      msecs_to_jiffies(atomic_read(&ts->esd_timeout))));
+}
+
+static ssize_t goodix_esd_timeout_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct goodix_ts_data *ts = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&ts->esd_timeout));
+}
+
+static ssize_t goodix_esd_timeout_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct goodix_ts_data *ts = dev_get_drvdata(dev);
+	int error, esd_timeout, new_esd_timeout;
+
+	error = kstrtouint(buf, 10, &new_esd_timeout);
+	if (error)
+		return error;
+
+	esd_timeout = atomic_read(&ts->esd_timeout);
+	if (esd_timeout && !new_esd_timeout)
+		goodix_disable_esd(ts);
+
+	atomic_set(&ts->esd_timeout, new_esd_timeout);
+	if (!esd_timeout && new_esd_timeout)
+		goodix_enable_esd(ts);
+
+	return count;
+}
+
+/* ESD timeout in ms. Default disabled (0). Recommended 2000 ms. */
+static DEVICE_ATTR(esd_timeout, S_IRUGO | S_IWUSR, goodix_esd_timeout_show,
+		   goodix_esd_timeout_store);
+
+static struct attribute *goodix_attrs[] = {
+	&dev_attr_esd_timeout.attr,
+	NULL
+};
+
+static const struct attribute_group goodix_attr_group = {
+	.attrs = goodix_attrs,
+};
+
 /**
  * goodix_get_gpio_config - Get GPIO config from ACPI/DT
  *
@@ -647,6 +764,8 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
 		return error;
 	}
 
+	goodix_enable_esd(ts);
+
 	return 0;
 }
 
@@ -672,7 +791,6 @@ static void goodix_config_cb(const struct firmware *cfg, void *ctx)
 	goodix_configure_dev(ts);
 
 err_release_cfg:
-	kfree(ts->cfg_name);
 	release_firmware(cfg);
 }
 
@@ -680,7 +798,7 @@ static int goodix_ts_probe(struct i2c_client *client,
 			   const struct i2c_device_id *id)
 {
 	struct goodix_ts_data *ts;
-	int error;
+	int error, esd_timeout;
 
 	dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
 
@@ -695,6 +813,7 @@ static int goodix_ts_probe(struct i2c_client *client,
 
 	ts->client = client;
 	i2c_set_clientdata(client, ts);
+	INIT_DELAYED_WORK(&ts->esd_work, goodix_esd_work);
 
 	error = goodix_i2c_test(client);
 	if (error) {
@@ -722,11 +841,28 @@ static int goodix_ts_probe(struct i2c_client *client,
 			return error;
 		}
 
+		error = device_property_read_u32(&ts->client->dev,
+					GOODIX_DEVICE_ESD_TIMEOUT_PROPERTY,
+					&esd_timeout);
+		if (!error)
+			atomic_set(&ts->esd_timeout, esd_timeout);
+
+		error = sysfs_create_group(&client->dev.kobj,
+					   &goodix_attr_group);
+		if (error) {
+			dev_err(&client->dev,
+				"Failed to create sysfs group: %d\n",
+				error);
+			return error;
+		}
+
 		/* update device config */
 		ts->cfg_name = kasprintf(GFP_KERNEL, "goodix_%d_cfg.bin",
 					 ts->id);
-		if (!ts->cfg_name)
-			return -ENOMEM;
+		if (!ts->cfg_name) {
+			error = -ENOMEM;
+			goto err_sysfs_remove_group;
+		}
 
 		error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name,
 						&client->dev, GFP_KERNEL, ts,
@@ -735,14 +871,32 @@ static int goodix_ts_probe(struct i2c_client *client,
 			dev_err(&client->dev,
 				"Failed to invoke firmware loader: %d\n",
 				error);
-			kfree(ts->cfg_name);
-			return error;
+			goto err_free_cfg_name;
 		}
 
 		return 0;
 	}
 
 	return goodix_configure_dev(ts);
+
+err_free_cfg_name:
+	if (ts->gpiod_int && ts->gpiod_rst)
+		kfree(ts->cfg_name);
+err_sysfs_remove_group:
+	if (ts->gpiod_int && ts->gpiod_rst)
+		sysfs_remove_group(&client->dev.kobj, &goodix_attr_group);
+	return error;
+}
+
+static int goodix_ts_remove(struct i2c_client *client)
+{
+	struct goodix_ts_data *ts = i2c_get_clientdata(client);
+
+	if (ts->gpiod_int && ts->gpiod_rst)
+		sysfs_remove_group(&client->dev.kobj, &goodix_attr_group);
+	goodix_disable_esd(ts);
+	kfree(ts->cfg_name);
+	return 0;
 }
 
 static int __maybe_unused goodix_suspend(struct device *dev)
@@ -755,6 +909,7 @@ static int __maybe_unused goodix_suspend(struct device *dev)
 	if (!ts->gpiod_int || !ts->gpiod_rst)
 		return 0;
 
+	goodix_disable_esd(ts);
 	/* Free IRQ as IRQ pin is used as output in the suspend sequence */
 	goodix_free_irq(ts);
 	/* Output LOW on the INT pin for 5 ms */
@@ -805,7 +960,11 @@ static int __maybe_unused goodix_resume(struct device *dev)
 	if (error)
 		return error;
 
-	return goodix_request_irq(ts);
+	error = goodix_request_irq(ts);
+	if (error)
+		return error;
+
+	return goodix_enable_esd(ts);
 }
 
 static SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume);
@@ -839,6 +998,7 @@ MODULE_DEVICE_TABLE(of, goodix_of_match);
 
 static struct i2c_driver goodix_ts_driver = {
 	.probe = goodix_ts_probe,
+	.remove = goodix_ts_remove,
 	.id_table = goodix_ts_id,
 	.driver = {
 		.name = "Goodix-TS",
-- 
1.9.1

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

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

* [PATCH v5 8/9] Input: goodix - add sysfs interface to dump config
@ 2015-09-07 14:36   ` Irina Tirdea
  0 siblings, 0 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input
  Cc: Mark Rutland, Octavian Purdila, linux-kernel, devicetree, Irina Tirdea

Goodix devices have a configuration information register area that
specify various parameters for the device. The configuration information
has a specific format described in the Goodix datasheet. It includes X/Y
resolution, maximum supported touch points, interrupt flags, various
sesitivity factors and settings for advanced features (like gesture
recognition).

Export a sysfs interface that would allow reading the configuration
information. The default device configuration can be used as a starting
point for creating a valid configuration firmware used by the device at
init time to update its configuration.

This sysfs interface will be exported only if the gpio pins are properly
initialized from ACPI/DT.

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 drivers/input/touchscreen/goodix.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 33a7b81..3179767 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -530,12 +530,35 @@ static ssize_t goodix_esd_timeout_store(struct device *dev,
 	return count;
 }
 
+static ssize_t goodix_dump_config_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct goodix_ts_data *ts = dev_get_drvdata(dev);
+	u8 config[GOODIX_CONFIG_MAX_LENGTH];
+	int error, count = 0, i;
+
+	error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
+				config, ts->cfg_len);
+	if (error) {
+		dev_warn(&ts->client->dev,
+			 "Error reading config (%d)\n",  error);
+		return error;
+	}
+
+	for (i = 0; i < ts->cfg_len; i++)
+		count += scnprintf(buf + count, PAGE_SIZE - count, "%02x ",
+				   config[i]);
+	return count;
+}
+
 /* ESD timeout in ms. Default disabled (0). Recommended 2000 ms. */
 static DEVICE_ATTR(esd_timeout, S_IRUGO | S_IWUSR, goodix_esd_timeout_show,
 		   goodix_esd_timeout_store);
+static DEVICE_ATTR(dump_config, S_IRUGO, goodix_dump_config_show, NULL);
 
 static struct attribute *goodix_attrs[] = {
 	&dev_attr_esd_timeout.attr,
+	&dev_attr_dump_config.attr,
 	NULL
 };
 
-- 
1.9.1


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

* [PATCH v5 8/9] Input: goodix - add sysfs interface to dump config
@ 2015-09-07 14:36   ` Irina Tirdea
  0 siblings, 0 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin,
	linux-input-u79uwXL29TY76Z2rM5mHXA
  Cc: Mark Rutland, Octavian Purdila,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Irina Tirdea

Goodix devices have a configuration information register area that
specify various parameters for the device. The configuration information
has a specific format described in the Goodix datasheet. It includes X/Y
resolution, maximum supported touch points, interrupt flags, various
sesitivity factors and settings for advanced features (like gesture
recognition).

Export a sysfs interface that would allow reading the configuration
information. The default device configuration can be used as a starting
point for creating a valid configuration firmware used by the device at
init time to update its configuration.

This sysfs interface will be exported only if the gpio pins are properly
initialized from ACPI/DT.

Signed-off-by: Irina Tirdea <irina.tirdea-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
 drivers/input/touchscreen/goodix.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 33a7b81..3179767 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -530,12 +530,35 @@ static ssize_t goodix_esd_timeout_store(struct device *dev,
 	return count;
 }
 
+static ssize_t goodix_dump_config_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct goodix_ts_data *ts = dev_get_drvdata(dev);
+	u8 config[GOODIX_CONFIG_MAX_LENGTH];
+	int error, count = 0, i;
+
+	error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
+				config, ts->cfg_len);
+	if (error) {
+		dev_warn(&ts->client->dev,
+			 "Error reading config (%d)\n",  error);
+		return error;
+	}
+
+	for (i = 0; i < ts->cfg_len; i++)
+		count += scnprintf(buf + count, PAGE_SIZE - count, "%02x ",
+				   config[i]);
+	return count;
+}
+
 /* ESD timeout in ms. Default disabled (0). Recommended 2000 ms. */
 static DEVICE_ATTR(esd_timeout, S_IRUGO | S_IWUSR, goodix_esd_timeout_show,
 		   goodix_esd_timeout_store);
+static DEVICE_ATTR(dump_config, S_IRUGO, goodix_dump_config_show, NULL);
 
 static struct attribute *goodix_attrs[] = {
 	&dev_attr_esd_timeout.attr,
+	&dev_attr_dump_config.attr,
 	NULL
 };
 
-- 
1.9.1

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

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

* [PATCH v5 9/9] Input: goodix - add runtime power management support
  2015-09-07 14:36 [PATCH v5 0/9] Goodix touchscreen enhancements Irina Tirdea
                   ` (7 preceding siblings ...)
  2015-09-07 14:36   ` Irina Tirdea
@ 2015-09-07 14:36 ` Irina Tirdea
  8 siblings, 0 replies; 25+ messages in thread
From: Irina Tirdea @ 2015-09-07 14:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input
  Cc: Mark Rutland, Octavian Purdila, linux-kernel, devicetree, Irina Tirdea

Add support for runtime power management so that the device is
turned off when not used (when the userspace holds no open
handles of the input device). The device uses autosuspend with a
default delay of 2 seconds, so the device will suspend if no
handles to it are open for 2 seconds.

The runtime management support is only available if the gpio pins
are properly initialized from ACPI/DT.

Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 drivers/input/touchscreen/goodix.c | 57 +++++++++++++++++++++++++++++++++++---
 1 file changed, 53 insertions(+), 4 deletions(-)

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 3179767..34c0183 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -27,6 +27,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <asm/unaligned.h>
 
@@ -75,6 +76,8 @@ struct goodix_ts_data {
 #define MAX_CONTACTS_LOC	5
 #define TRIGGER_LOC		6
 
+#define GOODIX_AUTOSUSPEND_DELAY_MS	2000
+
 static const unsigned long goodix_irq_flags[] = {
 	IRQ_TYPE_EDGE_RISING,
 	IRQ_TYPE_EDGE_FALLING,
@@ -566,6 +569,27 @@ static const struct attribute_group goodix_attr_group = {
 	.attrs = goodix_attrs,
 };
 
+static int goodix_open(struct input_dev *input_dev)
+{
+	struct goodix_ts_data *ts = input_get_drvdata(input_dev);
+	int error;
+
+	error = pm_runtime_get_sync(&ts->client->dev);
+	if (error < 0) {
+		pm_runtime_put_noidle(&ts->client->dev);
+		return error;
+	}
+	return 0;
+}
+
+static void goodix_close(struct input_dev *input_dev)
+{
+	struct goodix_ts_data *ts = input_get_drvdata(input_dev);
+
+	pm_runtime_mark_last_busy(&ts->client->dev);
+	pm_runtime_put_autosuspend(&ts->client->dev);
+}
+
 /**
  * goodix_get_gpio_config - Get GPIO config from ACPI/DT
  *
@@ -751,6 +775,9 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts)
 	ts->input_dev->id.vendor = 0x0416;
 	ts->input_dev->id.product = ts->id;
 	ts->input_dev->id.version = ts->version;
+	ts->input_dev->open = goodix_open;
+	ts->input_dev->close = goodix_close;
+	input_set_drvdata(ts->input_dev, ts);
 
 	error = input_register_device(ts->input_dev);
 	if (error) {
@@ -798,7 +825,8 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
  * @ts: our goodix_ts_data pointer
  *
  * request_firmware_wait callback that finishes
- * initialization of the device.
+ * initialization of the device. This will only be called
+ * when ts->gpiod_int and ts->gpiod_rst are properly initialized.
  */
 static void goodix_config_cb(const struct firmware *cfg, void *ctx)
 {
@@ -811,7 +839,21 @@ static void goodix_config_cb(const struct firmware *cfg, void *ctx)
 		if (error)
 			goto err_release_cfg;
 	}
-	goodix_configure_dev(ts);
+	error = goodix_configure_dev(ts);
+	if (error)
+		goto err_release_cfg;
+
+	error = pm_runtime_set_active(&ts->client->dev);
+	if (error) {
+		dev_err(&ts->client->dev, "failed to set active: %d\n", error);
+		goto err_release_cfg;
+	}
+	/* input_dev is a child of client->dev, ignore it for runtime pm */
+	pm_suspend_ignore_children(&ts->client->dev, true);
+	pm_runtime_enable(&ts->client->dev);
+	pm_runtime_set_autosuspend_delay(&ts->client->dev,
+					 GOODIX_AUTOSUSPEND_DELAY_MS);
+	pm_runtime_use_autosuspend(&ts->client->dev);
 
 err_release_cfg:
 	release_firmware(cfg);
@@ -915,8 +957,12 @@ static int goodix_ts_remove(struct i2c_client *client)
 {
 	struct goodix_ts_data *ts = i2c_get_clientdata(client);
 
-	if (ts->gpiod_int && ts->gpiod_rst)
+	if (ts->gpiod_int && ts->gpiod_rst) {
+		pm_runtime_disable(&client->dev);
+		pm_runtime_set_suspended(&client->dev);
+		pm_runtime_put_noidle(&client->dev);
 		sysfs_remove_group(&client->dev.kobj, &goodix_attr_group);
+	}
 	goodix_disable_esd(ts);
 	kfree(ts->cfg_name);
 	return 0;
@@ -990,7 +1036,10 @@ static int __maybe_unused goodix_resume(struct device *dev)
 	return goodix_enable_esd(ts);
 }
 
-static SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume);
+static const struct dev_pm_ops goodix_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(goodix_suspend, goodix_resume)
+	SET_RUNTIME_PM_OPS(goodix_suspend, goodix_resume, NULL)
+};
 
 static const struct i2c_device_id goodix_ts_id[] = {
 	{ "GDIX1001:00", 0 },
-- 
1.9.1


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

* Re: [PATCH v5 3/9] Input: goodix - reset device at init
  2015-09-07 14:36   ` Irina Tirdea
  (?)
@ 2015-09-09 16:57   ` Bastien Nocera
  -1 siblings, 0 replies; 25+ messages in thread
From: Bastien Nocera @ 2015-09-09 16:57 UTC (permalink / raw)
  To: Irina Tirdea, Dmitry Torokhov, Aleksei Mamlin, linux-input
  Cc: Mark Rutland, Octavian Purdila, linux-kernel, devicetree

On Mon, 2015-09-07 at 17:36 +0300, Irina Tirdea wrote:
> After power on, it is recommended that the driver resets the device.
> The reset procedure timing is described in the datasheet and is used
> at device init (before writing device configuration) and
> for power management. It is a sequence of setting the interrupt
> and reset pins high/low at specific timing intervals. This procedure
> also includes setting the slave address to the one specified in the
> ACPI/device tree.
> 
> This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
> driver gt9xx.c for Android (publicly available in Android kernel
> trees for various devices).
> 
> For reset the driver needs to control the interrupt and
> reset gpio pins (configured through ACPI/device tree). For devices
> that do not have the gpio pins declared, the functionality depending
> on these pins will not be available, but the device can still be used
> with basic functionality.

This throws:
Sep 09 18:22:47 winbook kernel: Goodix-TS i2c-GDIX1001:00: ID 9271, version: 1040
Sep 09 18:22:47 winbook kernel: ------------[ cut here ]------------
Sep 09 18:22:47 winbook kernel: WARNING: CPU: 3 PID: 3298 at drivers/pinctrl/intel/pinctrl-baytrail.c:338 byt_gpio_direction_output+0x97/0xa0()
Sep 09 18:22:47 winbook kernel: Potential Error: Setting GPIO with direct_irq_en to output
Sep 09 18:22:47 winbook kernel: Modules linked in:
Sep 09 18:22:47 winbook kernel:  goodix_backport(OE+) hid_logitech_hidpp hid_logitech_dj cdc_mbim cdc_wdm cdc_ncm usbnet mii uvcvideo videobuf2_vmalloc videobuf2_core videobuf2_memops v4l2_common snd_usb_audio v
Sep 09 18:22:47 winbook kernel:  snd_soc_sst_byt_rt5640_mach coretemp snd_soc_sst_baytrail_pcm iTCO_vendor_support snd_soc_sst_ipc kvm_intel snd_soc_sst_dsp gpio_keys kvm snd_intel_sst_acpi snd_intel_sst_core sn
Sep 09 18:22:47 winbook kernel:  lockd grace sunrpc i915 mmc_block i2c_algo_bit drm_kms_helper drm sdhci_acpi video sdhci mmc_core i2c_hid [last unloaded: goodix]
Sep 09 18:22:47 winbook kernel: CPU: 3 PID: 3298 Comm: insmod Tainted: G           OE   4.2.0-0.rc3.git4.2.fc22.i686 #1
Sep 09 18:22:47 winbook kernel: Hardware name: WinBook TW100/TW100, BIOS 1.02.00 08/25/2014
Sep 09 18:22:47 winbook kernel:  c0d439a7 bb4c1aaf 00000000 de2f7bb4 c0aa23b9 de2f7bf4 de2f7be4 c045c677
Sep 09 18:22:47 winbook kernel:  c0cbe3b8 de2f7c14 00000ce2 c0cbe3f4 00000152 c073cd87 c073cd87 f7c5e0b8
Sep 09 18:22:47 winbook kernel:  f4bb309c f7c5e0b0 de2f7c00 c045c6ee 00000009 de2f7bf4 c0cbe3b8 de2f7c14
Sep 09 18:22:47 winbook kernel: Call Trace:
Sep 09 18:22:47 winbook kernel:  [<c0aa23b9>] dump_stack+0x41/0x52
Sep 09 18:22:47 winbook kernel:  [<c045c677>] warn_slowpath_common+0x87/0xc0
Sep 09 18:22:47 winbook kernel:  [<c073cd87>] ? byt_gpio_direction_output+0x97/0xa0
Sep 09 18:22:47 winbook kernel:  [<c073cd87>] ? byt_gpio_direction_output+0x97/0xa0
Sep 09 18:22:47 winbook kernel:  [<c045c6ee>] warn_slowpath_fmt+0x3e/0x60
Sep 09 18:22:47 winbook kernel:  [<c073cd87>] byt_gpio_direction_output+0x97/0xa0
Sep 09 18:22:47 winbook kernel:  [<c073ccf0>] ? byt_gpio_irq_handler+0xc0/0xc0
Sep 09 18:22:47 winbook kernel:  [<c073f6f9>] _gpiod_direction_output_raw+0x59/0x1c0
Sep 09 18:22:47 winbook kernel:  [<c073f8ca>] gpiod_direction_output+0x2a/0x50
Sep 09 18:22:47 winbook kernel:  [<c04bc74b>] ? msleep+0x2b/0x40
Sep 09 18:22:47 winbook kernel:  [<f935a87e>] goodix_reset+0x3e/0x90 [goodix_backport]
Sep 09 18:22:47 winbook kernel:  [<f935b1ae>] goodix_ts_probe+0x27e/0x5a0 [goodix_backport]
Sep 09 18:22:47 winbook kernel:  [<c090ea01>] i2c_device_probe+0x101/0x1b0
Sep 09 18:22:47 winbook kernel:  [<c0613a05>] ? sysfs_create_link+0x25/0x50
Sep 09 18:22:47 winbook kernel:  [<f935af30>] ? goodix_configure_dev+0x1e0/0x1e0 [goodix_backport]

Which is the same error I had previously:
https://lkml.org/lkml/2015/6/30/434

I was testing this on a Onda v975w, but I'm now testing it on a WinBook
TW100.

<snip>
> +/*
> + * ACPI table specifies gpio pins in this order: first rst pin and
> + * then interrupt pin.
> + */
> +static const struct dmi_system_id goodix_rst_pin_first[] = {
> +#if defined(CONFIG_DMI) && defined(CONFIG_X86)
> +	{
> +		.ident = "WinBook TW100",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "TW100")
> +		}
> +	},

The DSDT for the WinBook one is here:
https://people.gnome.org/~hadess/Winbook%20TW100%20DSDT.dsl

For reference, the DSDT for the Onda, the tablet I tested this on some
months ago:
https://bugzilla.kernel.org/attachment.cgi?id=149331

Cheers

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

* Re: [PATCH v5 1/9] Input: goodix - sort includes alphabetically
  2015-09-07 14:36 ` [PATCH v5 1/9] Input: goodix - sort includes alphabetically Irina Tirdea
@ 2015-09-09 16:58   ` Bastien Nocera
  2015-10-04 19:46   ` Pavel Machek
  1 sibling, 0 replies; 25+ messages in thread
From: Bastien Nocera @ 2015-09-09 16:58 UTC (permalink / raw)
  To: Irina Tirdea, Dmitry Torokhov, Aleksei Mamlin, linux-input
  Cc: Mark Rutland, Octavian Purdila, linux-kernel, devicetree

On Mon, 2015-09-07 at 17:36 +0300, Irina Tirdea wrote:
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>

Acked-by: Bastien Nocera <hadess@hadess.net>

> ---
>  drivers/input/touchscreen/goodix.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/input/touchscreen/goodix.c
> b/drivers/input/touchscreen/goodix.c
> index e36162b..6ae28c5 100644
> --- a/drivers/input/touchscreen/goodix.c
> +++ b/drivers/input/touchscreen/goodix.c
> @@ -14,18 +14,18 @@
>   * Software Foundation; version 2 of the License.
>   */
>  
> -#include <linux/kernel.h>
> +#include <linux/acpi.h>
> +#include <linux/delay.h>
>  #include <linux/dmi.h>
>  #include <linux/i2c.h>
>  #include <linux/input.h>
>  #include <linux/input/mt.h>
> -#include <linux/module.h>
> -#include <linux/delay.h>
> -#include <linux/irq.h>
>  #include <linux/interrupt.h>
> -#include <linux/slab.h>
> -#include <linux/acpi.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
>  #include <linux/of.h>
> +#include <linux/slab.h>
>  #include <asm/unaligned.h>
>  
>  struct goodix_ts_data {

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

* Re: [PATCH v5 2/9] Input: goodix - use actual config length for each device type
  2015-09-07 14:36   ` Irina Tirdea
  (?)
@ 2015-09-09 16:58   ` Bastien Nocera
  -1 siblings, 0 replies; 25+ messages in thread
From: Bastien Nocera @ 2015-09-09 16:58 UTC (permalink / raw)
  To: Irina Tirdea, Dmitry Torokhov, Aleksei Mamlin, linux-input
  Cc: Mark Rutland, Octavian Purdila, linux-kernel, devicetree

On Mon, 2015-09-07 at 17:36 +0300, Irina Tirdea wrote:
> Each of the Goodix devices supported by this driver has a fixed size
> for
> the configuration information registers. The size varies depending on
> the
> device and is specified in the datasheet.
> 
> Use the proper configuration length as specified in the datasheet for
> each device model, so we do not read more than the actual size of the
> configuration registers.
> 
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>

Acked-by: Bastien Nocera <hadess@hadess.net>

> ---
>  drivers/input/touchscreen/goodix.c | 25 +++++++++++++++++++++++--
>  1 file changed, 23 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/input/touchscreen/goodix.c
> b/drivers/input/touchscreen/goodix.c
> index 6ae28c5..7be6eab 100644
> --- a/drivers/input/touchscreen/goodix.c
> +++ b/drivers/input/touchscreen/goodix.c
> @@ -36,6 +36,7 @@ struct goodix_ts_data {
>  	unsigned int max_touch_num;
>  	unsigned int int_trigger_type;
>  	bool rotated_screen;
> +	int cfg_len;
>  };
>  
>  #define GOODIX_MAX_HEIGHT		4096
> @@ -45,6 +46,8 @@ struct goodix_ts_data {
>  #define GOODIX_MAX_CONTACTS		10
>  
>  #define GOODIX_CONFIG_MAX_LENGTH	240
> +#define GOODIX_CONFIG_911_LENGTH	186
> +#define GOODIX_CONFIG_967_LENGTH	228
>  
>  /* Register defines */
>  #define GOODIX_READ_COOR_ADDR		0x814E
> @@ -115,6 +118,23 @@ static int goodix_i2c_read(struct i2c_client
> *client,
>  	return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
>  }
>  
> +static int goodix_get_cfg_len(u16 id)
> +{
> +	switch (id) {
> +	case 911:
> +	case 9271:
> +	case 9110:
> +	case 927:
> +	case 928:
> +		return GOODIX_CONFIG_911_LENGTH;
> +	case 912:
> +	case 967:
> +		return GOODIX_CONFIG_967_LENGTH;
> +	default:
> +		return GOODIX_CONFIG_MAX_LENGTH;
> +	}
> +}
> +
>  static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8
> *data)
>  {
>  	int touch_num;
> @@ -230,8 +250,7 @@ static void goodix_read_config(struct
> goodix_ts_data *ts)
>  	int error;
>  
>  	error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
> -				config,
> -				GOODIX_CONFIG_MAX_LENGTH);
> +				config, ts->cfg_len);
>  	if (error) {
>  		dev_warn(&ts->client->dev,
>  			 "Error reading config (%d), using
> defaults\n",
> @@ -398,6 +417,8 @@ static int goodix_ts_probe(struct i2c_client
> *client,
>  		return error;
>  	}
>  
> +	ts->cfg_len = goodix_get_cfg_len(id_info);
> +
>  	goodix_read_config(ts);
>  
>  	error = goodix_request_input_dev(ts, version_info, id_info);

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

* Re: [PATCH v5 3/9] Input: goodix - reset device at init
  2015-09-07 14:36   ` Irina Tirdea
  (?)
  (?)
@ 2015-09-15  9:48   ` Aleksei Mamlin
  2015-09-15 14:27     ` Tirdea, Irina
  -1 siblings, 1 reply; 25+ messages in thread
From: Aleksei Mamlin @ 2015-09-15  9:48 UTC (permalink / raw)
  To: Irina Tirdea
  Cc: Dmitry Torokhov, Bastien Nocera, linux-input, Mark Rutland,
	Octavian Purdila, linux-kernel, devicetree

On Mon,  7 Sep 2015 17:36:17 +0300
Irina Tirdea <irina.tirdea@intel.com> wrote:

> After power on, it is recommended that the driver resets the device.
> The reset procedure timing is described in the datasheet and is used
> at device init (before writing device configuration) and
> for power management. It is a sequence of setting the interrupt
> and reset pins high/low at specific timing intervals. This procedure
> also includes setting the slave address to the one specified in the
> ACPI/device tree.
> 
> This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
> driver gt9xx.c for Android (publicly available in Android kernel
> trees for various devices).
> 
> For reset the driver needs to control the interrupt and
> reset gpio pins (configured through ACPI/device tree). For devices
> that do not have the gpio pins declared, the functionality depending
> on these pins will not be available, but the device can still be used
> with basic functionality.
> 
> Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> ---
>  .../bindings/input/touchscreen/goodix.txt          |   5 +
>  drivers/input/touchscreen/goodix.c                 | 136 +++++++++++++++++++++
>  2 files changed, 141 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
> index 8ba98ee..c0715f8 100644
> --- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
> +++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
> @@ -12,6 +12,8 @@ Required properties:
>   - reg			: I2C address of the chip. Should be 0x5d or 0x14
>   - interrupt-parent	: Interrupt controller to which the chip is connected
>   - interrupts		: Interrupt to which the chip is connected
> + - gpios		: GPIOS the chip is connected to: first one is the
> +			  interrupt gpio and second one the reset gpio.
>  
>  Example:
>  
> @@ -23,6 +25,9 @@ Example:
>  			reg = <0x5d>;
>  			interrupt-parent = <&gpio>;
>  			interrupts = <0 0>;
> +
> +			gpios = <&gpio1 0 0>, /* INT */
> +				<&gpio1 1 0>; /* RST */
>  		};
>  
>  		/* ... */
> diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
> index 7be6eab..8edfc06 100644
> --- a/drivers/input/touchscreen/goodix.c
> +++ b/drivers/input/touchscreen/goodix.c
> @@ -17,6 +17,7 @@
>  #include <linux/acpi.h>
>  #include <linux/delay.h>
>  #include <linux/dmi.h>
> +#include <linux/gpio.h>
>  #include <linux/i2c.h>
>  #include <linux/input.h>
>  #include <linux/input/mt.h>
> @@ -37,6 +38,8 @@ struct goodix_ts_data {
>  	unsigned int int_trigger_type;
>  	bool rotated_screen;
>  	int cfg_len;
> +	struct gpio_desc *gpiod_int;
> +	struct gpio_desc *gpiod_rst;
>  };
>  
>  #define GOODIX_MAX_HEIGHT		4096
> @@ -89,6 +92,30 @@ static const struct dmi_system_id rotated_screen[] = {
>  	{}
>  };
>  
> +/*
> + * ACPI table specifies gpio pins in this order: first rst pin and
> + * then interrupt pin.
> + */
> +static const struct dmi_system_id goodix_rst_pin_first[] = {
> +#if defined(CONFIG_DMI) && defined(CONFIG_X86)
> +	{
> +		.ident = "WinBook TW100",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "TW100")
> +		}
> +	},
> +	{
> +		.ident = "WinBook TW700",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "TW700")
> +		},
> +	},
> +#endif
> +	{}
> +};
> +
>  /**
>   * goodix_i2c_read - read data from a register of the i2c slave device.
>   *
> @@ -237,6 +264,102 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
>  	return IRQ_HANDLED;
>  }
>  
> +static int goodix_int_sync(struct goodix_ts_data *ts)
> +{
> +	int error;
> +
> +	error = gpiod_direction_output(ts->gpiod_int, 0);
> +	if (error)
> +		return error;
> +	msleep(50);				/* T5: 50ms */
> +
> +	return gpiod_direction_input(ts->gpiod_int);
> +}
> +
> +/**
> + * goodix_reset - Reset device during power on
> + *
> + * @ts: goodix_ts_data pointer
> + */
> +static int goodix_reset(struct goodix_ts_data *ts)
> +{
> +	int error;
> +
> +	/* begin select I2C slave addr */
> +	error = gpiod_direction_output(ts->gpiod_rst, 0);
> +	if (error)
> +		return error;
> +	msleep(20);				/* T2: > 10ms */
> +	/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
> +	error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14);
> +	if (error)
> +		return error;
> +	usleep_range(100, 2000);		/* T3: > 100us */
> +	error = gpiod_direction_output(ts->gpiod_rst, 1);
> +	if (error)
> +		return error;
> +	usleep_range(6000, 10000);		/* T4: > 5ms */
> +	/* end select I2C slave addr */
> +	error = gpiod_direction_input(ts->gpiod_rst);
> +	if (error)
> +		return error;
> +	return goodix_int_sync(ts);
> +}
> +
> +/**
> + * goodix_get_gpio_config - Get GPIO config from ACPI/DT
> + *
> + * @ts: goodix_ts_data pointer
> + */
> +static int goodix_get_gpio_config(struct goodix_ts_data *ts,
> +				  const struct i2c_device_id *i2c_id)
> +{
> +	int error;
> +	struct device *dev;
> +	struct gpio_desc *gpiod;
> +	/* Default gpio pin order: irq, rst */
> +	int irq_idx = 0, rst_idx = 1;
> +
> +	if (!ts->client)
> +		return -EINVAL;
> +	dev = &ts->client->dev;
> +
> +	if (dmi_check_system(goodix_rst_pin_first)) {
> +		dev_dbg(&ts->client->dev,
> +			"Applying 'reverse gpio pin order' quirk\n");
> +		rst_idx = 0;
> +		irq_idx = 1;
> +	}
> +
> +	/* Get interrupt GPIO pin number */
> +	gpiod = devm_gpiod_get_index(dev, NULL, irq_idx, GPIOD_IN);
> +	if (IS_ERR(gpiod)) {
> +		error = PTR_ERR(gpiod);
> +		if (error != -EPROBE_DEFER)
> +			dev_warn(dev, "Failed to get GPIO %d: %d\n",
> +				 irq_idx, error);
> +		if (error == -ENOENT)
> +			return 0;
> +		return error;
> +	}
> +	ts->gpiod_int = gpiod;
> +
> +	/* Get the reset line GPIO pin number */
> +	gpiod = devm_gpiod_get_index(dev, NULL, rst_idx, GPIOD_IN);
> +	if (IS_ERR(gpiod)) {
> +		error = PTR_ERR(gpiod);
> +		if (error != -EPROBE_DEFER)
> +			dev_warn(dev, "Failed to get GPIO %d: %d\n",
> +				 rst_idx, error);
> +		if (error == -ENOENT)
> +			return 0;
> +		return error;
> +	}
> +	ts->gpiod_rst = gpiod;
> +
> +	return 0;
> +}
> +
>  /**
>   * goodix_read_config - Read the embedded configuration of the panel
>   *
> @@ -419,6 +542,19 @@ static int goodix_ts_probe(struct i2c_client *client,
>  
>  	ts->cfg_len = goodix_get_cfg_len(id_info);
>  
> +	error = goodix_get_gpio_config(ts, id);
> +	if (error)
> +		return error;
> +
> +	if (ts->gpiod_int && ts->gpiod_rst) {
> +		/* reset the controller */
> +		error = goodix_reset(ts);
> +		if (error) {
> +			dev_err(&client->dev, "Controller reset failed.\n");
> +			return error;
> +		}
> +	}
> +

On devices with devicetree, such as ARM tablets, we can set I2C address via DT, so driver should reset controller and set right address.
If we don't do this we get "I2C communication failure: -6".

Also, most of touchscreen drivers tries to reset controllers before start communicating, so we need do the same.

>  	goodix_read_config(ts);
>  
>  	error = goodix_request_input_dev(ts, version_info, id_info);
> -- 
> 1.9.1
> 


-- 
Thanks and regards,
Aleksei Mamlin

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

* RE: [PATCH v5 3/9] Input: goodix - reset device at init
  2015-09-15  9:48   ` Aleksei Mamlin
@ 2015-09-15 14:27     ` Tirdea, Irina
  0 siblings, 0 replies; 25+ messages in thread
From: Tirdea, Irina @ 2015-09-15 14:27 UTC (permalink / raw)
  To: Aleksei Mamlin
  Cc: Dmitry Torokhov, Bastien Nocera, linux-input, Mark Rutland,
	Purdila, Octavian, linux-kernel, devicetree



> -----Original Message-----
> From: Aleksei Mamlin [mailto:mamlinav@gmail.com]
> Sent: 15 September, 2015 12:48
> To: Tirdea, Irina
> Cc: Dmitry Torokhov; Bastien Nocera; linux-input@vger.kernel.org; Mark Rutland; Purdila, Octavian; linux-kernel@vger.kernel.org;
> devicetree@vger.kernel.org
> Subject: Re: [PATCH v5 3/9] Input: goodix - reset device at init
> 
> On Mon,  7 Sep 2015 17:36:17 +0300
> Irina Tirdea <irina.tirdea@intel.com> wrote:
> 
> > After power on, it is recommended that the driver resets the device.
> > The reset procedure timing is described in the datasheet and is used
> > at device init (before writing device configuration) and
> > for power management. It is a sequence of setting the interrupt
> > and reset pins high/low at specific timing intervals. This procedure
> > also includes setting the slave address to the one specified in the
> > ACPI/device tree.
> >
> > This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
> > driver gt9xx.c for Android (publicly available in Android kernel
> > trees for various devices).
> >
> > For reset the driver needs to control the interrupt and
> > reset gpio pins (configured through ACPI/device tree). For devices
> > that do not have the gpio pins declared, the functionality depending
> > on these pins will not be available, but the device can still be used
> > with basic functionality.
> >
> > Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
> > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> > ---
> >  .../bindings/input/touchscreen/goodix.txt          |   5 +
> >  drivers/input/touchscreen/goodix.c                 | 136 +++++++++++++++++++++
> >  2 files changed, 141 insertions(+)
> >
<snip>
> > +/**
> > + * goodix_reset - Reset device during power on
> > + *
> > + * @ts: goodix_ts_data pointer
> > + */
> > +static int goodix_reset(struct goodix_ts_data *ts)
> > +{
> > +	int error;
> > +
> > +	/* begin select I2C slave addr */
> > +	error = gpiod_direction_output(ts->gpiod_rst, 0);
> > +	if (error)
> > +		return error;
> > +	msleep(20);				/* T2: > 10ms */
> > +	/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
> > +	error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14);
> > +	if (error)
> > +		return error;
> > +	usleep_range(100, 2000);		/* T3: > 100us */
> > +	error = gpiod_direction_output(ts->gpiod_rst, 1);
> > +	if (error)
> > +		return error;
> > +	usleep_range(6000, 10000);		/* T4: > 5ms */
> > +	/* end select I2C slave addr */
> > +	error = gpiod_direction_input(ts->gpiod_rst);
> > +	if (error)
> > +		return error;
> > +	return goodix_int_sync(ts);
> > +}
> > +
<snip>
> >  /**
> >   * goodix_read_config - Read the embedded configuration of the panel
> >   *
> > @@ -419,6 +542,19 @@ static int goodix_ts_probe(struct i2c_client *client,
> >
> >  	ts->cfg_len = goodix_get_cfg_len(id_info);
> >
> > +	error = goodix_get_gpio_config(ts, id);
> > +	if (error)
> > +		return error;
> > +
> > +	if (ts->gpiod_int && ts->gpiod_rst) {
> > +		/* reset the controller */
> > +		error = goodix_reset(ts);
> > +		if (error) {
> > +			dev_err(&client->dev, "Controller reset failed.\n");
> > +			return error;
> > +		}
> > +	}
> > +
> 
> On devices with devicetree, such as ARM tablets, we can set I2C address via DT, so driver should reset controller and set right address.
> If we don't do this we get "I2C communication failure: -6".
>

This is exactly what this patch tries to do. The address set in ACPI or DT will be available
in ts->client->addr. The reset code checks for the address set by ACPI/DT and configures
the device to use that address.
 
> Also, most of touchscreen drivers tries to reset controllers before start communicating, so we need do the same.

Good catch! goodix_reset should be called before goodix_i2c_test. I'll send a new version with this fix.

Thanks,
Irina

> 
> >  	goodix_read_config(ts);
> >
> >  	error = goodix_request_input_dev(ts, version_info, id_info);
> > --
> > 1.9.1
> >
> 
> 
> --
> Thanks and regards,
> Aleksei Mamlin

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

* Re: [PATCH v5 1/9] Input: goodix - sort includes alphabetically
  2015-09-07 14:36 ` [PATCH v5 1/9] Input: goodix - sort includes alphabetically Irina Tirdea
  2015-09-09 16:58   ` Bastien Nocera
@ 2015-10-04 19:46   ` Pavel Machek
  2015-10-05 14:14     ` Tirdea, Irina
  1 sibling, 1 reply; 25+ messages in thread
From: Pavel Machek @ 2015-10-04 19:46 UTC (permalink / raw)
  To: Irina Tirdea
  Cc: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input,
	Mark Rutland, Octavian Purdila, linux-kernel, devicetree

On Mon 2015-09-07 17:36:15, Irina Tirdea wrote:
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> ---
>  drivers/input/touchscreen/goodix.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)

Don't.

Yes, we sometimes sort includes... to avoid patch rejects.

This is guaranteed to cause rejects.

If we do sort them, we use "longest-first" usually.

> 
> diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
> index e36162b..6ae28c5 100644
> --- a/drivers/input/touchscreen/goodix.c
> +++ b/drivers/input/touchscreen/goodix.c
> @@ -14,18 +14,18 @@
>   * Software Foundation; version 2 of the License.
>   */
>  
> -#include <linux/kernel.h>
> +#include <linux/acpi.h>
> +#include <linux/delay.h>
>  #include <linux/dmi.h>
>  #include <linux/i2c.h>
>  #include <linux/input.h>
>  #include <linux/input/mt.h>
> -#include <linux/module.h>
> -#include <linux/delay.h>
> -#include <linux/irq.h>
>  #include <linux/interrupt.h>
> -#include <linux/slab.h>
> -#include <linux/acpi.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
>  #include <linux/of.h>
> +#include <linux/slab.h>
>  #include <asm/unaligned.h>
>  
>  struct goodix_ts_data {
> -- 
> 1.9.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* RE: [PATCH v5 1/9] Input: goodix - sort includes alphabetically
  2015-10-04 19:46   ` Pavel Machek
@ 2015-10-05 14:14     ` Tirdea, Irina
  2015-10-05 20:57         ` Pavel Machek
  0 siblings, 1 reply; 25+ messages in thread
From: Tirdea, Irina @ 2015-10-05 14:14 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input,
	Mark Rutland, Purdila, Octavian, linux-kernel, devicetree



> -----Original Message-----
> From: Pavel Machek [mailto:pavel@ucw.cz]
> Sent: 04 October, 2015 22:47
> To: Tirdea, Irina
> Cc: Dmitry Torokhov; Bastien Nocera; Aleksei Mamlin; linux-input@vger.kernel.org; Mark Rutland; Purdila, Octavian; linux-
> kernel@vger.kernel.org; devicetree@vger.kernel.org
> Subject: Re: [PATCH v5 1/9] Input: goodix - sort includes alphabetically
> 
> On Mon 2015-09-07 17:36:15, Irina Tirdea wrote:
> > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> > ---
> >  drivers/input/touchscreen/goodix.c | 12 ++++++------
> >  1 file changed, 6 insertions(+), 6 deletions(-)
> 
> Don't.
> 
> Yes, we sometimes sort includes... to avoid patch rejects.
> 
> This is guaranteed to cause rejects.
> 
> If we do sort them, we use "longest-first" usually.
> 

When using random order, I've been told by reviewers that my ordering
is wrong [1], but I could not figure out what the "right" ordering is in order to apply.
I did not find any mention of how to sort includes in  Documentation/CodingStyle,
but I've seen patches in the kernel that fix random ordering by sorting them
alphabetically [2].

However, I do not feel strongly about this so I can drop this patch.

Thanks,
Irina

[1] https://lkml.org/lkml/2015/5/28/363
[2] https://patchwork.ozlabs.org/patch/408022/

> >
> > diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
> > index e36162b..6ae28c5 100644
> > --- a/drivers/input/touchscreen/goodix.c
> > +++ b/drivers/input/touchscreen/goodix.c
> > @@ -14,18 +14,18 @@
> >   * Software Foundation; version 2 of the License.
> >   */
> >
> > -#include <linux/kernel.h>
> > +#include <linux/acpi.h>
> > +#include <linux/delay.h>
> >  #include <linux/dmi.h>
> >  #include <linux/i2c.h>
> >  #include <linux/input.h>
> >  #include <linux/input/mt.h>
> > -#include <linux/module.h>
> > -#include <linux/delay.h>
> > -#include <linux/irq.h>
> >  #include <linux/interrupt.h>
> > -#include <linux/slab.h>
> > -#include <linux/acpi.h>
> > +#include <linux/irq.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> >  #include <linux/of.h>
> > +#include <linux/slab.h>
> >  #include <asm/unaligned.h>
> >
> >  struct goodix_ts_data {
> > --
> > 1.9.1
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > Please read the FAQ at  http://www.tux.org/lkml/
> 
> --
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [PATCH v5 1/9] Input: goodix - sort includes alphabetically
@ 2015-10-05 20:57         ` Pavel Machek
  0 siblings, 0 replies; 25+ messages in thread
From: Pavel Machek @ 2015-10-05 20:57 UTC (permalink / raw)
  To: Tirdea, Irina
  Cc: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input,
	Mark Rutland, Purdila, Octavian, linux-kernel, devicetree

Hi!

> > Subject: Re: [PATCH v5 1/9] Input: goodix - sort includes alphabetically
> > 
> > On Mon 2015-09-07 17:36:15, Irina Tirdea wrote:
> > > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> > > ---
> > >  drivers/input/touchscreen/goodix.c | 12 ++++++------
> > >  1 file changed, 6 insertions(+), 6 deletions(-)
> > 
> > Don't.
> > 
> > Yes, we sometimes sort includes... to avoid patch rejects.
> > 
> > This is guaranteed to cause rejects.
> > 
> > If we do sort them, we use "longest-first" usually.
> > 
> 
> When using random order, I've been told by reviewers that my ordering
> is wrong [1], but I could not figure out what the "right" ordering is in order to apply.
> I did not find any mention of how to sort includes in  Documentation/CodingStyle,
> but I've seen patches in the kernel that fix random ordering by sorting them
> alphabetically [2].

Ok, I have seen this more often:

https://lkml.org/lkml/2009/3/28/99
 
> However, I do not feel strongly about this so I can drop this patch.

Well, its really nit picking.

										Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [PATCH v5 1/9] Input: goodix - sort includes alphabetically
@ 2015-10-05 20:57         ` Pavel Machek
  0 siblings, 0 replies; 25+ messages in thread
From: Pavel Machek @ 2015-10-05 20:57 UTC (permalink / raw)
  To: Tirdea, Irina
  Cc: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin,
	linux-input-u79uwXL29TY76Z2rM5mHXA, Mark Rutland, Purdila,
	Octavian, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi!

> > Subject: Re: [PATCH v5 1/9] Input: goodix - sort includes alphabetically
> > 
> > On Mon 2015-09-07 17:36:15, Irina Tirdea wrote:
> > > Signed-off-by: Irina Tirdea <irina.tirdea-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> > > ---
> > >  drivers/input/touchscreen/goodix.c | 12 ++++++------
> > >  1 file changed, 6 insertions(+), 6 deletions(-)
> > 
> > Don't.
> > 
> > Yes, we sometimes sort includes... to avoid patch rejects.
> > 
> > This is guaranteed to cause rejects.
> > 
> > If we do sort them, we use "longest-first" usually.
> > 
> 
> When using random order, I've been told by reviewers that my ordering
> is wrong [1], but I could not figure out what the "right" ordering is in order to apply.
> I did not find any mention of how to sort includes in  Documentation/CodingStyle,
> but I've seen patches in the kernel that fix random ordering by sorting them
> alphabetically [2].

Ok, I have seen this more often:

https://lkml.org/lkml/2009/3/28/99
 
> However, I do not feel strongly about this so I can drop this patch.

Well, its really nit picking.

										Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* RE: [PATCH v5 1/9] Input: goodix - sort includes alphabetically
  2015-10-05 20:57         ` Pavel Machek
  (?)
@ 2015-10-08 10:05         ` Tirdea, Irina
  -1 siblings, 0 replies; 25+ messages in thread
From: Tirdea, Irina @ 2015-10-08 10:05 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Dmitry Torokhov, Bastien Nocera, Aleksei Mamlin, linux-input,
	Mark Rutland, Purdila, Octavian, linux-kernel, devicetree



> -----Original Message-----
> From: Pavel Machek [mailto:pavel@ucw.cz]
> Sent: 05 October, 2015 23:58
> To: Tirdea, Irina
> Cc: Dmitry Torokhov; Bastien Nocera; Aleksei Mamlin; linux-input@vger.kernel.org; Mark Rutland; Purdila, Octavian; linux-
> kernel@vger.kernel.org; devicetree@vger.kernel.org
> Subject: Re: [PATCH v5 1/9] Input: goodix - sort includes alphabetically
> 
> Hi!
> 

Hi!

> > > Subject: Re: [PATCH v5 1/9] Input: goodix - sort includes alphabetically
> > >
> > > On Mon 2015-09-07 17:36:15, Irina Tirdea wrote:
> > > > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> > > > ---
> > > >  drivers/input/touchscreen/goodix.c | 12 ++++++------
> > > >  1 file changed, 6 insertions(+), 6 deletions(-)
> > >
> > > Don't.
> > >
> > > Yes, we sometimes sort includes... to avoid patch rejects.
> > >
> > > This is guaranteed to cause rejects.
> > >
> > > If we do sort them, we use "longest-first" usually.
> > >
> >
> > When using random order, I've been told by reviewers that my ordering
> > is wrong [1], but I could not figure out what the "right" ordering is in order to apply.
> > I did not find any mention of how to sort includes in  Documentation/CodingStyle,
> > but I've seen patches in the kernel that fix random ordering by sorting them
> > alphabetically [2].
> 
> Ok, I have seen this more often:
> 
> https://lkml.org/lkml/2009/3/28/99
> 

I see. I wasn't aware of the inverse Xmas tree ordering.

> > However, I do not feel strongly about this so I can drop this patch.
> 
> Well, its really nit picking.
> 

I was thinking of dropping it since there does not seem to be a consensus on how
to order the includes (or even if we should order them).  In this case, I guess it's
up to Dmitry.
I'll use the inverse Xmas tree ordering in next patch set and see if that works for him.

Thanks,
Irina

> 										Pavel
> --
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

end of thread, other threads:[~2015-10-08 10:06 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-07 14:36 [PATCH v5 0/9] Goodix touchscreen enhancements Irina Tirdea
2015-09-07 14:36 ` [PATCH v5 1/9] Input: goodix - sort includes alphabetically Irina Tirdea
2015-09-09 16:58   ` Bastien Nocera
2015-10-04 19:46   ` Pavel Machek
2015-10-05 14:14     ` Tirdea, Irina
2015-10-05 20:57       ` Pavel Machek
2015-10-05 20:57         ` Pavel Machek
2015-10-08 10:05         ` Tirdea, Irina
2015-09-07 14:36 ` [PATCH v5 2/9] Input: goodix - use actual config length for each device type Irina Tirdea
2015-09-07 14:36   ` Irina Tirdea
2015-09-09 16:58   ` Bastien Nocera
2015-09-07 14:36 ` [PATCH v5 3/9] Input: goodix - reset device at init Irina Tirdea
2015-09-07 14:36   ` Irina Tirdea
2015-09-09 16:57   ` Bastien Nocera
2015-09-15  9:48   ` Aleksei Mamlin
2015-09-15 14:27     ` Tirdea, Irina
2015-09-07 14:36 ` [PATCH v5 4/9] Input: goodix - write configuration data to device Irina Tirdea
2015-09-07 14:36   ` Irina Tirdea
2015-09-07 14:36 ` [PATCH v5 5/9] Input: goodix - add power management support Irina Tirdea
2015-09-07 14:36 ` [PATCH v5 6/9] Input: goodix - use goodix_i2c_write_u8 instead of i2c_master_send Irina Tirdea
2015-09-07 14:36 ` [PATCH v5 7/9] Input: goodix - add support for ESD Irina Tirdea
2015-09-07 14:36   ` Irina Tirdea
2015-09-07 14:36 ` [PATCH v5 8/9] Input: goodix - add sysfs interface to dump config Irina Tirdea
2015-09-07 14:36   ` Irina Tirdea
2015-09-07 14:36 ` [PATCH v5 9/9] Input: goodix - add runtime power management support Irina Tirdea

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.