This patchset depends on Dmitry's patch that fixes the
GPIO ACPI API[1], so devm_gpiod_get_optional can be properly
used in the code.
[1] https://lkml.org/lkml/2015/11/11/465
Changes in v11:
- renamed irq-gpio and reset-gpio to irq-gpios and reset-gpios
- added Rob's Acked-by for the binding in patch 6.
Changes in v10:
- use devm_gpiod_get_optional and no longer ignore errors returned
by it
- use completion to signal other code paths in the driver that
the config firmware is loaded on the device
- dropped the patch that ordered the includes
Changes in v9:
- add GPIOLIB to driver dependencies
- add Tested-by tag from Bastien and Aleksei
Changes in v8:
- only allow new functionality for devices that declare named
gpios (using _DSD properties in ACPI or named DT properties)
Changes in v7:
- add dmi quirk to skip gpio pins setup and functionality that
depends on them for Onda v975w, WinBook TW100 and WinBook TW700.
- add support for named gpio pins
- rework the runtime pm patch to fix a couple of issues
- sort includes using inverse Xmas tree ordering
Changes in v6:
- skip runtime power manangent calls in open/close if the device
ACPI/DT configuration does not declare interrupt and reset gpio pins.
- reset the device before starting i2c communication
- add Bastien's ack to the first 2 patches
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 properly
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 (8):
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 | 13 +
drivers/input/touchscreen/Kconfig | 1 +
drivers/input/touchscreen/goodix.c | 766 ++++++++++++++++++++-
3 files changed, 742 insertions(+), 38 deletions(-)
--
1.9.1
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 <[email protected]>
Acked-by: Bastien Nocera <[email protected]>
Tested-by: Bastien Nocera <[email protected]>
Tested-by: Aleksei Mamlin <[email protected]>
---
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 4d113c9..56d0330 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
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 properly declared, the functionality
depending on these pins will not be available, but the device can still
be used with basic functionality.
For both device tree and ACPI, the interrupt gpio pin configuration is
read from the "irq-gpios" property and the reset pin configuration is
read from the "reset-gpios" property. For ACPI 5.1, named properties
can be specified using the _DSD section. This functionality will not be
available for devices that use indexed gpio pins declared in the _CRS
section (we need to provide backward compatibility with devices
that do not support using the interrupt gpio pin as output).
For ACPI, the pins can be specified using ACPI 5.1:
Device (STAC)
{
Name (_HID, "GDIX1001")
...
Method (_CRS, 0, Serialized)
{
Name (RBUF, ResourceTemplate ()
{
I2cSerialBus (0x0014, ControllerInitiated, 0x00061A80,
AddressingMode7Bit, "\\I2C0",
0x00, ResourceConsumer, ,
)
GpioInt (Edge, ActiveHigh, Exclusive, PullNone, 0x0000,
"\\I2C0", 0x00, ResourceConsumer, ,
)
{ // Pin list
0
}
GpioIo (Exclusive, PullDown, 0x0000, 0x0000,
IoRestrictionOutputOnly, "\\I2C0", 0x00,
ResourceConsumer, ,
)
{
1
}
})
Return (RBUF)
}
Name (_DSD, Package ()
{
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package ()
{
Package (2) {"irq-gpios", Package() {^STAC, 0, 0, 0 }},
Package (2) {"reset-gpios", Package() {^STAC, 1, 0, 0 }},
...
}
}
Signed-off-by: Octavian Purdila <[email protected]>
Signed-off-by: Irina Tirdea <[email protected]>
---
.../bindings/input/touchscreen/goodix.txt | 9 ++
drivers/input/touchscreen/Kconfig | 1 +
drivers/input/touchscreen/goodix.c | 101 +++++++++++++++++++++
3 files changed, 111 insertions(+)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
index 8ba98ee..c42d2ce 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
@@ -13,6 +13,12 @@ Required properties:
- interrupt-parent : Interrupt controller to which the chip is connected
- interrupts : Interrupt to which the chip is connected
+Optional properties:
+
+ - irq-gpios : GPIO pin used for IRQ. The driver uses the
+ interrupt gpio pin as output to reset the device.
+ - reset-gpios : GPIO pin used for reset
+
Example:
i2c@00000000 {
@@ -23,6 +29,9 @@ Example:
reg = <0x5d>;
interrupt-parent = <&gpio>;
interrupts = <0 0>;
+
+ irq-gpios = <&gpio1 0 0>;
+ reset-gpios = <&gpio1 1 0>;
};
/* ... */
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 2ccc522..121a0ac 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -324,6 +324,7 @@ config TOUCHSCREEN_FUJITSU
config TOUCHSCREEN_GOODIX
tristate "Goodix I2C touchscreen"
depends on I2C
+ depends on GPIOLIB
help
Say Y here if you have the Goodix touchscreen (such as one
installed in Onda v975w tablets) connected to your
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 56d0330..4744032 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/dmi.h>
+#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
@@ -37,8 +38,13 @@ 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_GPIO_INT_NAME "irq"
+#define GOODIX_GPIO_RST_NAME "reset"
+
#define GOODIX_MAX_HEIGHT 4096
#define GOODIX_MAX_WIDTH 4096
#define GOODIX_INT_TRIGGER 1
@@ -237,6 +243,88 @@ 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)
+{
+ int error;
+ struct device *dev;
+ struct gpio_desc *gpiod;
+
+ if (!ts->client)
+ return -EINVAL;
+ dev = &ts->client->dev;
+
+ /* Get the interrupt GPIO pin number */
+ gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN);
+ if (IS_ERR(gpiod)) {
+ error = PTR_ERR(gpiod);
+ if (error != -EPROBE_DEFER)
+ dev_dbg(dev, "Failed to get %s GPIO: %d\n",
+ GOODIX_GPIO_INT_NAME, error);
+ return error;
+ }
+ ts->gpiod_int = gpiod;
+
+ /* Get the reset line GPIO pin number */
+ gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN);
+ if (IS_ERR(gpiod)) {
+ error = PTR_ERR(gpiod);
+ if (error != -EPROBE_DEFER)
+ dev_dbg(dev, "Failed to get %s GPIO: %d\n",
+ GOODIX_GPIO_RST_NAME, error);
+ return error;
+ }
+ ts->gpiod_rst = gpiod;
+
+ return 0;
+}
+
/**
* goodix_read_config - Read the embedded configuration of the panel
*
@@ -405,6 +493,19 @@ static int goodix_ts_probe(struct i2c_client *client,
ts->client = client;
i2c_set_clientdata(client, ts);
+ error = goodix_get_gpio_config(ts);
+ 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;
+ }
+ }
+
error = goodix_i2c_test(client);
if (error) {
dev_err(&client->dev, "I2C communication failure: %d\n", error);
--
1.9.1
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 sensitivity 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 <[email protected]>
Signed-off-by: Irina Tirdea <[email protected]>
---
drivers/input/touchscreen/goodix.c | 247 ++++++++++++++++++++++++++++++++-----
1 file changed, 215 insertions(+), 32 deletions(-)
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 4744032..0911b0c9 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/dmi.h>
+#include <linux/firmware.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/input.h>
@@ -40,6 +41,10 @@ struct goodix_ts_data {
int cfg_len;
struct gpio_desc *gpiod_int;
struct gpio_desc *gpiod_rst;
+ u16 id;
+ u16 version;
+ char *cfg_name;
+ struct completion firmware_loading_complete;
};
#define GOODIX_GPIO_INT_NAME "irq"
@@ -124,6 +129,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) {
@@ -243,6 +281,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;
@@ -371,30 +476,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;
}
@@ -428,13 +532,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;
@@ -458,8 +559,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) {
@@ -471,13 +572,70 @@ 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 probe to finish initialization of the device.
+ * Contains the common initialization code for both devices that
+ * declare gpio pins and devices that do not. It is either called
+ * directly from probe or from request_firmware_wait callback.
+ */
+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:
+ release_firmware(cfg);
+ complete_all(&ts->firmware_loading_complete);
+}
+
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);
@@ -492,6 +650,7 @@ static int goodix_ts_probe(struct i2c_client *client,
ts->client = client;
i2c_set_clientdata(client, ts);
+ init_completion(&ts->firmware_loading_complete);
error = goodix_get_gpio_config(ts);
if (error)
@@ -512,29 +671,52 @@ 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);
-
- goodix_read_config(ts);
+ ts->cfg_len = goodix_get_cfg_len(ts->id);
- error = goodix_request_input_dev(ts, version_info, id_info);
- if (error)
- return error;
+ if (ts->gpiod_int && ts->gpiod_rst) {
+ /* update device config */
+ ts->cfg_name = kasprintf(GFP_KERNEL, "goodix_%d_cfg.bin",
+ ts->id);
+ if (!ts->cfg_name)
+ return -ENOMEM;
+
+ 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);
+ goto err_free_cfg_name;
+ }
- 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 goodix_configure_dev(ts);
+
+err_free_cfg_name:
+ if (ts->gpiod_int && ts->gpiod_rst)
+ kfree(ts->cfg_name);
+ 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)
+ return 0;
+
+ wait_for_completion(&ts->firmware_loading_complete);
+ kfree(ts->cfg_name);
+
return 0;
}
@@ -568,6 +750,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
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 <[email protected]>
Signed-off-by: Irina Tirdea <[email protected]>
---
drivers/input/touchscreen/goodix.c | 96 ++++++++++++++++++++++++++++++++++++--
1 file changed, 91 insertions(+), 5 deletions(-)
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 0911b0c9..0fd472d 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -45,6 +45,7 @@ struct goodix_ts_data {
u16 version;
char *cfg_name;
struct completion firmware_loading_complete;
+ unsigned long irq_flags;
};
#define GOODIX_GPIO_INT_NAME "irq"
@@ -61,6 +62,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
@@ -162,6 +166,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) {
@@ -281,6 +290,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
*
@@ -585,7 +606,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);
@@ -593,10 +613,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;
@@ -720,6 +738,73 @@ static int goodix_ts_remove(struct i2c_client *client)
return 0;
}
+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;
+
+ wait_for_completion(&ts->firmware_loading_complete);
+
+ /* 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 },
{ }
@@ -756,6 +841,7 @@ static struct i2c_driver goodix_ts_driver = {
.name = "Goodix-TS",
.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
Use goodix_i2c_write_u8 instead of i2c_master_send to simplify code.
Signed-off-by: Irina Tirdea <[email protected]>
Tested-by: Bastien Nocera <[email protected]>
Tested-by: Aleksei Mamlin <[email protected]>
---
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 0fd472d..9b7f2ad 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -275,16 +275,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
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 <[email protected]>
For the binding: Acked-by: Rob Herring <[email protected]>
Signed-off-by: Irina Tirdea <[email protected]>
---
.../bindings/input/touchscreen/goodix.txt | 4 +
drivers/input/touchscreen/goodix.c | 160 ++++++++++++++++++++-
2 files changed, 159 insertions(+), 5 deletions(-)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
index c42d2ce..a8492e3 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
@@ -18,6 +18,10 @@ Optional properties:
- irq-gpios : GPIO pin used for IRQ. The driver uses the
interrupt gpio pin as output to reset the device.
- reset-gpios : GPIO pin used for reset
+ - 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 9b7f2ad..28cbfa9 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -46,10 +46,13 @@ struct goodix_ts_data {
char *cfg_name;
struct completion firmware_loading_complete;
unsigned long irq_flags;
+ atomic_t esd_timeout;
+ struct delayed_work esd_work;
};
#define GOODIX_GPIO_INT_NAME "irq"
#define GOODIX_GPIO_RST_NAME "reset"
+#define GOODIX_DEVICE_ESD_TIMEOUT_PROPERTY "esd-recovery-timeout-ms"
#define GOODIX_MAX_HEIGHT 4096
#define GOODIX_MAX_WIDTH 4096
@@ -64,6 +67,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
@@ -406,6 +411,119 @@ 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;
+
+ wait_for_completion(&ts->firmware_loading_complete);
+
+ 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
*
@@ -637,7 +755,11 @@ 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;
+
+ goodix_enable_esd(ts);
err_release_cfg:
release_firmware(cfg);
@@ -648,7 +770,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);
@@ -664,6 +786,7 @@ static int goodix_ts_probe(struct i2c_client *client,
ts->client = client;
i2c_set_clientdata(client, ts);
init_completion(&ts->firmware_loading_complete);
+ INIT_DELAYED_WORK(&ts->esd_work, goodix_esd_work);
error = goodix_get_gpio_config(ts);
if (error)
@@ -693,11 +816,28 @@ static int goodix_ts_probe(struct i2c_client *client,
ts->cfg_len = goodix_get_cfg_len(ts->id);
if (ts->gpiod_int && ts->gpiod_rst) {
+ 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,
@@ -717,6 +857,9 @@ static int goodix_ts_probe(struct i2c_client *client,
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;
}
@@ -728,6 +871,8 @@ static int goodix_ts_remove(struct i2c_client *client)
return 0;
wait_for_completion(&ts->firmware_loading_complete);
+ sysfs_remove_group(&client->dev.kobj, &goodix_attr_group);
+ goodix_disable_esd(ts);
kfree(ts->cfg_name);
return 0;
@@ -745,6 +890,7 @@ static int __maybe_unused goodix_suspend(struct device *dev)
wait_for_completion(&ts->firmware_loading_complete);
+ 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 */
@@ -795,7 +941,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);
--
1.9.1
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 <[email protected]>
Tested-by: Bastien Nocera <[email protected]>
Tested-by: Aleksei Mamlin <[email protected]>
---
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 28cbfa9..ea5042f 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -511,12 +511,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
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 <[email protected]>
---
drivers/input/touchscreen/goodix.c | 159 +++++++++++++++++++++++++++++++++----
1 file changed, 145 insertions(+), 14 deletions(-)
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index ea5042f..a793b8c 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/of.h>
+#include <linux/pm_runtime.h>
#include <asm/unaligned.h>
struct goodix_ts_data {
@@ -48,6 +49,10 @@ struct goodix_ts_data {
unsigned long irq_flags;
atomic_t esd_timeout;
struct delayed_work esd_work;
+ bool suspended;
+ atomic_t open_count;
+ /* Protects power management calls and access to suspended flag */
+ struct mutex mutex;
};
#define GOODIX_GPIO_INT_NAME "irq"
@@ -78,6 +83,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,
@@ -193,6 +200,29 @@ static int goodix_get_cfg_len(u16 id)
}
}
+static int goodix_set_power_state(struct goodix_ts_data *ts, bool on)
+{
+ int error;
+
+ if (on) {
+ error = pm_runtime_get_sync(&ts->client->dev);
+ } else {
+ pm_runtime_mark_last_busy(&ts->client->dev);
+ error = pm_runtime_put_autosuspend(&ts->client->dev);
+ }
+
+ if (error < 0) {
+ dev_err(&ts->client->dev,
+ "failed to change power state to %d\n", on);
+ if (on)
+ pm_runtime_put_noidle(&ts->client->dev);
+
+ return error;
+ }
+
+ return 0;
+}
+
static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
{
int touch_num;
@@ -501,11 +531,13 @@ static ssize_t goodix_esd_timeout_store(struct device *dev,
return error;
esd_timeout = atomic_read(&ts->esd_timeout);
- if (esd_timeout && !new_esd_timeout)
+ if (esd_timeout && !new_esd_timeout &&
+ pm_runtime_active(&ts->client->dev))
goodix_disable_esd(ts);
atomic_set(&ts->esd_timeout, new_esd_timeout);
- if (!esd_timeout && new_esd_timeout)
+ if (!esd_timeout && new_esd_timeout &&
+ pm_runtime_active(&ts->client->dev))
goodix_enable_esd(ts);
return count;
@@ -518,17 +550,23 @@ static ssize_t goodix_dump_config_show(struct device *dev,
u8 config[GOODIX_CONFIG_MAX_LENGTH];
int error, count = 0, i;
+ error = goodix_set_power_state(ts, true);
+ if (error)
+ return error;
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);
+ goodix_set_power_state(ts, false);
return error;
}
+ goodix_set_power_state(ts, false);
for (i = 0; i < ts->cfg_len; i++)
count += scnprintf(buf + count, PAGE_SIZE - count, "%02x ",
config[i]);
+
return count;
}
@@ -547,6 +585,34 @@ 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;
+
+ if (!ts->gpiod_int || !ts->gpiod_rst)
+ return 0;
+
+ wait_for_completion(&ts->firmware_loading_complete);
+
+ error = goodix_set_power_state(ts, true);
+ if (error)
+ return error;
+ atomic_inc(&ts->open_count);
+ return 0;
+}
+
+static void goodix_close(struct input_dev *input_dev)
+{
+ struct goodix_ts_data *ts = input_get_drvdata(input_dev);
+
+ if (!ts->gpiod_int || !ts->gpiod_rst)
+ return;
+
+ goodix_set_power_state(ts, false);
+ atomic_dec(&ts->open_count);
+}
+
/**
* goodix_get_gpio_config - Get GPIO config from ACPI/DT
*
@@ -718,6 +784,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) {
@@ -765,7 +834,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)
{
@@ -784,6 +854,19 @@ static void goodix_config_cb(const struct firmware *cfg, void *ctx)
goodix_enable_esd(ts);
+ pm_runtime_set_autosuspend_delay(&ts->client->dev,
+ GOODIX_AUTOSUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&ts->client->dev);
+ 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;
+ }
+ pm_runtime_enable(&ts->client->dev);
+ /* Must not suspend immediately after device initialization */
+ pm_runtime_mark_last_busy(&ts->client->dev);
+ pm_request_autosuspend(&ts->client->dev);
+
err_release_cfg:
release_firmware(cfg);
complete_all(&ts->firmware_loading_complete);
@@ -810,6 +893,7 @@ static int goodix_ts_probe(struct i2c_client *client,
i2c_set_clientdata(client, ts);
init_completion(&ts->firmware_loading_complete);
INIT_DELAYED_WORK(&ts->esd_work, goodix_esd_work);
+ mutex_init(&ts->mutex);
error = goodix_get_gpio_config(ts);
if (error)
@@ -894,6 +978,11 @@ static int goodix_ts_remove(struct i2c_client *client)
return 0;
wait_for_completion(&ts->firmware_loading_complete);
+
+ 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);
@@ -901,11 +990,11 @@ static int goodix_ts_remove(struct i2c_client *client)
return 0;
}
-static int __maybe_unused goodix_suspend(struct device *dev)
+static int __maybe_unused goodix_sleep(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct goodix_ts_data *ts = i2c_get_clientdata(client);
- int error;
+ int error = 0;
/* We need gpio pins to suspend/resume */
if (!ts->gpiod_int || !ts->gpiod_rst)
@@ -913,6 +1002,11 @@ static int __maybe_unused goodix_suspend(struct device *dev)
wait_for_completion(&ts->firmware_loading_complete);
+ mutex_lock(&ts->mutex);
+
+ if (ts->suspended)
+ goto out_error;
+
goodix_disable_esd(ts);
/* Free IRQ as IRQ pin is used as output in the suspend sequence */
goodix_free_irq(ts);
@@ -920,7 +1014,7 @@ static int __maybe_unused goodix_suspend(struct device *dev)
error = gpiod_direction_output(ts->gpiod_int, 0);
if (error) {
goodix_request_irq(ts);
- return error;
+ goto out_error;
}
usleep_range(5000, 6000);
@@ -930,7 +1024,8 @@ static int __maybe_unused goodix_suspend(struct device *dev)
dev_err(&ts->client->dev, "Screen off command failed\n");
gpiod_direction_input(ts->gpiod_int);
goodix_request_irq(ts);
- return -EAGAIN;
+ error = -EAGAIN;
+ goto out_error;
}
/*
@@ -939,39 +1034,75 @@ static int __maybe_unused goodix_suspend(struct device *dev)
* sooner, delay 58ms here.
*/
msleep(58);
+ ts->suspended = true;
+ mutex_unlock(&ts->mutex);
return 0;
+
+out_error:
+ mutex_unlock(&ts->mutex);
+ return error;
}
-static int __maybe_unused goodix_resume(struct device *dev)
+static int __maybe_unused goodix_wakeup(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct goodix_ts_data *ts = i2c_get_clientdata(client);
- int error;
+ int error = 0;
if (!ts->gpiod_int || !ts->gpiod_rst)
return 0;
+ mutex_lock(&ts->mutex);
+
+ if (!ts->suspended)
+ goto out_error;
+
/*
* 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;
+ goto out_error;
usleep_range(2000, 5000);
error = goodix_int_sync(ts);
if (error)
- return error;
+ goto out_error;
error = goodix_request_irq(ts);
if (error)
- return error;
+ goto out_error;
+
+ error = goodix_enable_esd(ts);
+ if (error)
+ goto out_error;
+
+ ts->suspended = false;
+ mutex_unlock(&ts->mutex);
+
+ return 0;
+
+out_error:
+ mutex_unlock(&ts->mutex);
+ return error;
+}
- return goodix_enable_esd(ts);
+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);
+
+ if (!atomic_read(&ts->open_count))
+ return 0;
+
+ return goodix_wakeup(dev);
}
-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_sleep, goodix_resume)
+ SET_RUNTIME_PM_OPS(goodix_sleep, goodix_wakeup, NULL)
+};
static const struct i2c_device_id goodix_ts_id[] = {
{ "GDIX1001:00", 0 },
--
1.9.1
On Thu, 2015-11-19 at 14:26 +0200, 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 fails on a 4.3 kernel with an ACPI device (WinBook TW100):
Goodix-TS: probe of i2c-GDIX1001:00 failed with error -16
Can you please document which upstream commit is necessary to make this
behave properly?
I'll test again with a newer kernel.
Cheers
> -----Original Message-----
> From: Bastien Nocera [mailto:[email protected]]
> Sent: 19 November, 2015 17:25
> To: Tirdea, Irina; Dmitry Torokhov; Aleksei Mamlin; Karsten Merker; [email protected]
> Cc: Mark Rutland; Rob Herring; Purdila, Octavian; [email protected]; [email protected]
> Subject: Re: [PATCH v11 2/8] Input: goodix - reset device at init
>
> On Thu, 2015-11-19 at 14:26 +0200, 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 fails on a 4.3 kernel with an ACPI device (WinBook TW100):
> Goodix-TS: probe of i2c-GDIX1001:00 failed with error -16
>
> Can you please document which upstream commit is necessary to make this
> behave properly?
>
You need the patch that fixes the GPIO API [1] so that
devm_gpiod_get_optional works properly (I mentioned that in the cover
letter). This patch just got merged in the gpio tree, so it will take a
while until it will be merged in the main kernel tree or input tree.
Thanks,
Irina
[1] https://lkml.org/lkml/2015/11/11/465
> I'll test again with a newer kernel.
>
> Cheers
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m????????????I?
On Thu, Nov 19, 2015 at 02:26:36PM +0200, Irina Tirdea wrote:
> + if (ts->gpiod_int && ts->gpiod_rst) {
> + /* update device config */
> + ts->cfg_name = kasprintf(GFP_KERNEL, "goodix_%d_cfg.bin",
> + ts->id);
devm_kasprintf maybe (don't resubmit)?
Thanks.
--
Dmitry
On Thu, Nov 19, 2015 at 02:26:37PM +0200, Irina Tirdea wrote:
> 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 <[email protected]>
> Signed-off-by: Irina Tirdea <[email protected]>
> ---
> drivers/input/touchscreen/goodix.c | 96 ++++++++++++++++++++++++++++++++++++--
> 1 file changed, 91 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
> index 0911b0c9..0fd472d 100644
> --- a/drivers/input/touchscreen/goodix.c
> +++ b/drivers/input/touchscreen/goodix.c
> @@ -45,6 +45,7 @@ struct goodix_ts_data {
> u16 version;
> char *cfg_name;
> struct completion firmware_loading_complete;
> + unsigned long irq_flags;
> };
>
> #define GOODIX_GPIO_INT_NAME "irq"
> @@ -61,6 +62,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
> @@ -162,6 +166,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) {
> @@ -281,6 +290,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
> *
> @@ -585,7 +606,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);
>
> @@ -593,10 +613,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;
> @@ -720,6 +738,73 @@ static int goodix_ts_remove(struct i2c_client *client)
> return 0;
> }
>
> +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;
> +
> + wait_for_completion(&ts->firmware_loading_complete);
This is not that nice as it may lead to angry splats from the PM core if
firmware loading takes too long and we start suspending before it
completes.
Rafael, if we issue pm_stay_awake() before requesting firmware and
pm_relax() once it is done, will this prevent the current suspend
timeouts?
Thanks.
--
Dmitry
Hi,
On Thu, Nov 19, 2015 at 7:24 PM, Dmitry Torokhov
<[email protected]> wrote:
> On Thu, Nov 19, 2015 at 02:26:37PM +0200, Irina Tirdea wrote:
>> Implement suspend/resume for goodix driver.
>>
[cut]
>>
>> +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;
>> +
>> + wait_for_completion(&ts->firmware_loading_complete);
>
> This is not that nice as it may lead to angry splats from the PM core if
> firmware loading takes too long and we start suspending before it
> completes.
What exactly do you mean? The suspend watchdog or something else?
> Rafael, if we issue pm_stay_awake() before requesting firmware and
> pm_relax() once it is done, will this prevent the current suspend
> timeouts?
pm_stay_awake() only works if the checking of wakeup sources is
enabled which generally depends on what user space does.
Thanks,
Rafael
On Thu, Nov 19, 2015 at 2:18 PM, Rafael J. Wysocki <[email protected]> wrote:
> Hi,
>
> On Thu, Nov 19, 2015 at 7:24 PM, Dmitry Torokhov
> <[email protected]> wrote:
>> On Thu, Nov 19, 2015 at 02:26:37PM +0200, Irina Tirdea wrote:
>>> Implement suspend/resume for goodix driver.
>>>
>
> [cut]
>
>>>
>>> +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;
>>> +
>>> + wait_for_completion(&ts->firmware_loading_complete);
>>
>> This is not that nice as it may lead to angry splats from the PM core if
>> firmware loading takes too long and we start suspending before it
>> completes.
>
> What exactly do you mean? The suspend watchdog or something else?
I was thinking about dpm watchdog that will panic the system if
suspend takes too long. Hmm, this is debug facility and the default
timeout is 60 seconds, so I guess we can ignore my concern.
>
>> Rafael, if we issue pm_stay_awake() before requesting firmware and
>> pm_relax() once it is done, will this prevent the current suspend
>> timeouts?
>
> pm_stay_awake() only works if the checking of wakeup sources is
> enabled which generally depends on what user space does.
I see.
Thanks.
--
Dmitry
> -----Original Message-----
> From: Dmitry Torokhov [mailto:[email protected]]
> Sent: 19 November, 2015 20:21
> To: Tirdea, Irina
> Cc: Bastien Nocera; Aleksei Mamlin; Karsten Merker; [email protected]; Mark Rutland; Rob Herring; Purdila, Octavian; linux-
> [email protected]; [email protected]
> Subject: Re: [PATCH v11 3/8] Input: goodix - write configuration data to device
>
> On Thu, Nov 19, 2015 at 02:26:36PM +0200, Irina Tirdea wrote:
> > + if (ts->gpiod_int && ts->gpiod_rst) {
> > + /* update device config */
> > + ts->cfg_name = kasprintf(GFP_KERNEL, "goodix_%d_cfg.bin",
> > + ts->id);
>
> devm_kasprintf maybe (don't resubmit)?
>
Yes, using devm_kasprintf would be better.
Thanks,
Irina
> Thanks.
>
> --
> Dmitry
On Thu, Nov 19, 2015 at 02:26:35PM +0200, 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 properly declared, the functionality
> depending on these pins will not be available, but the device can still
> be used with basic functionality.
>
> For both device tree and ACPI, the interrupt gpio pin configuration is
> read from the "irq-gpios" property and the reset pin configuration is
> read from the "reset-gpios" property. For ACPI 5.1, named properties
> can be specified using the _DSD section. This functionality will not be
> available for devices that use indexed gpio pins declared in the _CRS
> section (we need to provide backward compatibility with devices
> that do not support using the interrupt gpio pin as output).
>
> For ACPI, the pins can be specified using ACPI 5.1:
> Device (STAC)
> {
> Name (_HID, "GDIX1001")
> ...
>
> Method (_CRS, 0, Serialized)
> {
> Name (RBUF, ResourceTemplate ()
> {
> I2cSerialBus (0x0014, ControllerInitiated, 0x00061A80,
> AddressingMode7Bit, "\\I2C0",
> 0x00, ResourceConsumer, ,
> )
>
> GpioInt (Edge, ActiveHigh, Exclusive, PullNone, 0x0000,
> "\\I2C0", 0x00, ResourceConsumer, ,
> )
> { // Pin list
> 0
> }
>
> GpioIo (Exclusive, PullDown, 0x0000, 0x0000,
> IoRestrictionOutputOnly, "\\I2C0", 0x00,
> ResourceConsumer, ,
> )
> {
> 1
> }
> })
> Return (RBUF)
> }
>
> Name (_DSD, Package ()
> {
> ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
> Package ()
> {
> Package (2) {"irq-gpios", Package() {^STAC, 0, 0, 0 }},
> Package (2) {"reset-gpios", Package() {^STAC, 1, 0, 0 }},
> ...
> }
> }
>
> Signed-off-by: Octavian Purdila <[email protected]>
> Signed-off-by: Irina Tirdea <[email protected]>
> ---
> .../bindings/input/touchscreen/goodix.txt | 9 ++
For the binding:
Acked-by: Rob Herring <[email protected]>
> drivers/input/touchscreen/Kconfig | 1 +
> drivers/input/touchscreen/goodix.c | 101 +++++++++++++++++++++
> 3 files changed, 111 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
> index 8ba98ee..c42d2ce 100644
> --- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
> +++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
> @@ -13,6 +13,12 @@ Required properties:
> - interrupt-parent : Interrupt controller to which the chip is connected
> - interrupts : Interrupt to which the chip is connected
>
> +Optional properties:
> +
> + - irq-gpios : GPIO pin used for IRQ. The driver uses the
> + interrupt gpio pin as output to reset the device.
> + - reset-gpios : GPIO pin used for reset
> +
> Example:
>
> i2c@00000000 {
> @@ -23,6 +29,9 @@ Example:
> reg = <0x5d>;
> interrupt-parent = <&gpio>;
> interrupts = <0 0>;
> +
> + irq-gpios = <&gpio1 0 0>;
> + reset-gpios = <&gpio1 1 0>;
> };
>
> /* ... */
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 2ccc522..121a0ac 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -324,6 +324,7 @@ config TOUCHSCREEN_FUJITSU
> config TOUCHSCREEN_GOODIX
> tristate "Goodix I2C touchscreen"
> depends on I2C
> + depends on GPIOLIB
> help
> Say Y here if you have the Goodix touchscreen (such as one
> installed in Onda v975w tablets) connected to your
> diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
> index 56d0330..4744032 100644
> --- a/drivers/input/touchscreen/goodix.c
> +++ b/drivers/input/touchscreen/goodix.c
> @@ -16,6 +16,7 @@
>
> #include <linux/kernel.h>
> #include <linux/dmi.h>
> +#include <linux/gpio.h>
> #include <linux/i2c.h>
> #include <linux/input.h>
> #include <linux/input/mt.h>
> @@ -37,8 +38,13 @@ 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_GPIO_INT_NAME "irq"
> +#define GOODIX_GPIO_RST_NAME "reset"
> +
> #define GOODIX_MAX_HEIGHT 4096
> #define GOODIX_MAX_WIDTH 4096
> #define GOODIX_INT_TRIGGER 1
> @@ -237,6 +243,88 @@ 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)
> +{
> + int error;
> + struct device *dev;
> + struct gpio_desc *gpiod;
> +
> + if (!ts->client)
> + return -EINVAL;
> + dev = &ts->client->dev;
> +
> + /* Get the interrupt GPIO pin number */
> + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN);
> + if (IS_ERR(gpiod)) {
> + error = PTR_ERR(gpiod);
> + if (error != -EPROBE_DEFER)
> + dev_dbg(dev, "Failed to get %s GPIO: %d\n",
> + GOODIX_GPIO_INT_NAME, error);
> + return error;
> + }
> + ts->gpiod_int = gpiod;
> +
> + /* Get the reset line GPIO pin number */
> + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN);
> + if (IS_ERR(gpiod)) {
> + error = PTR_ERR(gpiod);
> + if (error != -EPROBE_DEFER)
> + dev_dbg(dev, "Failed to get %s GPIO: %d\n",
> + GOODIX_GPIO_RST_NAME, error);
> + return error;
> + }
> + ts->gpiod_rst = gpiod;
> +
> + return 0;
> +}
> +
> /**
> * goodix_read_config - Read the embedded configuration of the panel
> *
> @@ -405,6 +493,19 @@ static int goodix_ts_probe(struct i2c_client *client,
> ts->client = client;
> i2c_set_clientdata(client, ts);
>
> + error = goodix_get_gpio_config(ts);
> + 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;
> + }
> + }
> +
> error = goodix_i2c_test(client);
> if (error) {
> dev_err(&client->dev, "I2C communication failure: %d\n", error);
> --
> 1.9.1
>
On Thu, Nov 19, 2015 at 02:26:39PM +0200, Irina Tirdea wrote:
> Add ESD (Electrostatic Discharge) protection mechanism.
[...]
> 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 <[email protected]>
> For the binding: Acked-by: Rob Herring <[email protected]>
You should not have the "For the binding:" part here. It was just a note
so it was clear what part I looked at.
It is preferred to split DT bindings to separate patches for this
reason.
> Signed-off-by: Irina Tirdea <[email protected]>
> ---
> .../bindings/input/touchscreen/goodix.txt | 4 +
> drivers/input/touchscreen/goodix.c | 160 ++++++++++++++++++++-
> 2 files changed, 159 insertions(+), 5 deletions(-)
On Fri, Nov 20, 2015 at 7:44 AM, Rob Herring <[email protected]> wrote:
> On Thu, Nov 19, 2015 at 02:26:39PM +0200, Irina Tirdea wrote:
>> Add ESD (Electrostatic Discharge) protection mechanism.
>
> [...]
>
>> 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 <[email protected]>
>> For the binding: Acked-by: Rob Herring <[email protected]>
>
> You should not have the "For the binding:" part here. It was just a note
> so it was clear what part I looked at.
>
> It is preferred to split DT bindings to separate patches for this
> reason.
>
It however does not make sense if one wants to research when and why
something was changed. We do not split patches to .h files from .c
either.
Whenever I can I merge the driver and dt binding changes together if
they have been posted and reviewed as separate patches. The
Acks/Reviewed are mostly important for maintainers anyway.
Thanks.
--
Dmitry
On Thu, 2015-11-19 at 14:26 +0200, Irina Tirdea wrote:
>
<snip>
> Signed-off-by: Octavian Purdila <[email protected]>
> Signed-off-by: Irina Tirdea <[email protected]>
Works on the WinBook TW100
Acked-by: Bastien Nocera <[email protected]>
Tested-by: Bastien Nocera <[email protected]>
On Thu, 2015-11-19 at 14:26 +0200, Irina Tirdea wrote:
>
<snip>
> Signed-off-by: Octavian Purdila <[email protected]>
> Signed-off-by: Irina Tirdea <[email protected]>
Works on the WinBook TW100
Tested-by: Bastien Nocera <[email protected]>
On Thu, 2015-11-19 at 14:26 +0200, Irina Tirdea wrote:
> 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 <[email protected]>
> Signed-off-by: Irina Tirdea <[email protected]>
Not functional on the WinBook TW100, but doesn't break anything
Tested-by: Bastien Nocera <[email protected]>
On Thu, 2015-11-19 at 14:26 +0200, Irina Tirdea wrote:
>
<snip>
> Signed-off-by: Irina Tirdea <[email protected]>
> For the binding: Acked-by: Rob Herring <[email protected]>
>
> Signed-off-by: Irina Tirdea <[email protected]>
Tested-by: Bastien Nocera <[email protected]>
On Thu, 2015-11-19 at 14:26 +0200, Irina Tirdea wrote:
> 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 <[email protected]>
Tested-by: Bastien Nocera <[email protected]>
> -----Original Message-----
> From: Rob Herring [mailto:[email protected]]
> Sent: 20 November, 2015 17:45
> To: Tirdea, Irina
> Cc: Dmitry Torokhov; Bastien Nocera; Aleksei Mamlin; Karsten Merker; [email protected]; Mark Rutland; Purdila, Octavian;
> [email protected]; [email protected]
> Subject: Re: [PATCH v11 6/8] Input: goodix - add support for ESD
>
> On Thu, Nov 19, 2015 at 02:26:39PM +0200, Irina Tirdea wrote:
> > Add ESD (Electrostatic Discharge) protection mechanism.
>
> [...]
>
> > 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 <[email protected]>
> > For the binding: Acked-by: Rob Herring <[email protected]>
>
> You should not have the "For the binding:" part here. It was just a note
> so it was clear what part I looked at.
>
I saw it done like that in another patch already merged, so I thought it's
the right way [1]. Dmitry, could you fix this at merge or you need me to
send another patchset?
Thanks,
Irina
[1] https://git.kernel.org/cgit/linux/kernel/git/jic23/iio.git/commit/?id=d2a3e0931a8f3b95b910096d022ffd98adbd075c
> It is preferred to split DT bindings to separate patches for this
> reason.
>
> > Signed-off-by: Irina Tirdea <[email protected]>
> > ---
> > .../bindings/input/touchscreen/goodix.txt | 4 +
> > drivers/input/touchscreen/goodix.c | 160 ++++++++++++++++++++-
> > 2 files changed, 159 insertions(+), 5 deletions(-)
On Fri, Nov 27, 2015 at 05:24:21PM +0000, Tirdea, Irina wrote:
>
>
> > -----Original Message-----
> > From: Rob Herring [mailto:[email protected]]
> > Sent: 20 November, 2015 17:45
> > To: Tirdea, Irina
> > Cc: Dmitry Torokhov; Bastien Nocera; Aleksei Mamlin; Karsten Merker; [email protected]; Mark Rutland; Purdila, Octavian;
> > [email protected]; [email protected]
> > Subject: Re: [PATCH v11 6/8] Input: goodix - add support for ESD
> >
> > On Thu, Nov 19, 2015 at 02:26:39PM +0200, Irina Tirdea wrote:
> > > Add ESD (Electrostatic Discharge) protection mechanism.
> >
> > [...]
> >
> > > 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 <[email protected]>
> > > For the binding: Acked-by: Rob Herring <[email protected]>
> >
> > You should not have the "For the binding:" part here. It was just a note
> > so it was clear what part I looked at.
> >
>
> I saw it done like that in another patch already merged, so I thought it's
> the right way [1]. Dmitry, could you fix this at merge or you need me to
> send another patchset?
No, I can fix it up locally. Let me sort through the latest submission
and I'll let you know if I need any changes.
Thanks!
--
Dmitry
On Thu, 19 Nov 2015 14:26:35 +0200
Irina Tirdea <[email protected]> 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 properly declared, the functionality
> depending on these pins will not be available, but the device can still
> be used with basic functionality.
>
> For both device tree and ACPI, the interrupt gpio pin configuration is
> read from the "irq-gpios" property and the reset pin configuration is
> read from the "reset-gpios" property. For ACPI 5.1, named properties
> can be specified using the _DSD section. This functionality will not be
> available for devices that use indexed gpio pins declared in the _CRS
> section (we need to provide backward compatibility with devices
> that do not support using the interrupt gpio pin as output).
>
> For ACPI, the pins can be specified using ACPI 5.1:
> Device (STAC)
> {
> Name (_HID, "GDIX1001")
> ...
>
> Method (_CRS, 0, Serialized)
> {
> Name (RBUF, ResourceTemplate ()
> {
> I2cSerialBus (0x0014, ControllerInitiated, 0x00061A80,
> AddressingMode7Bit, "\\I2C0",
> 0x00, ResourceConsumer, ,
> )
>
> GpioInt (Edge, ActiveHigh, Exclusive, PullNone, 0x0000,
> "\\I2C0", 0x00, ResourceConsumer, ,
> )
> { // Pin list
> 0
> }
>
> GpioIo (Exclusive, PullDown, 0x0000, 0x0000,
> IoRestrictionOutputOnly, "\\I2C0", 0x00,
> ResourceConsumer, ,
> )
> {
> 1
> }
> })
> Return (RBUF)
> }
>
> Name (_DSD, Package ()
> {
> ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
> Package ()
> {
> Package (2) {"irq-gpios", Package() {^STAC, 0, 0, 0 }},
> Package (2) {"reset-gpios", Package() {^STAC, 1, 0, 0 }},
> ...
> }
> }
>
> Signed-off-by: Octavian Purdila <[email protected]>
> Signed-off-by: Irina Tirdea <[email protected]>
Works on the Wexler TAB7200
Tested-by: Aleksei Mamlin <[email protected]>
> ---
> .../bindings/input/touchscreen/goodix.txt | 9 ++
> drivers/input/touchscreen/Kconfig | 1 +
> drivers/input/touchscreen/goodix.c | 101 +++++++++++++++++++++
> 3 files changed, 111 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
> index 8ba98ee..c42d2ce 100644
> --- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
> +++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
> @@ -13,6 +13,12 @@ Required properties:
> - interrupt-parent : Interrupt controller to which the chip is connected
> - interrupts : Interrupt to which the chip is connected
>
> +Optional properties:
> +
> + - irq-gpios : GPIO pin used for IRQ. The driver uses the
> + interrupt gpio pin as output to reset the device.
> + - reset-gpios : GPIO pin used for reset
> +
> Example:
>
> i2c@00000000 {
> @@ -23,6 +29,9 @@ Example:
> reg = <0x5d>;
> interrupt-parent = <&gpio>;
> interrupts = <0 0>;
> +
> + irq-gpios = <&gpio1 0 0>;
> + reset-gpios = <&gpio1 1 0>;
> };
>
> /* ... */
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 2ccc522..121a0ac 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -324,6 +324,7 @@ config TOUCHSCREEN_FUJITSU
> config TOUCHSCREEN_GOODIX
> tristate "Goodix I2C touchscreen"
> depends on I2C
> + depends on GPIOLIB
> help
> Say Y here if you have the Goodix touchscreen (such as one
> installed in Onda v975w tablets) connected to your
> diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
> index 56d0330..4744032 100644
> --- a/drivers/input/touchscreen/goodix.c
> +++ b/drivers/input/touchscreen/goodix.c
> @@ -16,6 +16,7 @@
>
> #include <linux/kernel.h>
> #include <linux/dmi.h>
> +#include <linux/gpio.h>
> #include <linux/i2c.h>
> #include <linux/input.h>
> #include <linux/input/mt.h>
> @@ -37,8 +38,13 @@ 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_GPIO_INT_NAME "irq"
> +#define GOODIX_GPIO_RST_NAME "reset"
> +
> #define GOODIX_MAX_HEIGHT 4096
> #define GOODIX_MAX_WIDTH 4096
> #define GOODIX_INT_TRIGGER 1
> @@ -237,6 +243,88 @@ 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)
> +{
> + int error;
> + struct device *dev;
> + struct gpio_desc *gpiod;
> +
> + if (!ts->client)
> + return -EINVAL;
> + dev = &ts->client->dev;
> +
> + /* Get the interrupt GPIO pin number */
> + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN);
> + if (IS_ERR(gpiod)) {
> + error = PTR_ERR(gpiod);
> + if (error != -EPROBE_DEFER)
> + dev_dbg(dev, "Failed to get %s GPIO: %d\n",
> + GOODIX_GPIO_INT_NAME, error);
> + return error;
> + }
> + ts->gpiod_int = gpiod;
> +
> + /* Get the reset line GPIO pin number */
> + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN);
> + if (IS_ERR(gpiod)) {
> + error = PTR_ERR(gpiod);
> + if (error != -EPROBE_DEFER)
> + dev_dbg(dev, "Failed to get %s GPIO: %d\n",
> + GOODIX_GPIO_RST_NAME, error);
> + return error;
> + }
> + ts->gpiod_rst = gpiod;
> +
> + return 0;
> +}
> +
> /**
> * goodix_read_config - Read the embedded configuration of the panel
> *
> @@ -405,6 +493,19 @@ static int goodix_ts_probe(struct i2c_client *client,
> ts->client = client;
> i2c_set_clientdata(client, ts);
>
> + error = goodix_get_gpio_config(ts);
> + 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;
> + }
> + }
> +
> error = goodix_i2c_test(client);
> if (error) {
> dev_err(&client->dev, "I2C communication failure: %d\n", error);
> --
> 1.9.1
>
--
Thanks and regards,
Aleksei Mamlin
On Thu, 19 Nov 2015 14:26:36 +0200
Irina Tirdea <[email protected]> wrote:
> 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 sensitivity 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 <[email protected]>
> Signed-off-by: Irina Tirdea <[email protected]>
Tested-by: Aleksei Mamlin <[email protected]>
> ---
> drivers/input/touchscreen/goodix.c | 247 ++++++++++++++++++++++++++++++++-----
> 1 file changed, 215 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
> index 4744032..0911b0c9 100644
> --- a/drivers/input/touchscreen/goodix.c
> +++ b/drivers/input/touchscreen/goodix.c
> @@ -16,6 +16,7 @@
>
> #include <linux/kernel.h>
> #include <linux/dmi.h>
> +#include <linux/firmware.h>
> #include <linux/gpio.h>
> #include <linux/i2c.h>
> #include <linux/input.h>
> @@ -40,6 +41,10 @@ struct goodix_ts_data {
> int cfg_len;
> struct gpio_desc *gpiod_int;
> struct gpio_desc *gpiod_rst;
> + u16 id;
> + u16 version;
> + char *cfg_name;
> + struct completion firmware_loading_complete;
> };
>
> #define GOODIX_GPIO_INT_NAME "irq"
> @@ -124,6 +129,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) {
> @@ -243,6 +281,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;
> @@ -371,30 +476,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;
> }
> @@ -428,13 +532,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;
>
> @@ -458,8 +559,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) {
> @@ -471,13 +572,70 @@ 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 probe to finish initialization of the device.
> + * Contains the common initialization code for both devices that
> + * declare gpio pins and devices that do not. It is either called
> + * directly from probe or from request_firmware_wait callback.
> + */
> +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:
> + release_firmware(cfg);
> + complete_all(&ts->firmware_loading_complete);
> +}
> +
> 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);
>
> @@ -492,6 +650,7 @@ static int goodix_ts_probe(struct i2c_client *client,
>
> ts->client = client;
> i2c_set_clientdata(client, ts);
> + init_completion(&ts->firmware_loading_complete);
>
> error = goodix_get_gpio_config(ts);
> if (error)
> @@ -512,29 +671,52 @@ 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);
> -
> - goodix_read_config(ts);
> + ts->cfg_len = goodix_get_cfg_len(ts->id);
>
> - error = goodix_request_input_dev(ts, version_info, id_info);
> - if (error)
> - return error;
> + if (ts->gpiod_int && ts->gpiod_rst) {
> + /* update device config */
> + ts->cfg_name = kasprintf(GFP_KERNEL, "goodix_%d_cfg.bin",
> + ts->id);
> + if (!ts->cfg_name)
> + return -ENOMEM;
> +
> + 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);
> + goto err_free_cfg_name;
> + }
>
> - 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 goodix_configure_dev(ts);
> +
> +err_free_cfg_name:
> + if (ts->gpiod_int && ts->gpiod_rst)
> + kfree(ts->cfg_name);
> + 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)
> + return 0;
> +
> + wait_for_completion(&ts->firmware_loading_complete);
> + kfree(ts->cfg_name);
> +
> return 0;
> }
>
> @@ -568,6 +750,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
>
--
Thanks and regards,
Aleksei Mamlin
On Thu, Nov 19, 2015 at 02:26:33PM +0200, Irina Tirdea wrote:
> This patchset depends on Dmitry's patch that fixes the
> GPIO ACPI API[1], so devm_gpiod_get_optional can be properly
> used in the code.
>
> [1] https://lkml.org/lkml/2015/11/11/465
Applied 1 through 5 (see my goodix branch), looking at the rest.
--
Dmitry