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>
This commit is contained in:
parent
f9a056e002
commit
c1ed18c11b
@ -32,10 +32,25 @@ config I2C_HID_OF
|
||||
will be called i2c-hid-of. It will also build/depend on the
|
||||
module i2c-hid.
|
||||
|
||||
config I2C_HID_OF_GOODIX
|
||||
tristate "Driver for Goodix hid-i2c based devices on OF systems"
|
||||
default n
|
||||
depends on I2C && INPUT && OF
|
||||
help
|
||||
Say Y here if you want support for Goodix i2c devices that use
|
||||
the i2c-hid protocol on Open Firmware (Device Tree)-based
|
||||
systems.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
This support is also available as a module. If so, the module
|
||||
will be called i2c-hid-of-goodix. It will also build/depend on
|
||||
the module i2c-hid.
|
||||
|
||||
endmenu
|
||||
|
||||
config I2C_HID_CORE
|
||||
tristate
|
||||
default y if I2C_HID_ACPI=y || I2C_HID_OF=y
|
||||
default m if I2C_HID_ACPI=m || I2C_HID_OF=m
|
||||
default y if I2C_HID_ACPI=y || I2C_HID_OF=y || I2C_HID_OF_GOODIX=y
|
||||
default m if I2C_HID_ACPI=m || I2C_HID_OF=m || I2C_HID_OF_GOODIX=m
|
||||
select HID
|
||||
|
@ -10,3 +10,4 @@ i2c-hid-$(CONFIG_DMI) += i2c-hid-dmi-quirks.o
|
||||
|
||||
obj-$(CONFIG_I2C_HID_ACPI) += i2c-hid-acpi.o
|
||||
obj-$(CONFIG_I2C_HID_OF) += i2c-hid-of.o
|
||||
obj-$(CONFIG_I2C_HID_OF_GOODIX) += i2c-hid-of-goodix.o
|
||||
|
116
drivers/hid/i2c-hid/i2c-hid-of-goodix.c
Normal file
116
drivers/hid/i2c-hid/i2c-hid-of-goodix.c
Normal file
@ -0,0 +1,116 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for Goodix touchscreens that use the i2c-hid protocol.
|
||||
*
|
||||
* Copyright 2020 Google LLC
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "i2c-hid.h"
|
||||
|
||||
struct goodix_i2c_hid_timing_data {
|
||||
unsigned int post_gpio_reset_delay_ms;
|
||||
unsigned int post_power_delay_ms;
|
||||
};
|
||||
|
||||
struct i2c_hid_of_goodix {
|
||||
struct i2chid_ops ops;
|
||||
|
||||
struct regulator *vdd;
|
||||
struct gpio_desc *reset_gpio;
|
||||
const struct goodix_i2c_hid_timing_data *timings;
|
||||
};
|
||||
|
||||
static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
|
||||
{
|
||||
struct i2c_hid_of_goodix *ihid_goodix =
|
||||
container_of(ops, struct i2c_hid_of_goodix, ops);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(ihid_goodix->vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ihid_goodix->timings->post_power_delay_ms)
|
||||
msleep(ihid_goodix->timings->post_power_delay_ms);
|
||||
|
||||
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 0);
|
||||
if (ihid_goodix->timings->post_gpio_reset_delay_ms)
|
||||
msleep(ihid_goodix->timings->post_gpio_reset_delay_ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void goodix_i2c_hid_power_down(struct i2chid_ops *ops)
|
||||
{
|
||||
struct i2c_hid_of_goodix *ihid_goodix =
|
||||
container_of(ops, struct i2c_hid_of_goodix, ops);
|
||||
|
||||
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
|
||||
regulator_disable(ihid_goodix->vdd);
|
||||
}
|
||||
|
||||
static int i2c_hid_of_goodix_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_hid_of_goodix *ihid_goodix;
|
||||
|
||||
ihid_goodix = devm_kzalloc(&client->dev, sizeof(*ihid_goodix),
|
||||
GFP_KERNEL);
|
||||
if (!ihid_goodix)
|
||||
return -ENOMEM;
|
||||
|
||||
ihid_goodix->ops.power_up = goodix_i2c_hid_power_up;
|
||||
ihid_goodix->ops.power_down = goodix_i2c_hid_power_down;
|
||||
|
||||
/* Start out with reset asserted */
|
||||
ihid_goodix->reset_gpio =
|
||||
devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ihid_goodix->reset_gpio))
|
||||
return PTR_ERR(ihid_goodix->reset_gpio);
|
||||
|
||||
ihid_goodix->vdd = devm_regulator_get(&client->dev, "vdd");
|
||||
if (IS_ERR(ihid_goodix->vdd))
|
||||
return PTR_ERR(ihid_goodix->vdd);
|
||||
|
||||
ihid_goodix->timings = device_get_match_data(&client->dev);
|
||||
|
||||
return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001);
|
||||
}
|
||||
|
||||
static const struct goodix_i2c_hid_timing_data goodix_gt7375p_timing_data = {
|
||||
.post_power_delay_ms = 10,
|
||||
.post_gpio_reset_delay_ms = 180,
|
||||
};
|
||||
|
||||
static const struct of_device_id goodix_i2c_hid_of_match[] = {
|
||||
{ .compatible = "goodix,gt7375p", .data = &goodix_gt7375p_timing_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, goodix_i2c_hid_of_match);
|
||||
|
||||
static struct i2c_driver goodix_i2c_hid_ts_driver = {
|
||||
.driver = {
|
||||
.name = "i2c_hid_of_goodix",
|
||||
.pm = &i2c_hid_core_pm,
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.of_match_table = of_match_ptr(goodix_i2c_hid_of_match),
|
||||
},
|
||||
.probe = i2c_hid_of_goodix_probe,
|
||||
.remove = i2c_hid_core_remove,
|
||||
.shutdown = i2c_hid_core_shutdown,
|
||||
};
|
||||
module_i2c_driver(goodix_i2c_hid_ts_driver);
|
||||
|
||||
MODULE_AUTHOR("Douglas Anderson <dianders@chromium.org>");
|
||||
MODULE_DESCRIPTION("Goodix i2c-hid touchscreen driver");
|
||||
MODULE_LICENSE("GPL v2");
|
Loading…
x
Reference in New Issue
Block a user