Commit 27b730e0 authored by Jiri Kosina's avatar Jiri Kosina

Merge branch 'for-5.12/i2c-hid' into for-linus

- ACPI and OF support made more generic / decoupled. From Douglas Anderson
- support for Goodix devices from Douglas Anderson
parents d6310078 c1ed18c1
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/goodix,gt7375p.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Goodix GT7375P touchscreen
maintainers:
- Douglas Anderson <dianders@chromium.org>
description:
Supports the Goodix GT7375P touchscreen.
This touchscreen uses the i2c-hid protocol but has some non-standard
power sequencing required.
properties:
compatible:
items:
- const: goodix,gt7375p
reg:
enum:
- 0x5d
- 0x14
interrupts:
maxItems: 1
reset-gpios:
true
vdd-supply:
description: The 3.3V supply to the touchscreen.
required:
- compatible
- reg
- interrupts
- reset-gpios
- vdd-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,rpmh.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
ap_ts: touchscreen@5d {
compatible = "goodix,gt7375p";
reg = <0x5d>;
interrupt-parent = <&tlmm>;
interrupts = <9 IRQ_TYPE_LEVEL_LOW>;
reset-gpios = <&tlmm 8 GPIO_ACTIVE_LOW>;
vdd-supply = <&pp3300_ts>;
};
};
...@@ -749,7 +749,8 @@ CONFIG_SND_SOC_WM8904=m ...@@ -749,7 +749,8 @@ CONFIG_SND_SOC_WM8904=m
CONFIG_SND_SOC_WSA881X=m CONFIG_SND_SOC_WSA881X=m
CONFIG_SND_SIMPLE_CARD=m CONFIG_SND_SIMPLE_CARD=m
CONFIG_SND_AUDIO_GRAPH_CARD=m CONFIG_SND_AUDIO_GRAPH_CARD=m
CONFIG_I2C_HID=m CONFIG_I2C_HID_ACPI=m
CONFIG_I2C_HID_OF=m
CONFIG_USB_CONN_GPIO=m CONFIG_USB_CONN_GPIO=m
CONFIG_USB=y CONFIG_USB=y
CONFIG_USB_OTG=y CONFIG_USB_OTG=y
......
...@@ -138,7 +138,7 @@ obj-$(CONFIG_USB_HID) += usbhid/ ...@@ -138,7 +138,7 @@ obj-$(CONFIG_USB_HID) += usbhid/
obj-$(CONFIG_USB_MOUSE) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/
obj-$(CONFIG_USB_KBD) += usbhid/ obj-$(CONFIG_USB_KBD) += usbhid/
obj-$(CONFIG_I2C_HID) += i2c-hid/ obj-$(CONFIG_I2C_HID_CORE) += i2c-hid/
obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/ obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/
obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/ obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/
......
...@@ -2,18 +2,55 @@ ...@@ -2,18 +2,55 @@
menu "I2C HID support" menu "I2C HID support"
depends on I2C depends on I2C
config I2C_HID config I2C_HID_ACPI
tristate "HID over I2C transport layer" tristate "HID over I2C transport layer ACPI driver"
default n default n
depends on I2C && INPUT depends on I2C && INPUT && ACPI
select HID help
Say Y here if you use a keyboard, a touchpad, a touchscreen, or any
other HID based devices which is connected to your computer via I2C.
This driver supports ACPI-based systems.
If unsure, say N.
This support is also available as a module. If so, the module
will be called i2c-hid-acpi. It will also build/depend on the
module i2c-hid.
config I2C_HID_OF
tristate "HID over I2C transport layer Open Firmware driver"
default n
depends on I2C && INPUT && OF
help help
Say Y here if you use a keyboard, a touchpad, a touchscreen, or any Say Y here if you use a keyboard, a touchpad, a touchscreen, or any
other HID based devices which is connected to your computer via I2C. other HID based devices which is connected to your computer via I2C.
This driver supports Open Firmware (Device Tree)-based systems.
If unsure, say N. If unsure, say N.
This support is also available as a module. If so, the module This support is also available as a module. If so, the module
will be called i2c-hid. 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 endmenu
config I2C_HID_CORE
tristate
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
...@@ -3,7 +3,11 @@ ...@@ -3,7 +3,11 @@
# Makefile for the I2C input drivers # Makefile for the I2C input drivers
# #
obj-$(CONFIG_I2C_HID) += i2c-hid.o obj-$(CONFIG_I2C_HID_CORE) += i2c-hid.o
i2c-hid-objs = i2c-hid-core.o i2c-hid-objs = i2c-hid-core.o
i2c-hid-$(CONFIG_DMI) += i2c-hid-dmi-quirks.o 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
/*
* HID over I2C ACPI Subclass
*
* Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
* Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
* Copyright (c) 2012 Red Hat, Inc
*
* This code was forked out of the core code, which was partly based on
* "USB HID support for Linux":
*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2007-2008 Oliver Neukum
* Copyright (c) 2006-2010 Jiri Kosina
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pm.h>
#include "i2c-hid.h"
struct i2c_hid_acpi {
struct i2chid_ops ops;
struct i2c_client *client;
};
static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
/*
* The CHPN0001 ACPI device, which is used to describe the Chipone
* ICN8505 controller, has a _CID of PNP0C50 but is not HID compatible.
*/
{"CHPN0001", 0 },
{ },
};
static int i2c_hid_acpi_get_descriptor(struct i2c_client *client)
{
static guid_t i2c_hid_guid =
GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
union acpi_object *obj;
struct acpi_device *adev;
acpi_handle handle;
u16 hid_descriptor_address;
handle = ACPI_HANDLE(&client->dev);
if (!handle || acpi_bus_get_device(handle, &adev)) {
dev_err(&client->dev, "Error could not get ACPI device\n");
return -ENODEV;
}
if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0)
return -ENODEV;
obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL,
ACPI_TYPE_INTEGER);
if (!obj) {
dev_err(&client->dev, "Error _DSM call to get HID descriptor address failed\n");
return -ENODEV;
}
hid_descriptor_address = obj->integer.value;
ACPI_FREE(obj);
return hid_descriptor_address;
}
static void i2c_hid_acpi_shutdown_tail(struct i2chid_ops *ops)
{
struct i2c_hid_acpi *ihid_acpi =
container_of(ops, struct i2c_hid_acpi, ops);
struct device *dev = &ihid_acpi->client->dev;
acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D3_COLD);
}
static int i2c_hid_acpi_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
struct device *dev = &client->dev;
struct i2c_hid_acpi *ihid_acpi;
struct acpi_device *adev;
u16 hid_descriptor_address;
int ret;
ihid_acpi = devm_kzalloc(&client->dev, sizeof(*ihid_acpi), GFP_KERNEL);
if (!ihid_acpi)
return -ENOMEM;
ihid_acpi->client = client;
ihid_acpi->ops.shutdown_tail = i2c_hid_acpi_shutdown_tail;
ret = i2c_hid_acpi_get_descriptor(client);
if (ret < 0)
return ret;
hid_descriptor_address = ret;
adev = ACPI_COMPANION(dev);
if (adev)
acpi_device_fix_up_power(adev);
if (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) {
device_set_wakeup_capable(dev, true);
device_set_wakeup_enable(dev, false);
}
return i2c_hid_core_probe(client, &ihid_acpi->ops,
hid_descriptor_address);
}
static const struct acpi_device_id i2c_hid_acpi_match[] = {
{"ACPI0C50", 0 },
{"PNP0C50", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match);
static struct i2c_driver i2c_hid_acpi_driver = {
.driver = {
.name = "i2c_hid_acpi",
.pm = &i2c_hid_core_pm,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.acpi_match_table = ACPI_PTR(i2c_hid_acpi_match),
},
.probe = i2c_hid_acpi_probe,
.remove = i2c_hid_core_remove,
.shutdown = i2c_hid_core_shutdown,
};
module_i2c_driver(i2c_hid_acpi_driver);
MODULE_DESCRIPTION("HID over I2C ACPI driver");
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
MODULE_LICENSE("GPL");
This diff is collapsed.
// 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");
/*
* HID over I2C Open Firmware Subclass
*
* Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
* Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
* Copyright (c) 2012 Red Hat, Inc
*
* This code was forked out of the core code, which was partly based on
* "USB HID support for Linux":
*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2007-2008 Oliver Neukum
* Copyright (c) 2006-2010 Jiri Kosina
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <linux/delay.h>
#include <linux/device.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 i2c_hid_of {
struct i2chid_ops ops;
struct i2c_client *client;
struct regulator_bulk_data supplies[2];
int post_power_delay_ms;
};
static int i2c_hid_of_power_up(struct i2chid_ops *ops)
{
struct i2c_hid_of *ihid_of = container_of(ops, struct i2c_hid_of, ops);
struct device *dev = &ihid_of->client->dev;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ihid_of->supplies),
ihid_of->supplies);
if (ret) {
dev_warn(dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
if (ihid_of->post_power_delay_ms)
msleep(ihid_of->post_power_delay_ms);
return 0;
}
static void i2c_hid_of_power_down(struct i2chid_ops *ops)
{
struct i2c_hid_of *ihid_of = container_of(ops, struct i2c_hid_of, ops);
regulator_bulk_disable(ARRAY_SIZE(ihid_of->supplies),
ihid_of->supplies);
}
static int i2c_hid_of_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
struct device *dev = &client->dev;
struct i2c_hid_of *ihid_of;
u16 hid_descriptor_address;
int ret;
u32 val;
ihid_of = devm_kzalloc(&client->dev, sizeof(*ihid_of), GFP_KERNEL);
if (!ihid_of)
return -ENOMEM;
ihid_of->ops.power_up = i2c_hid_of_power_up;
ihid_of->ops.power_down = i2c_hid_of_power_down;
ret = of_property_read_u32(dev->of_node, "hid-descr-addr", &val);
if (ret) {
dev_err(&client->dev, "HID register address not provided\n");
return -ENODEV;
}
if (val >> 16) {
dev_err(&client->dev, "Bad HID register address: 0x%08x\n",
val);
return -EINVAL;
}
hid_descriptor_address = val;
if (!device_property_read_u32(&client->dev, "post-power-on-delay-ms",
&val))
ihid_of->post_power_delay_ms = val;
ihid_of->supplies[0].supply = "vdd";
ihid_of->supplies[1].supply = "vddl";
ret = devm_regulator_bulk_get(&client->dev,
ARRAY_SIZE(ihid_of->supplies),
ihid_of->supplies);
if (ret)
return ret;
return i2c_hid_core_probe(client, &ihid_of->ops,
hid_descriptor_address);
}
static const struct of_device_id i2c_hid_of_match[] = {
{ .compatible = "hid-over-i2c" },
{},
};
MODULE_DEVICE_TABLE(of, i2c_hid_of_match);
static const struct i2c_device_id i2c_hid_of_id_table[] = {
{ "hid", 0 },
{ "hid-over-i2c", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, i2c_hid_of_id_table);
static struct i2c_driver i2c_hid_of_driver = {
.driver = {
.name = "i2c_hid_of",
.pm = &i2c_hid_core_pm,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(i2c_hid_of_match),
},
.probe = i2c_hid_of_probe,
.remove = i2c_hid_core_remove,
.shutdown = i2c_hid_core_shutdown,
.id_table = i2c_hid_of_id_table,
};
module_i2c_driver(i2c_hid_of_driver);
MODULE_DESCRIPTION("HID over I2C OF driver");
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
MODULE_LICENSE("GPL");
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#ifndef I2C_HID_H #ifndef I2C_HID_H
#define I2C_HID_H #define I2C_HID_H
#include <linux/i2c.h>
#ifdef CONFIG_DMI #ifdef CONFIG_DMI
struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name); struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name);
...@@ -17,4 +18,25 @@ static inline char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name, ...@@ -17,4 +18,25 @@ static inline char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
{ return NULL; } { return NULL; }
#endif #endif
/**
* struct i2chid_ops - Ops provided to the core.
*
* @power_up: do sequencing to power up the device.
* @power_down: do sequencing to power down the device.
* @shutdown_tail: called at the end of shutdown.
*/
struct i2chid_ops {
int (*power_up)(struct i2chid_ops *ops);
void (*power_down)(struct i2chid_ops *ops);
void (*shutdown_tail)(struct i2chid_ops *ops);
};
int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
u16 hid_descriptor_address);
int i2c_hid_core_remove(struct i2c_client *client);
void i2c_hid_core_shutdown(struct i2c_client *client);
extern const struct dev_pm_ops i2c_hid_core_pm;
#endif #endif
/*
* HID over I2C protocol implementation
*
* Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
* Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#ifndef __LINUX_I2C_HID_H
#define __LINUX_I2C_HID_H
#include <linux/regulator/consumer.h>
#include <linux/types.h>
/**
* struct i2chid_platform_data - used by hid over i2c implementation.
* @hid_descriptor_address: i2c register where the HID descriptor is stored.
* @supplies: regulators for powering on the device.
* @post_power_delay_ms: delay after powering on before device is usable.
*
* Note that it is the responsibility of the platform driver (or the acpi 5.0
* driver, or the flattened device tree) to setup the irq related to the gpio in
* the struct i2c_board_info.
* The platform driver should also setup the gpio according to the device:
*
* A typical example is the following:
* irq = gpio_to_irq(intr_gpio);
* hkdk4412_i2c_devs5[0].irq = irq; // store the irq in i2c_board_info
* gpio_request(intr_gpio, "elan-irq");
* s3c_gpio_setpull(intr_gpio, S3C_GPIO_PULL_UP);
*/
struct i2c_hid_platform_data {
u16 hid_descriptor_address;
struct regulator_bulk_data supplies[2];
int post_power_delay_ms;
};
#endif /* __LINUX_I2C_HID_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment