Skip to content

Commit c1ed18c

Browse files
diandersbentiss
authored andcommitted
HID: i2c-hid: Introduce goodix-i2c-hid using i2c-hid core
Goodix i2c-hid touchscreens are mostly i2c-hid compliant but have some special power sequencing requirements, including the need to drive a reset line during the sequencing. Let's use the new rejiggering of i2c-hid to support this with a thin wrapper driver to support the first Goodix i2c-hid touchscreen: GT7375P Signed-off-by: Douglas Anderson <dianders@chromium.org> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
1 parent f9a056e commit c1ed18c

3 files changed

Lines changed: 134 additions & 2 deletions

File tree

drivers/hid/i2c-hid/Kconfig

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,25 @@ config I2C_HID_OF
3232
will be called i2c-hid-of. It will also build/depend on the
3333
module i2c-hid.
3434

35+
config I2C_HID_OF_GOODIX
36+
tristate "Driver for Goodix hid-i2c based devices on OF systems"
37+
default n
38+
depends on I2C && INPUT && OF
39+
help
40+
Say Y here if you want support for Goodix i2c devices that use
41+
the i2c-hid protocol on Open Firmware (Device Tree)-based
42+
systems.
43+
44+
If unsure, say N.
45+
46+
This support is also available as a module. If so, the module
47+
will be called i2c-hid-of-goodix. It will also build/depend on
48+
the module i2c-hid.
49+
3550
endmenu
3651

3752
config I2C_HID_CORE
3853
tristate
39-
default y if I2C_HID_ACPI=y || I2C_HID_OF=y
40-
default m if I2C_HID_ACPI=m || I2C_HID_OF=m
54+
default y if I2C_HID_ACPI=y || I2C_HID_OF=y || I2C_HID_OF_GOODIX=y
55+
default m if I2C_HID_ACPI=m || I2C_HID_OF=m || I2C_HID_OF_GOODIX=m
4156
select HID

drivers/hid/i2c-hid/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ i2c-hid-$(CONFIG_DMI) += i2c-hid-dmi-quirks.o
1010

1111
obj-$(CONFIG_I2C_HID_ACPI) += i2c-hid-acpi.o
1212
obj-$(CONFIG_I2C_HID_OF) += i2c-hid-of.o
13+
obj-$(CONFIG_I2C_HID_OF_GOODIX) += i2c-hid-of-goodix.o
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Driver for Goodix touchscreens that use the i2c-hid protocol.
4+
*
5+
* Copyright 2020 Google LLC
6+
*/
7+
8+
#include <linux/delay.h>
9+
#include <linux/device.h>
10+
#include <linux/gpio/consumer.h>
11+
#include <linux/i2c.h>
12+
#include <linux/kernel.h>
13+
#include <linux/module.h>
14+
#include <linux/of.h>
15+
#include <linux/pm.h>
16+
#include <linux/regulator/consumer.h>
17+
18+
#include "i2c-hid.h"
19+
20+
struct goodix_i2c_hid_timing_data {
21+
unsigned int post_gpio_reset_delay_ms;
22+
unsigned int post_power_delay_ms;
23+
};
24+
25+
struct i2c_hid_of_goodix {
26+
struct i2chid_ops ops;
27+
28+
struct regulator *vdd;
29+
struct gpio_desc *reset_gpio;
30+
const struct goodix_i2c_hid_timing_data *timings;
31+
};
32+
33+
static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
34+
{
35+
struct i2c_hid_of_goodix *ihid_goodix =
36+
container_of(ops, struct i2c_hid_of_goodix, ops);
37+
int ret;
38+
39+
ret = regulator_enable(ihid_goodix->vdd);
40+
if (ret)
41+
return ret;
42+
43+
if (ihid_goodix->timings->post_power_delay_ms)
44+
msleep(ihid_goodix->timings->post_power_delay_ms);
45+
46+
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 0);
47+
if (ihid_goodix->timings->post_gpio_reset_delay_ms)
48+
msleep(ihid_goodix->timings->post_gpio_reset_delay_ms);
49+
50+
return 0;
51+
}
52+
53+
static void goodix_i2c_hid_power_down(struct i2chid_ops *ops)
54+
{
55+
struct i2c_hid_of_goodix *ihid_goodix =
56+
container_of(ops, struct i2c_hid_of_goodix, ops);
57+
58+
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
59+
regulator_disable(ihid_goodix->vdd);
60+
}
61+
62+
static int i2c_hid_of_goodix_probe(struct i2c_client *client,
63+
const struct i2c_device_id *id)
64+
{
65+
struct i2c_hid_of_goodix *ihid_goodix;
66+
67+
ihid_goodix = devm_kzalloc(&client->dev, sizeof(*ihid_goodix),
68+
GFP_KERNEL);
69+
if (!ihid_goodix)
70+
return -ENOMEM;
71+
72+
ihid_goodix->ops.power_up = goodix_i2c_hid_power_up;
73+
ihid_goodix->ops.power_down = goodix_i2c_hid_power_down;
74+
75+
/* Start out with reset asserted */
76+
ihid_goodix->reset_gpio =
77+
devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
78+
if (IS_ERR(ihid_goodix->reset_gpio))
79+
return PTR_ERR(ihid_goodix->reset_gpio);
80+
81+
ihid_goodix->vdd = devm_regulator_get(&client->dev, "vdd");
82+
if (IS_ERR(ihid_goodix->vdd))
83+
return PTR_ERR(ihid_goodix->vdd);
84+
85+
ihid_goodix->timings = device_get_match_data(&client->dev);
86+
87+
return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001);
88+
}
89+
90+
static const struct goodix_i2c_hid_timing_data goodix_gt7375p_timing_data = {
91+
.post_power_delay_ms = 10,
92+
.post_gpio_reset_delay_ms = 180,
93+
};
94+
95+
static const struct of_device_id goodix_i2c_hid_of_match[] = {
96+
{ .compatible = "goodix,gt7375p", .data = &goodix_gt7375p_timing_data },
97+
{ }
98+
};
99+
MODULE_DEVICE_TABLE(of, goodix_i2c_hid_of_match);
100+
101+
static struct i2c_driver goodix_i2c_hid_ts_driver = {
102+
.driver = {
103+
.name = "i2c_hid_of_goodix",
104+
.pm = &i2c_hid_core_pm,
105+
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
106+
.of_match_table = of_match_ptr(goodix_i2c_hid_of_match),
107+
},
108+
.probe = i2c_hid_of_goodix_probe,
109+
.remove = i2c_hid_core_remove,
110+
.shutdown = i2c_hid_core_shutdown,
111+
};
112+
module_i2c_driver(goodix_i2c_hid_ts_driver);
113+
114+
MODULE_AUTHOR("Douglas Anderson <dianders@chromium.org>");
115+
MODULE_DESCRIPTION("Goodix i2c-hid touchscreen driver");
116+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)