xref: /linux/drivers/hid/i2c-hid/i2c-hid-of-goodix.c (revision baf34f3bbe6de7ac1efb4e31342403e9cca8888d)
1c1ed18c1SDouglas Anderson // SPDX-License-Identifier: GPL-2.0
2c1ed18c1SDouglas Anderson /*
3c1ed18c1SDouglas Anderson  * Driver for Goodix touchscreens that use the i2c-hid protocol.
4c1ed18c1SDouglas Anderson  *
5c1ed18c1SDouglas Anderson  * Copyright 2020 Google LLC
6c1ed18c1SDouglas Anderson  */
7c1ed18c1SDouglas Anderson 
8c1ed18c1SDouglas Anderson #include <linux/delay.h>
9c1ed18c1SDouglas Anderson #include <linux/device.h>
10c1ed18c1SDouglas Anderson #include <linux/gpio/consumer.h>
11c1ed18c1SDouglas Anderson #include <linux/i2c.h>
12c1ed18c1SDouglas Anderson #include <linux/kernel.h>
13c1ed18c1SDouglas Anderson #include <linux/module.h>
14c1ed18c1SDouglas Anderson #include <linux/of.h>
15c1ed18c1SDouglas Anderson #include <linux/pm.h>
16c1ed18c1SDouglas Anderson #include <linux/regulator/consumer.h>
17c1ed18c1SDouglas Anderson 
18c1ed18c1SDouglas Anderson #include "i2c-hid.h"
19c1ed18c1SDouglas Anderson 
20c1ed18c1SDouglas Anderson struct goodix_i2c_hid_timing_data {
21c1ed18c1SDouglas Anderson 	unsigned int post_gpio_reset_delay_ms;
22c1ed18c1SDouglas Anderson 	unsigned int post_power_delay_ms;
23c1ed18c1SDouglas Anderson };
24c1ed18c1SDouglas Anderson 
25c1ed18c1SDouglas Anderson struct i2c_hid_of_goodix {
26c1ed18c1SDouglas Anderson 	struct i2chid_ops ops;
27c1ed18c1SDouglas Anderson 
28c1ed18c1SDouglas Anderson 	struct regulator *vdd;
2918eeef46SDouglas Anderson 	struct notifier_block nb;
30c1ed18c1SDouglas Anderson 	struct gpio_desc *reset_gpio;
31c1ed18c1SDouglas Anderson 	const struct goodix_i2c_hid_timing_data *timings;
32c1ed18c1SDouglas Anderson };
33c1ed18c1SDouglas Anderson 
3418eeef46SDouglas Anderson static void goodix_i2c_hid_deassert_reset(struct i2c_hid_of_goodix *ihid_goodix,
3518eeef46SDouglas Anderson 					  bool regulator_just_turned_on)
36c1ed18c1SDouglas Anderson {
3718eeef46SDouglas Anderson 	if (regulator_just_turned_on && ihid_goodix->timings->post_power_delay_ms)
38c1ed18c1SDouglas Anderson 		msleep(ihid_goodix->timings->post_power_delay_ms);
39c1ed18c1SDouglas Anderson 
40c1ed18c1SDouglas Anderson 	gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 0);
41c1ed18c1SDouglas Anderson 	if (ihid_goodix->timings->post_gpio_reset_delay_ms)
42c1ed18c1SDouglas Anderson 		msleep(ihid_goodix->timings->post_gpio_reset_delay_ms);
4318eeef46SDouglas Anderson }
44c1ed18c1SDouglas Anderson 
4518eeef46SDouglas Anderson static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
4618eeef46SDouglas Anderson {
4718eeef46SDouglas Anderson 	struct i2c_hid_of_goodix *ihid_goodix =
4818eeef46SDouglas Anderson 		container_of(ops, struct i2c_hid_of_goodix, ops);
4918eeef46SDouglas Anderson 
5018eeef46SDouglas Anderson 	return regulator_enable(ihid_goodix->vdd);
51c1ed18c1SDouglas Anderson }
52c1ed18c1SDouglas Anderson 
53c1ed18c1SDouglas Anderson static void goodix_i2c_hid_power_down(struct i2chid_ops *ops)
54c1ed18c1SDouglas Anderson {
55c1ed18c1SDouglas Anderson 	struct i2c_hid_of_goodix *ihid_goodix =
56c1ed18c1SDouglas Anderson 		container_of(ops, struct i2c_hid_of_goodix, ops);
57c1ed18c1SDouglas Anderson 
58c1ed18c1SDouglas Anderson 	regulator_disable(ihid_goodix->vdd);
59c1ed18c1SDouglas Anderson }
60c1ed18c1SDouglas Anderson 
6118eeef46SDouglas Anderson static int ihid_goodix_vdd_notify(struct notifier_block *nb,
6218eeef46SDouglas Anderson 				    unsigned long event,
6318eeef46SDouglas Anderson 				    void *ignored)
6418eeef46SDouglas Anderson {
6518eeef46SDouglas Anderson 	struct i2c_hid_of_goodix *ihid_goodix =
6618eeef46SDouglas Anderson 		container_of(nb, struct i2c_hid_of_goodix, nb);
6718eeef46SDouglas Anderson 	int ret = NOTIFY_OK;
6818eeef46SDouglas Anderson 
6918eeef46SDouglas Anderson 	switch (event) {
7018eeef46SDouglas Anderson 	case REGULATOR_EVENT_PRE_DISABLE:
7118eeef46SDouglas Anderson 		gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
7218eeef46SDouglas Anderson 		break;
7318eeef46SDouglas Anderson 
7418eeef46SDouglas Anderson 	case REGULATOR_EVENT_ENABLE:
7518eeef46SDouglas Anderson 		goodix_i2c_hid_deassert_reset(ihid_goodix, true);
7618eeef46SDouglas Anderson 		break;
7718eeef46SDouglas Anderson 
7818eeef46SDouglas Anderson 	case REGULATOR_EVENT_ABORT_DISABLE:
7918eeef46SDouglas Anderson 		goodix_i2c_hid_deassert_reset(ihid_goodix, false);
8018eeef46SDouglas Anderson 		break;
8118eeef46SDouglas Anderson 
8218eeef46SDouglas Anderson 	default:
8318eeef46SDouglas Anderson 		ret = NOTIFY_DONE;
8418eeef46SDouglas Anderson 		break;
8518eeef46SDouglas Anderson 	}
8618eeef46SDouglas Anderson 
8718eeef46SDouglas Anderson 	return ret;
8818eeef46SDouglas Anderson }
8918eeef46SDouglas Anderson 
90*baf34f3bSStephen Kitt static int i2c_hid_of_goodix_probe(struct i2c_client *client)
91c1ed18c1SDouglas Anderson {
92c1ed18c1SDouglas Anderson 	struct i2c_hid_of_goodix *ihid_goodix;
9318eeef46SDouglas Anderson 	int ret;
94c1ed18c1SDouglas Anderson 	ihid_goodix = devm_kzalloc(&client->dev, sizeof(*ihid_goodix),
95c1ed18c1SDouglas Anderson 				   GFP_KERNEL);
96c1ed18c1SDouglas Anderson 	if (!ihid_goodix)
97c1ed18c1SDouglas Anderson 		return -ENOMEM;
98c1ed18c1SDouglas Anderson 
99c1ed18c1SDouglas Anderson 	ihid_goodix->ops.power_up = goodix_i2c_hid_power_up;
100c1ed18c1SDouglas Anderson 	ihid_goodix->ops.power_down = goodix_i2c_hid_power_down;
101c1ed18c1SDouglas Anderson 
102c1ed18c1SDouglas Anderson 	/* Start out with reset asserted */
103c1ed18c1SDouglas Anderson 	ihid_goodix->reset_gpio =
104c1ed18c1SDouglas Anderson 		devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
105c1ed18c1SDouglas Anderson 	if (IS_ERR(ihid_goodix->reset_gpio))
106c1ed18c1SDouglas Anderson 		return PTR_ERR(ihid_goodix->reset_gpio);
107c1ed18c1SDouglas Anderson 
108c1ed18c1SDouglas Anderson 	ihid_goodix->vdd = devm_regulator_get(&client->dev, "vdd");
109c1ed18c1SDouglas Anderson 	if (IS_ERR(ihid_goodix->vdd))
110c1ed18c1SDouglas Anderson 		return PTR_ERR(ihid_goodix->vdd);
111c1ed18c1SDouglas Anderson 
112c1ed18c1SDouglas Anderson 	ihid_goodix->timings = device_get_match_data(&client->dev);
113c1ed18c1SDouglas Anderson 
11418eeef46SDouglas Anderson 	/*
11518eeef46SDouglas Anderson 	 * We need to control the "reset" line in lockstep with the regulator
11618eeef46SDouglas Anderson 	 * actually turning on an off instead of just when we make the request.
11718eeef46SDouglas Anderson 	 * This matters if the regulator is shared with another consumer.
11818eeef46SDouglas Anderson 	 * - If the regulator is off then we must assert reset. The reset
11918eeef46SDouglas Anderson 	 *   line is active low and on some boards it could cause a current
12018eeef46SDouglas Anderson 	 *   leak if left high.
12118eeef46SDouglas Anderson 	 * - If the regulator is on then we don't want reset asserted for very
12218eeef46SDouglas Anderson 	 *   long. Holding the controller in reset apparently draws extra
12318eeef46SDouglas Anderson 	 *   power.
12418eeef46SDouglas Anderson 	 */
12518eeef46SDouglas Anderson 	ihid_goodix->nb.notifier_call = ihid_goodix_vdd_notify;
12625ddd7cfSDouglas Anderson 	ret = devm_regulator_register_notifier(ihid_goodix->vdd, &ihid_goodix->nb);
1272787710fSDaniel Thompson 	if (ret)
12818eeef46SDouglas Anderson 		return dev_err_probe(&client->dev, ret,
12918eeef46SDouglas Anderson 			"regulator notifier request failed\n");
13018eeef46SDouglas Anderson 
13118eeef46SDouglas Anderson 	/*
13218eeef46SDouglas Anderson 	 * If someone else is holding the regulator on (or the regulator is
13318eeef46SDouglas Anderson 	 * an always-on one) we might never be told to deassert reset. Do it
1342787710fSDaniel Thompson 	 * now... and temporarily bump the regulator reference count just to
1352787710fSDaniel Thompson 	 * make sure it is impossible for this to race with our own notifier!
1362787710fSDaniel Thompson 	 * We also assume that someone else might have _just barely_ turned
1372787710fSDaniel Thompson 	 * the regulator on so we'll do the full "post_power_delay" just in
1382787710fSDaniel Thompson 	 * case.
13918eeef46SDouglas Anderson 	 */
1402787710fSDaniel Thompson 	if (ihid_goodix->reset_gpio && regulator_is_enabled(ihid_goodix->vdd)) {
1412787710fSDaniel Thompson 		ret = regulator_enable(ihid_goodix->vdd);
1422787710fSDaniel Thompson 		if (ret)
1432787710fSDaniel Thompson 			return ret;
14418eeef46SDouglas Anderson 		goodix_i2c_hid_deassert_reset(ihid_goodix, true);
1452787710fSDaniel Thompson 		regulator_disable(ihid_goodix->vdd);
1462787710fSDaniel Thompson 	}
14718eeef46SDouglas Anderson 
148b60d3c80SAlistair Francis 	return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001, 0);
149c1ed18c1SDouglas Anderson }
150c1ed18c1SDouglas Anderson 
151c1ed18c1SDouglas Anderson static const struct goodix_i2c_hid_timing_data goodix_gt7375p_timing_data = {
152c1ed18c1SDouglas Anderson 	.post_power_delay_ms = 10,
153c1ed18c1SDouglas Anderson 	.post_gpio_reset_delay_ms = 180,
154c1ed18c1SDouglas Anderson };
155c1ed18c1SDouglas Anderson 
156c1ed18c1SDouglas Anderson static const struct of_device_id goodix_i2c_hid_of_match[] = {
157c1ed18c1SDouglas Anderson 	{ .compatible = "goodix,gt7375p", .data = &goodix_gt7375p_timing_data },
158c1ed18c1SDouglas Anderson 	{ }
159c1ed18c1SDouglas Anderson };
160c1ed18c1SDouglas Anderson MODULE_DEVICE_TABLE(of, goodix_i2c_hid_of_match);
161c1ed18c1SDouglas Anderson 
162c1ed18c1SDouglas Anderson static struct i2c_driver goodix_i2c_hid_ts_driver = {
163c1ed18c1SDouglas Anderson 	.driver = {
164c1ed18c1SDouglas Anderson 		.name	= "i2c_hid_of_goodix",
165c1ed18c1SDouglas Anderson 		.pm	= &i2c_hid_core_pm,
166c1ed18c1SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
167c1ed18c1SDouglas Anderson 		.of_match_table = of_match_ptr(goodix_i2c_hid_of_match),
168c1ed18c1SDouglas Anderson 	},
169*baf34f3bSStephen Kitt 	.probe_new	= i2c_hid_of_goodix_probe,
170c1ed18c1SDouglas Anderson 	.remove		= i2c_hid_core_remove,
171c1ed18c1SDouglas Anderson 	.shutdown	= i2c_hid_core_shutdown,
172c1ed18c1SDouglas Anderson };
173c1ed18c1SDouglas Anderson module_i2c_driver(goodix_i2c_hid_ts_driver);
174c1ed18c1SDouglas Anderson 
175c1ed18c1SDouglas Anderson MODULE_AUTHOR("Douglas Anderson <dianders@chromium.org>");
176c1ed18c1SDouglas Anderson MODULE_DESCRIPTION("Goodix i2c-hid touchscreen driver");
177c1ed18c1SDouglas Anderson MODULE_LICENSE("GPL v2");
178