diff --git a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml new file mode 100644 index 000000000000..05e6f2df604c --- /dev/null +++ b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/elan,ekth6915.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Elan eKTH6915 touchscreen controller + +maintainers: + - Douglas Anderson + +description: + Supports the Elan eKTH6915 touchscreen controller. + This touchscreen controller uses the i2c-hid protocol with a reset GPIO. + +properties: + compatible: + items: + - const: elan,ekth6915 + + reg: + const: 0x10 + + interrupts: + maxItems: 1 + + reset-gpios: + description: Reset GPIO; not all touchscreens using eKTH6915 hook this up. + + vcc33-supply: + description: The 3.3V supply to the touchscreen. + + vccio-supply: + description: + The IO supply to the touchscreen. Need not be specified if this is the + same as the 3.3V supply. + +required: + - compatible + - reg + - interrupts + - vcc33-supply + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + ap_ts: touchscreen@10 { + compatible = "elan,ekth6915"; + reg = <0x10>; + + interrupt-parent = <&tlmm>; + interrupts = <9 IRQ_TYPE_LEVEL_LOW>; + + reset-gpios = <&tlmm 8 GPIO_ACTIVE_LOW>; + vcc33-supply = <&pp3300_ts>; + }; + }; diff --git a/drivers/hid/i2c-hid/Kconfig b/drivers/hid/i2c-hid/Kconfig index a16c6a69680b..5273ee2bb134 100644 --- a/drivers/hid/i2c-hid/Kconfig +++ b/drivers/hid/i2c-hid/Kconfig @@ -32,6 +32,21 @@ config I2C_HID_OF will be called i2c-hid-of. It will also build/depend on the module i2c-hid. +config I2C_HID_OF_ELAN + tristate "Driver for Elan hid-i2c based devices on OF systems" + default n + depends on I2C && INPUT && OF + help + Say Y here if you want support for Elan 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-elan. 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 diff --git a/drivers/hid/i2c-hid/Makefile b/drivers/hid/i2c-hid/Makefile index 302545a771f3..55bd5e0f35af 100644 --- a/drivers/hid/i2c-hid/Makefile +++ b/drivers/hid/i2c-hid/Makefile @@ -10,4 +10,5 @@ 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_ELAN) += i2c-hid-of-elan.o obj-$(CONFIG_I2C_HID_OF_GOODIX) += i2c-hid-of-goodix.o diff --git a/drivers/hid/i2c-hid/i2c-hid-of-elan.c b/drivers/hid/i2c-hid/i2c-hid-of-elan.c new file mode 100644 index 000000000000..2d991325e734 --- /dev/null +++ b/drivers/hid/i2c-hid/i2c-hid-of-elan.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Elan touchscreens that use the i2c-hid protocol. + * + * Copyright 2020 Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-hid.h" + +struct elan_i2c_hid_chip_data { + unsigned int post_gpio_reset_delay_ms; + unsigned int post_power_delay_ms; + u16 hid_descriptor_address; +}; + +struct i2c_hid_of_elan { + struct i2chid_ops ops; + + struct regulator *vcc33; + struct regulator *vccio; + struct gpio_desc *reset_gpio; + const struct elan_i2c_hid_chip_data *chip_data; +}; + +static int elan_i2c_hid_power_up(struct i2chid_ops *ops) +{ + struct i2c_hid_of_elan *ihid_elan = + container_of(ops, struct i2c_hid_of_elan, ops); + int ret; + + ret = regulator_enable(ihid_elan->vcc33); + if (ret) + return ret; + + ret = regulator_enable(ihid_elan->vccio); + if (ret) { + regulator_disable(ihid_elan->vcc33); + return ret; + } + + if (ihid_elan->chip_data->post_power_delay_ms) + msleep(ihid_elan->chip_data->post_power_delay_ms); + + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 0); + if (ihid_elan->chip_data->post_gpio_reset_delay_ms) + msleep(ihid_elan->chip_data->post_gpio_reset_delay_ms); + + return 0; +} + +static void elan_i2c_hid_power_down(struct i2chid_ops *ops) +{ + struct i2c_hid_of_elan *ihid_elan = + container_of(ops, struct i2c_hid_of_elan, ops); + + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1); + regulator_disable(ihid_elan->vccio); + regulator_disable(ihid_elan->vcc33); +} + +static int i2c_hid_of_elan_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_hid_of_elan *ihid_elan; + + ihid_elan = devm_kzalloc(&client->dev, sizeof(*ihid_elan), GFP_KERNEL); + if (!ihid_elan) + return -ENOMEM; + + ihid_elan->ops.power_up = elan_i2c_hid_power_up; + ihid_elan->ops.power_down = elan_i2c_hid_power_down; + + /* Start out with reset asserted */ + ihid_elan->reset_gpio = + devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ihid_elan->reset_gpio)) + return PTR_ERR(ihid_elan->reset_gpio); + + ihid_elan->vccio = devm_regulator_get(&client->dev, "vccio"); + if (IS_ERR(ihid_elan->vccio)) + return PTR_ERR(ihid_elan->vccio); + + ihid_elan->vcc33 = devm_regulator_get(&client->dev, "vcc33"); + if (IS_ERR(ihid_elan->vcc33)) + return PTR_ERR(ihid_elan->vcc33); + + ihid_elan->chip_data = device_get_match_data(&client->dev); + + return i2c_hid_core_probe(client, &ihid_elan->ops, + ihid_elan->chip_data->hid_descriptor_address, 0); +} + +static const struct elan_i2c_hid_chip_data elan_ekth6915_chip_data = { + .post_power_delay_ms = 1, + .post_gpio_reset_delay_ms = 300, + .hid_descriptor_address = 0x0001, +}; + +static const struct of_device_id elan_i2c_hid_of_match[] = { + { .compatible = "elan,ekth6915", .data = &elan_ekth6915_chip_data }, + { } +}; +MODULE_DEVICE_TABLE(of, elan_i2c_hid_of_match); + +static struct i2c_driver elan_i2c_hid_ts_driver = { + .driver = { + .name = "i2c_hid_of_elan", + .pm = &i2c_hid_core_pm, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = of_match_ptr(elan_i2c_hid_of_match), + }, + .probe = i2c_hid_of_elan_probe, + .remove = i2c_hid_core_remove, + .shutdown = i2c_hid_core_shutdown, +}; +module_i2c_driver(elan_i2c_hid_ts_driver); + +MODULE_AUTHOR("Douglas Anderson "); +MODULE_DESCRIPTION("Elan i2c-hid touchscreen driver"); +MODULE_LICENSE("GPL");