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; 29eb16f59eSDouglas Anderson struct regulator *vddio; 30c1ed18c1SDouglas Anderson struct gpio_desc *reset_gpio; 31c1ed18c1SDouglas Anderson const struct goodix_i2c_hid_timing_data *timings; 32c1ed18c1SDouglas Anderson }; 33c1ed18c1SDouglas Anderson 3418eeef46SDouglas Anderson static int goodix_i2c_hid_power_up(struct i2chid_ops *ops) 3518eeef46SDouglas Anderson { 3618eeef46SDouglas Anderson struct i2c_hid_of_goodix *ihid_goodix = 3718eeef46SDouglas Anderson container_of(ops, struct i2c_hid_of_goodix, ops); 38557e05faSDouglas Anderson int ret; 3918eeef46SDouglas Anderson 40557e05faSDouglas Anderson ret = regulator_enable(ihid_goodix->vdd); 41557e05faSDouglas Anderson if (ret) 42557e05faSDouglas Anderson return ret; 43557e05faSDouglas Anderson 44eb16f59eSDouglas Anderson ret = regulator_enable(ihid_goodix->vddio); 45eb16f59eSDouglas Anderson if (ret) 46eb16f59eSDouglas Anderson return ret; 47eb16f59eSDouglas Anderson 48557e05faSDouglas Anderson if (ihid_goodix->timings->post_power_delay_ms) 49557e05faSDouglas Anderson msleep(ihid_goodix->timings->post_power_delay_ms); 50557e05faSDouglas Anderson 51557e05faSDouglas Anderson gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 0); 52557e05faSDouglas Anderson if (ihid_goodix->timings->post_gpio_reset_delay_ms) 53557e05faSDouglas Anderson msleep(ihid_goodix->timings->post_gpio_reset_delay_ms); 54557e05faSDouglas Anderson 55557e05faSDouglas Anderson return 0; 56c1ed18c1SDouglas Anderson } 57c1ed18c1SDouglas Anderson 58c1ed18c1SDouglas Anderson static void goodix_i2c_hid_power_down(struct i2chid_ops *ops) 59c1ed18c1SDouglas Anderson { 60c1ed18c1SDouglas Anderson struct i2c_hid_of_goodix *ihid_goodix = 61c1ed18c1SDouglas Anderson container_of(ops, struct i2c_hid_of_goodix, ops); 62c1ed18c1SDouglas Anderson 6318eeef46SDouglas Anderson gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1); 64eb16f59eSDouglas Anderson regulator_disable(ihid_goodix->vddio); 65557e05faSDouglas Anderson regulator_disable(ihid_goodix->vdd); 6618eeef46SDouglas Anderson } 6718eeef46SDouglas Anderson 68baf34f3bSStephen Kitt static int i2c_hid_of_goodix_probe(struct i2c_client *client) 69c1ed18c1SDouglas Anderson { 70c1ed18c1SDouglas Anderson struct i2c_hid_of_goodix *ihid_goodix; 71557e05faSDouglas Anderson 72c1ed18c1SDouglas Anderson ihid_goodix = devm_kzalloc(&client->dev, sizeof(*ihid_goodix), 73c1ed18c1SDouglas Anderson GFP_KERNEL); 74c1ed18c1SDouglas Anderson if (!ihid_goodix) 75c1ed18c1SDouglas Anderson return -ENOMEM; 76c1ed18c1SDouglas Anderson 77c1ed18c1SDouglas Anderson ihid_goodix->ops.power_up = goodix_i2c_hid_power_up; 78c1ed18c1SDouglas Anderson ihid_goodix->ops.power_down = goodix_i2c_hid_power_down; 79c1ed18c1SDouglas Anderson 80c1ed18c1SDouglas Anderson /* Start out with reset asserted */ 81c1ed18c1SDouglas Anderson ihid_goodix->reset_gpio = 82c1ed18c1SDouglas Anderson devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH); 83c1ed18c1SDouglas Anderson if (IS_ERR(ihid_goodix->reset_gpio)) 84c1ed18c1SDouglas Anderson return PTR_ERR(ihid_goodix->reset_gpio); 85c1ed18c1SDouglas Anderson 86c1ed18c1SDouglas Anderson ihid_goodix->vdd = devm_regulator_get(&client->dev, "vdd"); 87c1ed18c1SDouglas Anderson if (IS_ERR(ihid_goodix->vdd)) 88c1ed18c1SDouglas Anderson return PTR_ERR(ihid_goodix->vdd); 89c1ed18c1SDouglas Anderson 90eb16f59eSDouglas Anderson ihid_goodix->vddio = devm_regulator_get(&client->dev, "mainboard-vddio"); 91eb16f59eSDouglas Anderson if (IS_ERR(ihid_goodix->vddio)) 92eb16f59eSDouglas Anderson return PTR_ERR(ihid_goodix->vddio); 93eb16f59eSDouglas Anderson 94c1ed18c1SDouglas Anderson ihid_goodix->timings = device_get_match_data(&client->dev); 95c1ed18c1SDouglas Anderson 96b60d3c80SAlistair Francis return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001, 0); 97c1ed18c1SDouglas Anderson } 98c1ed18c1SDouglas Anderson 99c1ed18c1SDouglas Anderson static const struct goodix_i2c_hid_timing_data goodix_gt7375p_timing_data = { 100c1ed18c1SDouglas Anderson .post_power_delay_ms = 10, 101c1ed18c1SDouglas Anderson .post_gpio_reset_delay_ms = 180, 102c1ed18c1SDouglas Anderson }; 103c1ed18c1SDouglas Anderson 104c1ed18c1SDouglas Anderson static const struct of_device_id goodix_i2c_hid_of_match[] = { 105c1ed18c1SDouglas Anderson { .compatible = "goodix,gt7375p", .data = &goodix_gt7375p_timing_data }, 106c1ed18c1SDouglas Anderson { } 107c1ed18c1SDouglas Anderson }; 108c1ed18c1SDouglas Anderson MODULE_DEVICE_TABLE(of, goodix_i2c_hid_of_match); 109c1ed18c1SDouglas Anderson 110c1ed18c1SDouglas Anderson static struct i2c_driver goodix_i2c_hid_ts_driver = { 111c1ed18c1SDouglas Anderson .driver = { 112c1ed18c1SDouglas Anderson .name = "i2c_hid_of_goodix", 113c1ed18c1SDouglas Anderson .pm = &i2c_hid_core_pm, 114c1ed18c1SDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS, 115c1ed18c1SDouglas Anderson .of_match_table = of_match_ptr(goodix_i2c_hid_of_match), 116c1ed18c1SDouglas Anderson }, 117*e4b88075SUwe Kleine-König .probe = i2c_hid_of_goodix_probe, 118c1ed18c1SDouglas Anderson .remove = i2c_hid_core_remove, 119c1ed18c1SDouglas Anderson .shutdown = i2c_hid_core_shutdown, 120c1ed18c1SDouglas Anderson }; 121c1ed18c1SDouglas Anderson module_i2c_driver(goodix_i2c_hid_ts_driver); 122c1ed18c1SDouglas Anderson 123c1ed18c1SDouglas Anderson MODULE_AUTHOR("Douglas Anderson <dianders@chromium.org>"); 124c1ed18c1SDouglas Anderson MODULE_DESCRIPTION("Goodix i2c-hid touchscreen driver"); 125c1ed18c1SDouglas Anderson MODULE_LICENSE("GPL v2"); 126